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