1 /* 2 * Copyright (c) 1983 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)tftpd.c 5.9 (Berkeley) 10/10/88"; 26 #endif /* not lint */ 27 28 /* 29 * Trivial file transfer protocol server. 30 * 31 * This version includes many modifications by Jim Guyton <guyton@rand-unix> 32 */ 33 34 #include <sys/types.h> 35 #include <sys/socket.h> 36 #include <sys/ioctl.h> 37 #include <sys/wait.h> 38 #include <sys/stat.h> 39 40 #include <netinet/in.h> 41 42 #include <arpa/tftp.h> 43 44 #include <signal.h> 45 #include <stdio.h> 46 #include <errno.h> 47 #include <ctype.h> 48 #include <netdb.h> 49 #include <setjmp.h> 50 #include <syslog.h> 51 52 #define TIMEOUT 5 53 54 extern int errno; 55 struct sockaddr_in sin = { AF_INET }; 56 int peer; 57 int rexmtval = TIMEOUT; 58 int maxtimeout = 5*TIMEOUT; 59 60 #define PKTSIZE SEGSIZE+4 61 char buf[PKTSIZE]; 62 char ackbuf[PKTSIZE]; 63 struct sockaddr_in from; 64 int fromlen; 65 66 #define MAXARG 4 67 char *dirs[MAXARG+1]; 68 69 main(ac, av) 70 char **av; 71 { 72 register struct tftphdr *tp; 73 register int n = 0; 74 int on = 1; 75 76 ac--; av++; 77 while (ac-- > 0 && n < MAXARG) 78 dirs[n++] = *av++; 79 openlog("tftpd", LOG_PID, LOG_DAEMON); 80 if (ioctl(0, FIONBIO, &on) < 0) { 81 syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); 82 exit(1); 83 } 84 fromlen = sizeof (from); 85 n = recvfrom(0, buf, sizeof (buf), 0, 86 (caddr_t)&from, &fromlen); 87 if (n < 0) { 88 syslog(LOG_ERR, "recvfrom: %m\n"); 89 exit(1); 90 } 91 /* 92 * Now that we have read the message out of the UDP 93 * socket, we fork and exit. Thus, inetd will go back 94 * to listening to the tftp port, and the next request 95 * to come in will start up a new instance of tftpd. 96 * 97 * We do this so that inetd can run tftpd in "wait" mode. 98 * The problem with tftpd running in "nowait" mode is that 99 * inetd may get one or more successful "selects" on the 100 * tftp port before we do our receive, so more than one 101 * instance of tftpd may be started up. Worse, if tftpd 102 * break before doing the above "recvfrom", inetd would 103 * spawn endless instances, clogging the system. 104 */ 105 { 106 int pid; 107 int i, j; 108 109 for (i = 1; i < 20; i++) { 110 pid = fork(); 111 if (pid < 0) { 112 sleep(i); 113 /* 114 * flush out to most recently sent request. 115 * 116 * This may drop some request, but those 117 * will be resent by the clients when 118 * they timeout. The positive effect of 119 * this flush is to (try to) prevent more 120 * than one tftpd being started up to service 121 * a single request from a single client. 122 */ 123 j = sizeof from; 124 i = recvfrom(0, buf, sizeof (buf), 0, 125 (caddr_t)&from, &j); 126 if (i > 0) { 127 n = i; 128 fromlen = j; 129 } 130 } else { 131 break; 132 } 133 } 134 if (pid < 0) { 135 syslog(LOG_ERR, "fork: %m\n"); 136 exit(1); 137 } else if (pid != 0) { 138 exit(0); 139 } 140 } 141 from.sin_family = AF_INET; 142 alarm(0); 143 close(0); 144 close(1); 145 peer = socket(AF_INET, SOCK_DGRAM, 0); 146 if (peer < 0) { 147 syslog(LOG_ERR, "socket: %m\n"); 148 exit(1); 149 } 150 if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) { 151 syslog(LOG_ERR, "bind: %m\n"); 152 exit(1); 153 } 154 if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) { 155 syslog(LOG_ERR, "connect: %m\n"); 156 exit(1); 157 } 158 tp = (struct tftphdr *)buf; 159 tp->th_opcode = ntohs(tp->th_opcode); 160 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 161 tftp(tp, n); 162 exit(1); 163 } 164 165 int validate_access(); 166 int sendfile(), recvfile(); 167 168 struct formats { 169 char *f_mode; 170 int (*f_validate)(); 171 int (*f_send)(); 172 int (*f_recv)(); 173 int f_convert; 174 } formats[] = { 175 { "netascii", validate_access, sendfile, recvfile, 1 }, 176 { "octet", validate_access, sendfile, recvfile, 0 }, 177 #ifdef notdef 178 { "mail", validate_user, sendmail, recvmail, 1 }, 179 #endif 180 { 0 } 181 }; 182 183 /* 184 * Handle initial connection protocol. 185 */ 186 tftp(tp, size) 187 struct tftphdr *tp; 188 int size; 189 { 190 register char *cp; 191 int first = 1, ecode; 192 register struct formats *pf; 193 char *filename, *mode; 194 195 filename = cp = tp->th_stuff; 196 again: 197 while (cp < buf + size) { 198 if (*cp == '\0') 199 break; 200 cp++; 201 } 202 if (*cp != '\0') { 203 nak(EBADOP); 204 exit(1); 205 } 206 if (first) { 207 mode = ++cp; 208 first = 0; 209 goto again; 210 } 211 for (cp = mode; *cp; cp++) 212 if (isupper(*cp)) 213 *cp = tolower(*cp); 214 for (pf = formats; pf->f_mode; pf++) 215 if (strcmp(pf->f_mode, mode) == 0) 216 break; 217 if (pf->f_mode == 0) { 218 nak(EBADOP); 219 exit(1); 220 } 221 ecode = (*pf->f_validate)(filename, tp->th_opcode); 222 if (ecode) { 223 nak(ecode); 224 exit(1); 225 } 226 if (tp->th_opcode == WRQ) 227 (*pf->f_recv)(pf); 228 else 229 (*pf->f_send)(pf); 230 exit(0); 231 } 232 233 234 FILE *file; 235 236 /* 237 * Validate file access. Since we 238 * have no uid or gid, for now require 239 * file to exist and be publicly 240 * readable/writable. 241 * If we were invoked with arguments 242 * from inetd then the file must also be 243 * in one of the given directory prefixes. 244 * Note also, full path name must be 245 * given as we have no login directory. 246 */ 247 validate_access(filename, mode) 248 char *filename; 249 int mode; 250 { 251 struct stat stbuf; 252 int fd; 253 char **dirp = dirs; 254 255 if (*filename != '/') 256 return (EACCESS); 257 for (; *dirp; dirp++) 258 if (strncmp(filename, *dirp, strlen(*dirp)) == 0) 259 break; 260 if (*dirp==0 && dirp!=dirs) 261 return (EACCESS); 262 if (stat(filename, &stbuf) < 0) 263 return (errno == ENOENT ? ENOTFOUND : EACCESS); 264 if (mode == RRQ) { 265 if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) 266 return (EACCESS); 267 } else { 268 if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) 269 return (EACCESS); 270 } 271 fd = open(filename, mode == RRQ ? 0 : 1); 272 if (fd < 0) 273 return (errno + 100); 274 file = fdopen(fd, (mode == RRQ)? "r":"w"); 275 if (file == NULL) { 276 return errno+100; 277 } 278 return (0); 279 } 280 281 int timeout; 282 jmp_buf timeoutbuf; 283 284 timer() 285 { 286 287 timeout += rexmtval; 288 if (timeout >= maxtimeout) 289 exit(1); 290 longjmp(timeoutbuf, 1); 291 } 292 293 /* 294 * Send the requested file. 295 */ 296 sendfile(pf) 297 struct formats *pf; 298 { 299 struct tftphdr *dp, *r_init(); 300 register struct tftphdr *ap; /* ack packet */ 301 register int block = 1, size, n; 302 303 signal(SIGALRM, timer); 304 dp = r_init(); 305 ap = (struct tftphdr *)ackbuf; 306 do { 307 size = readit(file, &dp, pf->f_convert); 308 if (size < 0) { 309 nak(errno + 100); 310 goto abort; 311 } 312 dp->th_opcode = htons((u_short)DATA); 313 dp->th_block = htons((u_short)block); 314 timeout = 0; 315 (void) setjmp(timeoutbuf); 316 317 send_data: 318 if (send(peer, dp, size + 4, 0) != size + 4) { 319 syslog(LOG_ERR, "tftpd: write: %m\n"); 320 goto abort; 321 } 322 read_ahead(file, pf->f_convert); 323 for ( ; ; ) { 324 alarm(rexmtval); /* read the ack */ 325 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 326 alarm(0); 327 if (n < 0) { 328 syslog(LOG_ERR, "tftpd: read: %m\n"); 329 goto abort; 330 } 331 ap->th_opcode = ntohs((u_short)ap->th_opcode); 332 ap->th_block = ntohs((u_short)ap->th_block); 333 334 if (ap->th_opcode == ERROR) 335 goto abort; 336 337 if (ap->th_opcode == ACK) { 338 if (ap->th_block == block) { 339 break; 340 } 341 /* Re-synchronize with the other side */ 342 (void) synchnet(peer); 343 if (ap->th_block == (block -1)) { 344 goto send_data; 345 } 346 } 347 348 } 349 block++; 350 } while (size == SEGSIZE); 351 abort: 352 (void) fclose(file); 353 } 354 355 justquit() 356 { 357 exit(0); 358 } 359 360 361 /* 362 * Receive a file. 363 */ 364 recvfile(pf) 365 struct formats *pf; 366 { 367 struct tftphdr *dp, *w_init(); 368 register struct tftphdr *ap; /* ack buffer */ 369 register int block = 0, n, size; 370 371 signal(SIGALRM, timer); 372 dp = w_init(); 373 ap = (struct tftphdr *)ackbuf; 374 do { 375 timeout = 0; 376 ap->th_opcode = htons((u_short)ACK); 377 ap->th_block = htons((u_short)block); 378 block++; 379 (void) setjmp(timeoutbuf); 380 send_ack: 381 if (send(peer, ackbuf, 4, 0) != 4) { 382 syslog(LOG_ERR, "tftpd: write: %m\n"); 383 goto abort; 384 } 385 write_behind(file, pf->f_convert); 386 for ( ; ; ) { 387 alarm(rexmtval); 388 n = recv(peer, dp, PKTSIZE, 0); 389 alarm(0); 390 if (n < 0) { /* really? */ 391 syslog(LOG_ERR, "tftpd: read: %m\n"); 392 goto abort; 393 } 394 dp->th_opcode = ntohs((u_short)dp->th_opcode); 395 dp->th_block = ntohs((u_short)dp->th_block); 396 if (dp->th_opcode == ERROR) 397 goto abort; 398 if (dp->th_opcode == DATA) { 399 if (dp->th_block == block) { 400 break; /* normal */ 401 } 402 /* Re-synchronize with the other side */ 403 (void) synchnet(peer); 404 if (dp->th_block == (block-1)) 405 goto send_ack; /* rexmit */ 406 } 407 } 408 /* size = write(file, dp->th_data, n - 4); */ 409 size = writeit(file, &dp, n - 4, pf->f_convert); 410 if (size != (n-4)) { /* ahem */ 411 if (size < 0) nak(errno + 100); 412 else nak(ENOSPACE); 413 goto abort; 414 } 415 } while (size == SEGSIZE); 416 write_behind(file, pf->f_convert); 417 (void) fclose(file); /* close data file */ 418 419 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 420 ap->th_block = htons((u_short)(block)); 421 (void) send(peer, ackbuf, 4, 0); 422 423 signal(SIGALRM, justquit); /* just quit on timeout */ 424 alarm(rexmtval); 425 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 426 alarm(0); 427 if (n >= 4 && /* if read some data */ 428 dp->th_opcode == DATA && /* and got a data block */ 429 block == dp->th_block) { /* then my last ack was lost */ 430 (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 431 } 432 abort: 433 return; 434 } 435 436 struct errmsg { 437 int e_code; 438 char *e_msg; 439 } errmsgs[] = { 440 { EUNDEF, "Undefined error code" }, 441 { ENOTFOUND, "File not found" }, 442 { EACCESS, "Access violation" }, 443 { ENOSPACE, "Disk full or allocation exceeded" }, 444 { EBADOP, "Illegal TFTP operation" }, 445 { EBADID, "Unknown transfer ID" }, 446 { EEXISTS, "File already exists" }, 447 { ENOUSER, "No such user" }, 448 { -1, 0 } 449 }; 450 451 /* 452 * Send a nak packet (error message). 453 * Error code passed in is one of the 454 * standard TFTP codes, or a UNIX errno 455 * offset by 100. 456 */ 457 nak(error) 458 int error; 459 { 460 register struct tftphdr *tp; 461 int length; 462 register struct errmsg *pe; 463 extern char *sys_errlist[]; 464 465 tp = (struct tftphdr *)buf; 466 tp->th_opcode = htons((u_short)ERROR); 467 tp->th_code = htons((u_short)error); 468 for (pe = errmsgs; pe->e_code >= 0; pe++) 469 if (pe->e_code == error) 470 break; 471 if (pe->e_code < 0) { 472 pe->e_msg = sys_errlist[error - 100]; 473 tp->th_code = EUNDEF; /* set 'undef' errorcode */ 474 } 475 strcpy(tp->th_msg, pe->e_msg); 476 length = strlen(pe->e_msg); 477 tp->th_msg[length] = '\0'; 478 length += 5; 479 if (send(peer, buf, length, 0) != length) 480 syslog(LOG_ERR, "nak: %m\n"); 481 } 482