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