xref: /csrg-svn/libexec/tftpd/tftpd.c (revision 16385)
1 #ifndef lint
2 static	char sccsid[] = "@(#)tftpd.c	4.13 (Berkeley) 04/12/84";
3 #endif
4 
5 /*
6  * Trivial file transfer protocol server.
7  */
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/ioctl.h>
11 #include <sys/wait.h>
12 #include <sys/stat.h>
13 
14 #include <netinet/in.h>
15 
16 #include <arpa/tftp.h>
17 
18 #include <signal.h>
19 #include <stdio.h>
20 #include <errno.h>
21 #include <ctype.h>
22 #include <netdb.h>
23 #include <setjmp.h>
24 
25 #define	TIMEOUT		5
26 
27 extern	int errno;
28 struct	sockaddr_in sin = { AF_INET };
29 int	peer;
30 int	rexmtval = TIMEOUT;
31 int	maxtimeout = 5*TIMEOUT;
32 char	buf[BUFSIZ];
33 struct	sockaddr_in from;
34 int	fromlen;
35 
36 main()
37 {
38 	register struct tftphdr *tp;
39 	register int n;
40 
41 	alarm(10);
42 	fromlen = sizeof (from);
43 	n = recvfrom(0, buf, sizeof (buf), 0,
44 	    (caddr_t)&from, &fromlen);
45 	if (n < 0) {
46 		perror("tftpd: recvfrom");
47 		exit(1);
48 	}
49 	from.sin_family = AF_INET;
50 	alarm(0);
51 	close(0);
52 	close(1);
53 	peer = socket(AF_INET, SOCK_DGRAM, 0);
54 	if (peer < 0) {
55 		perror("tftpd: socket");
56 		exit(1);
57 	}
58 	if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) {
59 		perror("tftpd: bind");
60 		exit(1);
61 	}
62 	if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) {
63 		perror("tftpd: connect");
64 		exit(1);
65 	}
66 	tp = (struct tftphdr *)buf;
67 	tp->th_opcode = ntohs(tp->th_opcode);
68 	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
69 		tftp(tp, n);
70 	exit(1);
71 }
72 
73 int	validate_access();
74 int	sendfile(), recvfile();
75 
76 struct formats {
77 	char	*f_mode;
78 	int	(*f_validate)();
79 	int	(*f_send)();
80 	int	(*f_recv)();
81 } formats[] = {
82 	{ "netascii",	validate_access,	sendfile,	recvfile },
83 	{ "octet",	validate_access,	sendfile,	recvfile },
84 #ifdef notdef
85 	{ "mail",	validate_user,		sendmail,	recvmail },
86 #endif
87 	{ 0 }
88 };
89 
90 /*
91  * Handle initial connection protocol.
92  */
93 tftp(tp, size)
94 	struct tftphdr *tp;
95 	int size;
96 {
97 	register char *cp;
98 	int first = 1, ecode;
99 	register struct formats *pf;
100 	char *filename, *mode;
101 
102 	filename = cp = tp->th_stuff;
103 again:
104 	while (cp < buf + size) {
105 		if (*cp == '\0')
106 			break;
107 		cp++;
108 	}
109 	if (*cp != '\0') {
110 		nak(EBADOP);
111 		exit(1);
112 	}
113 	if (first) {
114 		mode = ++cp;
115 		first = 0;
116 		goto again;
117 	}
118 	for (cp = mode; *cp; cp++)
119 		if (isupper(*cp))
120 			*cp = tolower(*cp);
121 	for (pf = formats; pf->f_mode; pf++)
122 		if (strcmp(pf->f_mode, mode) == 0)
123 			break;
124 	if (pf->f_mode == 0) {
125 		nak(EBADOP);
126 		exit(1);
127 	}
128 	ecode = (*pf->f_validate)(filename, tp->th_opcode);
129 	if (ecode) {
130 		nak(ecode);
131 		exit(1);
132 	}
133 	if (tp->th_opcode == WRQ)
134 		(*pf->f_recv)(pf);
135 	else
136 		(*pf->f_send)(pf);
137 	exit(0);
138 }
139 
140 int	fd;
141 
142 /*
143  * Validate file access.  Since we
144  * have no uid or gid, for now require
145  * file to exist and be publicly
146  * readable/writable.
147  * Note also, full path name must be
148  * given as we have no login directory.
149  */
150 validate_access(file, mode)
151 	char *file;
152 	int mode;
153 {
154 	struct stat stbuf;
155 
156 	if (*file != '/')
157 		return (EACCESS);
158 	if (stat(file, &stbuf) < 0)
159 		return (errno == ENOENT ? ENOTFOUND : EACCESS);
160 	if (mode == RRQ) {
161 		if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
162 			return (EACCESS);
163 	} else {
164 		if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
165 			return (EACCESS);
166 	}
167 	fd = open(file, mode == RRQ ? 0 : 1);
168 	if (fd < 0)
169 		return (errno + 100);
170 	return (0);
171 }
172 
173 int	timeout;
174 jmp_buf	timeoutbuf;
175 
176 timer()
177 {
178 
179 	timeout += rexmtval;
180 	if (timeout >= maxtimeout)
181 		exit(1);
182 	longjmp(timeoutbuf, 1);
183 }
184 
185 /*
186  * Send the requested file.
187  */
188 sendfile(pf)
189 	struct format *pf;
190 {
191 	register struct tftphdr *tp;
192 	register int block = 1, size, n;
193 
194 	signal(SIGALRM, timer);
195 	tp = (struct tftphdr *)buf;
196 	do {
197 		size = read(fd, tp->th_data, SEGSIZE);
198 		if (size < 0) {
199 			nak(errno + 100);
200 			return;
201 		}
202 		tp->th_opcode = htons((u_short)DATA);
203 		tp->th_block = htons((u_short)block);
204 		timeout = 0;
205 		(void) setjmp(timeoutbuf);
206 		if (send(peer, buf, size + 4, 0) != size + 4) {
207 			perror("tftpd: send");
208 			return;
209 		}
210 		do {
211 			alarm(rexmtval);
212 			n = recv(peer, buf, sizeof (buf), 0);
213 			alarm(0);
214 			if (n < 0) {
215 				perror("tftpd: recv");
216 				return;
217 			}
218 			tp->th_opcode = ntohs((u_short)tp->th_opcode);
219 			tp->th_block = ntohs((u_short)tp->th_block);
220 			if (tp->th_opcode == ERROR)
221 				return;
222 		} while (tp->th_opcode != ACK || tp->th_block != block);
223 		block++;
224 	} while (size == SEGSIZE);
225 }
226 
227 /*
228  * Receive a file.
229  */
230 recvfile(pf)
231 	struct format *pf;
232 {
233 	register struct tftphdr *tp;
234 	register int block = 0, n, size;
235 
236 	signal(SIGALRM, timer);
237 	tp = (struct tftphdr *)buf;
238 	do {
239 		timeout = 0;
240 		tp->th_opcode = htons((u_short)ACK);
241 		tp->th_block = htons((u_short)block);
242 		block++;
243 		(void) setjmp(timeoutbuf);
244 		if (send(peer, buf, 4, 0) != 4) {
245 			perror("tftpd: send");
246 			goto abort;
247 		}
248 		do {
249 			alarm(rexmtval);
250 			n = recv(peer, buf, sizeof (buf), 0);
251 			alarm(0);
252 			if (n < 0) {
253 				perror("tftpd: recv");
254 				goto abort;
255 			}
256 			tp->th_opcode = ntohs((u_short)tp->th_opcode);
257 			tp->th_block = ntohs((u_short)tp->th_block);
258 			if (tp->th_opcode == ERROR)
259 				goto abort;
260 		} while (tp->th_opcode != DATA || block != tp->th_block);
261 		size = write(fd, tp->th_data, n - 4);
262 		if (size < 0) {
263 			nak(errno + 100);
264 			goto abort;
265 		}
266 	} while (size == SEGSIZE);
267 abort:
268 	tp->th_opcode = htons((u_short)ACK);
269 	tp->th_block = htons((u_short)(block));
270 	(void) send(peer, buf, 4, 0);
271 }
272 
273 struct errmsg {
274 	int	e_code;
275 	char	*e_msg;
276 } errmsgs[] = {
277 	{ EUNDEF,	"Undefined error code" },
278 	{ ENOTFOUND,	"File not found" },
279 	{ EACCESS,	"Access violation" },
280 	{ ENOSPACE,	"Disk full or allocation exceeded" },
281 	{ EBADOP,	"Illegal TFTP operation" },
282 	{ EBADID,	"Unknown transfer ID" },
283 	{ EEXISTS,	"File already exists" },
284 	{ ENOUSER,	"No such user" },
285 	{ -1,		0 }
286 };
287 
288 /*
289  * Send a nak packet (error message).
290  * Error code passed in is one of the
291  * standard TFTP codes, or a UNIX errno
292  * offset by 100.
293  */
294 nak(error)
295 	int error;
296 {
297 	register struct tftphdr *tp;
298 	int length;
299 	register struct errmsg *pe;
300 	extern char *sys_errlist[];
301 
302 	tp = (struct tftphdr *)buf;
303 	tp->th_opcode = htons((u_short)ERROR);
304 	tp->th_code = htons((u_short)error);
305 	for (pe = errmsgs; pe->e_code >= 0; pe++)
306 		if (pe->e_code == error)
307 			break;
308 	if (pe->e_code < 0)
309 		pe->e_msg = sys_errlist[error - 100];
310 	strcpy(tp->th_msg, pe->e_msg);
311 	length = strlen(pe->e_msg);
312 	tp->th_msg[length] = '\0';
313 	length += 5;
314 	if (send(peer, buf, length, 0) != length)
315 		perror("nak");
316 	exit(1);
317 }
318