Centralize re-synchronization routine. Only do re-synchronization
[unix-history] / usr / src / libexec / tftpd / tftpd.c
CommitLineData
8c5eec2f
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1983 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
ce4fd43b 13#ifndef lint
f9cd9486 14static char sccsid[] = "@(#)tftpd.c 5.4 (Berkeley) %G%";
8c5eec2f 15#endif not lint
f8675e8e 16
7a859218 17
f8675e8e
SL
18/*
19 * Trivial file transfer protocol server.
7a859218
GM
20 *
21 * This version includes many modifications by Jim Guyton <guyton@rand-unix>
f8675e8e 22 */
7a859218 23
f8675e8e 24#include <sys/types.h>
f8675e8e 25#include <sys/socket.h>
f8675e8e 26#include <sys/ioctl.h>
ce4fd43b
SL
27#include <sys/wait.h>
28#include <sys/stat.h>
de3b21e8
SL
29
30#include <netinet/in.h>
31
533343f1
SL
32#include <arpa/tftp.h>
33
de3b21e8 34#include <signal.h>
f8675e8e 35#include <stdio.h>
f8675e8e
SL
36#include <errno.h>
37#include <ctype.h>
4d5e33bd 38#include <netdb.h>
103499bc 39#include <setjmp.h>
3f99c0f7 40#include <syslog.h>
103499bc 41
103499bc 42#define TIMEOUT 5
de3b21e8 43
f8675e8e 44extern int errno;
4d5e33bd 45struct sockaddr_in sin = { AF_INET };
bb933cc2 46int peer;
103499bc
SL
47int rexmtval = TIMEOUT;
48int maxtimeout = 5*TIMEOUT;
7a859218
GM
49
50#define PKTSIZE SEGSIZE+4
51char buf[PKTSIZE];
52char ackbuf[PKTSIZE];
bb933cc2
MK
53struct sockaddr_in from;
54int fromlen;
f8675e8e 55
bb933cc2 56main()
f8675e8e 57{
f8675e8e
SL
58 register struct tftphdr *tp;
59 register int n;
60
076ae92c 61 openlog("tftpd", LOG_PID, LOG_DAEMON);
bb933cc2
MK
62 alarm(10);
63 fromlen = sizeof (from);
64 n = recvfrom(0, buf, sizeof (buf), 0,
65 (caddr_t)&from, &fromlen);
66 if (n < 0) {
67 perror("tftpd: recvfrom");
4d5e33bd
SL
68 exit(1);
69 }
bb933cc2
MK
70 from.sin_family = AF_INET;
71 alarm(0);
bb933cc2
MK
72 close(0);
73 close(1);
74 peer = socket(AF_INET, SOCK_DGRAM, 0);
75 if (peer < 0) {
3f99c0f7 76 syslog(LOG_ERR, "socket: %m");
bb933cc2
MK
77 exit(1);
78 }
79 if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) {
3f99c0f7 80 syslog(LOG_ERR, "bind: %m");
bb933cc2 81 exit(1);
f8675e8e 82 }
bb933cc2 83 if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) {
3f99c0f7 84 syslog(LOG_ERR, "connect: %m");
bb933cc2 85 exit(1);
f8675e8e 86 }
bb933cc2
MK
87 tp = (struct tftphdr *)buf;
88 tp->th_opcode = ntohs(tp->th_opcode);
89 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
90 tftp(tp, n);
91 exit(1);
103499bc
SL
92}
93
f8675e8e
SL
94int validate_access();
95int sendfile(), recvfile();
96
97struct formats {
98 char *f_mode;
99 int (*f_validate)();
100 int (*f_send)();
101 int (*f_recv)();
7a859218 102 int f_convert;
f8675e8e 103} formats[] = {
7a859218
GM
104 { "netascii", validate_access, sendfile, recvfile, 1 },
105 { "octet", validate_access, sendfile, recvfile, 0 },
f8675e8e 106#ifdef notdef
7a859218 107 { "mail", validate_user, sendmail, recvmail, 1 },
f8675e8e
SL
108#endif
109 { 0 }
110};
111
f8675e8e
SL
112/*
113 * Handle initial connection protocol.
114 */
bb933cc2 115tftp(tp, size)
f8675e8e
SL
116 struct tftphdr *tp;
117 int size;
118{
119 register char *cp;
120 int first = 1, ecode;
121 register struct formats *pf;
122 char *filename, *mode;
123
f8675e8e
SL
124 filename = cp = tp->th_stuff;
125again:
126 while (cp < buf + size) {
127 if (*cp == '\0')
128 break;
129 cp++;
130 }
131 if (*cp != '\0') {
132 nak(EBADOP);
133 exit(1);
134 }
135 if (first) {
136 mode = ++cp;
137 first = 0;
138 goto again;
139 }
140 for (cp = mode; *cp; cp++)
141 if (isupper(*cp))
142 *cp = tolower(*cp);
143 for (pf = formats; pf->f_mode; pf++)
144 if (strcmp(pf->f_mode, mode) == 0)
145 break;
146 if (pf->f_mode == 0) {
147 nak(EBADOP);
148 exit(1);
149 }
bb933cc2 150 ecode = (*pf->f_validate)(filename, tp->th_opcode);
f8675e8e
SL
151 if (ecode) {
152 nak(ecode);
153 exit(1);
154 }
155 if (tp->th_opcode == WRQ)
156 (*pf->f_recv)(pf);
157 else
158 (*pf->f_send)(pf);
159 exit(0);
160}
161
7a859218
GM
162
163FILE *file;
bb933cc2 164
f8675e8e
SL
165/*
166 * Validate file access. Since we
167 * have no uid or gid, for now require
168 * file to exist and be publicly
169 * readable/writable.
170 * Note also, full path name must be
171 * given as we have no login directory.
172 */
7a859218
GM
173validate_access(filename, mode)
174 char *filename;
f8675e8e
SL
175 int mode;
176{
177 struct stat stbuf;
7a859218 178 int fd;
f8675e8e 179
7a859218 180 if (*filename != '/')
f8675e8e 181 return (EACCESS);
7a859218 182 if (stat(filename, &stbuf) < 0)
f8675e8e
SL
183 return (errno == ENOENT ? ENOTFOUND : EACCESS);
184 if (mode == RRQ) {
185 if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
186 return (EACCESS);
187 } else {
188 if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
189 return (EACCESS);
190 }
7a859218 191 fd = open(filename, mode == RRQ ? 0 : 1);
f8675e8e
SL
192 if (fd < 0)
193 return (errno + 100);
7a859218
GM
194 file = fdopen(fd, (mode == RRQ)? "r":"w");
195 if (file == NULL) {
196 return errno+100;
197 }
f8675e8e
SL
198 return (0);
199}
200
103499bc
SL
201int timeout;
202jmp_buf timeoutbuf;
f8675e8e
SL
203
204timer()
205{
103499bc
SL
206
207 timeout += rexmtval;
208 if (timeout >= maxtimeout)
f8675e8e 209 exit(1);
103499bc 210 longjmp(timeoutbuf, 1);
f8675e8e
SL
211}
212
213/*
214 * Send the requested file.
215 */
216sendfile(pf)
7a859218 217 struct formats *pf;
f8675e8e 218{
7a859218
GM
219 struct tftphdr *dp, *r_init();
220 register struct tftphdr *ap; /* ack packet */
f8675e8e
SL
221 register int block = 1, size, n;
222
103499bc 223 signal(SIGALRM, timer);
7a859218
GM
224 dp = r_init();
225 ap = (struct tftphdr *)ackbuf;
f8675e8e 226 do {
7a859218 227 size = readit(file, &dp, pf->f_convert);
f8675e8e
SL
228 if (size < 0) {
229 nak(errno + 100);
7a859218 230 goto abort;
f8675e8e 231 }
7a859218
GM
232 dp->th_opcode = htons((u_short)DATA);
233 dp->th_block = htons((u_short)block);
f8675e8e 234 timeout = 0;
103499bc 235 (void) setjmp(timeoutbuf);
7a859218 236
f9cd9486
GM
237send_data:
238 /* Now, we flush anything pending to be read */
239 /* This is to try to keep in synch between the two sides */
240 while (1) {
241 int i;
242 char rbuf[PKTSIZE];
243
244 (void) ioctl(peer, FIONREAD, &i);
245 if (i) {
246 fromlen = sizeof from;
247 n = recvfrom(peer, rbuf, sizeof (rbuf), 0,
248 (caddr_t)&from, &fromlen);
249 } else {
250 break;
251 }
252 }
7a859218
GM
253 if (send(peer, dp, size + 4, 0) != size + 4) {
254 perror("tftpd: write");
255 goto abort;
f8675e8e 256 }
7a859218 257 read_ahead(file, pf->f_convert);
f9cd9486 258 for ( ; ; ) {
7a859218
GM
259 alarm(rexmtval); /* read the ack */
260 n = recv(peer, ackbuf, sizeof (ackbuf), 0);
f8675e8e 261 alarm(0);
103499bc 262 if (n < 0) {
7a859218
GM
263 perror("tftpd: read");
264 goto abort;
103499bc 265 }
7a859218
GM
266 ap->th_opcode = ntohs((u_short)ap->th_opcode);
267 ap->th_block = ntohs((u_short)ap->th_block);
268
269 if (ap->th_opcode == ERROR)
270 goto abort;
f9cd9486
GM
271
272 if (ap->th_opcode == ACK) {
273 if (ap->th_block == block) {
274 break;
275 }
276 if (ap->th_block == (block -1)) {
277 goto send_data;
278 }
279 }
7a859218 280
f9cd9486 281 }
f8675e8e
SL
282 block++;
283 } while (size == SEGSIZE);
7a859218
GM
284abort:
285 (void) fclose(file);
f8675e8e
SL
286}
287
7a859218
GM
288justquit()
289{
290 exit(0);
291}
292
293
f8675e8e
SL
294/*
295 * Receive a file.
296 */
297recvfile(pf)
7a859218 298 struct formats *pf;
f8675e8e 299{
7a859218
GM
300 struct tftphdr *dp, *w_init();
301 register struct tftphdr *ap; /* ack buffer */
f8675e8e
SL
302 register int block = 0, n, size;
303
103499bc 304 signal(SIGALRM, timer);
7a859218
GM
305 dp = w_init();
306 ap = (struct tftphdr *)ackbuf;
f8675e8e
SL
307 do {
308 timeout = 0;
7a859218
GM
309 ap->th_opcode = htons((u_short)ACK);
310 ap->th_block = htons((u_short)block);
f8675e8e 311 block++;
103499bc 312 (void) setjmp(timeoutbuf);
7a859218 313send_ack:
f9cd9486
GM
314 /* Now, we flush anything pending to be read */
315 /* This is to try to keep in synch between the two sides */
316 while (1) {
317 int i;
318 char rbuf[PKTSIZE];
319
320 (void) ioctl(peer, FIONREAD, &i);
321 if (i) {
322 fromlen = sizeof from;
323 n = recvfrom(peer, rbuf, sizeof (rbuf), 0,
324 (caddr_t)&from, &fromlen);
325 } else {
326 break;
327 }
328 }
7a859218
GM
329 if (send(peer, ackbuf, 4, 0) != 4) {
330 perror("tftpd: write");
103499bc 331 goto abort;
f8675e8e 332 }
7a859218
GM
333 write_behind(file, pf->f_convert);
334 for ( ; ; ) {
103499bc 335 alarm(rexmtval);
7a859218 336 n = recv(peer, dp, PKTSIZE, 0);
f8675e8e 337 alarm(0);
7a859218
GM
338 if (n < 0) { /* really? */
339 perror("tftpd: read");
103499bc
SL
340 goto abort;
341 }
7a859218
GM
342 dp->th_opcode = ntohs((u_short)dp->th_opcode);
343 dp->th_block = ntohs((u_short)dp->th_block);
344 if (dp->th_opcode == ERROR)
103499bc 345 goto abort;
7a859218
GM
346 if (dp->th_opcode == DATA) {
347 if (dp->th_block == block) {
348 break; /* normal */
349 }
350 if (dp->th_block == (block-1))
351 goto send_ack; /* rexmit */
352 }
353 }
354 /* size = write(file, dp->th_data, n - 4); */
355 size = writeit(file, &dp, n - 4, pf->f_convert);
356 if (size != (n-4)) { /* ahem */
357 if (size < 0) nak(errno + 100);
358 else nak(ENOSPACE);
103499bc 359 goto abort;
f8675e8e
SL
360 }
361 } while (size == SEGSIZE);
7a859218
GM
362 write_behind(file, pf->f_convert);
363 (void) fclose(file); /* close data file */
364
365 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */
366 ap->th_block = htons((u_short)(block));
367 (void) send(peer, ackbuf, 4, 0);
368
369 signal(SIGALRM, justquit); /* just quit on timeout */
370 alarm(rexmtval);
371 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
372 alarm(0);
373 if (n >= 4 && /* if read some data */
374 dp->th_opcode == DATA && /* and got a data block */
375 block == dp->th_block) { /* then my last ack was lost */
376 (void) send(peer, ackbuf, 4, 0); /* resend final ack */
377 }
103499bc 378abort:
7a859218 379 return;
f8675e8e
SL
380}
381
382struct errmsg {
383 int e_code;
384 char *e_msg;
385} errmsgs[] = {
386 { EUNDEF, "Undefined error code" },
387 { ENOTFOUND, "File not found" },
388 { EACCESS, "Access violation" },
389 { ENOSPACE, "Disk full or allocation exceeded" },
390 { EBADOP, "Illegal TFTP operation" },
391 { EBADID, "Unknown transfer ID" },
392 { EEXISTS, "File already exists" },
393 { ENOUSER, "No such user" },
394 { -1, 0 }
395};
396
397/*
398 * Send a nak packet (error message).
399 * Error code passed in is one of the
400 * standard TFTP codes, or a UNIX errno
401 * offset by 100.
402 */
403nak(error)
404 int error;
405{
406 register struct tftphdr *tp;
407 int length;
408 register struct errmsg *pe;
409 extern char *sys_errlist[];
410
411 tp = (struct tftphdr *)buf;
412 tp->th_opcode = htons((u_short)ERROR);
413 tp->th_code = htons((u_short)error);
414 for (pe = errmsgs; pe->e_code >= 0; pe++)
415 if (pe->e_code == error)
416 break;
7a859218 417 if (pe->e_code < 0) {
f8675e8e 418 pe->e_msg = sys_errlist[error - 100];
7a859218
GM
419 tp->th_code = EUNDEF; /* set 'undef' errorcode */
420 }
f8675e8e
SL
421 strcpy(tp->th_msg, pe->e_msg);
422 length = strlen(pe->e_msg);
423 tp->th_msg[length] = '\0';
424 length += 5;
bb933cc2 425 if (send(peer, buf, length, 0) != length)
f8675e8e
SL
426 perror("nak");
427}