1*9971Ssam /* tftpd.c 4.7 82/12/25 */ 27772Ssam 37772Ssam /* 47772Ssam * Trivial file transfer protocol server. 57772Ssam */ 67772Ssam #include <sys/types.h> 77772Ssam #include <sys/socket.h> 89220Ssam #include <sys/ioctl.h> 99220Ssam 109220Ssam #include <netinet/in.h> 119220Ssam 127772Ssam #include <signal.h> 137772Ssam #include <stat.h> 147772Ssam #include <stdio.h> 157772Ssam #include <wait.h> 167772Ssam #include <errno.h> 177772Ssam #include <ctype.h> 188385Ssam #include <netdb.h> 199220Ssam 207772Ssam #include "tftp.h" 217772Ssam 227772Ssam extern int errno; 238385Ssam struct sockaddr_in sin = { AF_INET }; 247772Ssam int f; 257772Ssam char buf[BUFSIZ]; 267772Ssam 277772Ssam main(argc, argv) 287772Ssam char *argv[]; 297772Ssam { 307772Ssam union wait status; 317772Ssam struct sockaddr_in from; 327772Ssam register struct tftphdr *tp; 337772Ssam register int n; 348385Ssam struct servent *sp; 357772Ssam 368385Ssam sp = getservbyname("tftp", "udp"); 378385Ssam if (sp == 0) { 388385Ssam fprintf(stderr, "tftpd: udp/tftp: unknown service\n"); 398385Ssam exit(1); 408385Ssam } 41*9971Ssam sin.sin_port = sp->s_port; 427772Ssam #ifndef DEBUG 437772Ssam if (fork()) 447772Ssam exit(0); 457772Ssam for (f = 0; f < 10; f++) 467772Ssam (void) close(f); 477772Ssam (void) open("/", 0); 487772Ssam (void) dup2(0, 1); 497772Ssam (void) dup2(0, 2); 507772Ssam { int t = open("/dev/tty", 2); 517772Ssam if (t >= 0) { 527772Ssam ioctl(t, TIOCNOTTY, (char *)0); 537772Ssam (void) close(t); 547772Ssam } 557772Ssam } 567772Ssam #endif 577772Ssam for (;;) { 589220Ssam int fromlen; 599220Ssam 609262Ssam f = socket(AF_INET, SOCK_DGRAM, 0, 0); 617772Ssam if (f < 0) { 629220Ssam perror("tftpd: socket"); 639220Ssam close(f); 647772Ssam sleep(5); 657772Ssam continue; 667772Ssam } 679220Ssam while (bind(f, (caddr_t)&sin, sizeof (sin), 0) < 0) { 689220Ssam perror("tftpd: bind"); 699220Ssam close(f); 709220Ssam sleep(5); 719220Ssam continue; 729220Ssam } 737772Ssam again: 749220Ssam fromlen = sizeof (from); 759245Ssam n = recvfrom(f, buf, sizeof (buf), 0, (caddr_t)&from, &fromlen); 767772Ssam if (n <= 0) { 777772Ssam if (n < 0) 789220Ssam perror("tftpd: recvfrom"); 797772Ssam goto again; 807772Ssam } 817772Ssam tp = (struct tftphdr *)buf; 827772Ssam tp->th_opcode = ntohs(tp->th_opcode); 837772Ssam if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 847772Ssam if (fork() == 0) 857772Ssam tftp(&from, tp, n); 867772Ssam (void) close(f); 877772Ssam while (wait3(status, WNOHANG, 0) > 0) 887772Ssam continue; 897772Ssam } 907772Ssam } 917772Ssam 927772Ssam int validate_access(); 937772Ssam int sendfile(), recvfile(); 947772Ssam 957772Ssam struct formats { 967772Ssam char *f_mode; 977772Ssam int (*f_validate)(); 987772Ssam int (*f_send)(); 997772Ssam int (*f_recv)(); 1007772Ssam } formats[] = { 1017772Ssam { "netascii", validate_access, sendfile, recvfile }, 1027772Ssam { "octet", validate_access, sendfile, recvfile }, 1037772Ssam #ifdef notdef 1047772Ssam { "mail", validate_user, sendmail, recvmail }, 1057772Ssam #endif 1067772Ssam { 0 } 1077772Ssam }; 1087772Ssam 1097772Ssam int fd; /* file being transferred */ 1107772Ssam 1117772Ssam /* 1127772Ssam * Handle initial connection protocol. 1137772Ssam */ 1147772Ssam tftp(client, tp, size) 1157772Ssam struct sockaddr_in *client; 1167772Ssam struct tftphdr *tp; 1177772Ssam int size; 1187772Ssam { 1197772Ssam register char *cp; 1207772Ssam int first = 1, ecode; 1217772Ssam register struct formats *pf; 1227772Ssam char *filename, *mode; 1237772Ssam 1249220Ssam if (connect(f, (caddr_t)client, sizeof (*client), 0) < 0) { 1257772Ssam perror("connect"); 1267772Ssam exit(1); 1277772Ssam } 1287772Ssam filename = cp = tp->th_stuff; 1297772Ssam again: 1307772Ssam while (cp < buf + size) { 1317772Ssam if (*cp == '\0') 1327772Ssam break; 1337772Ssam cp++; 1347772Ssam } 1357772Ssam if (*cp != '\0') { 1367772Ssam nak(EBADOP); 1377772Ssam exit(1); 1387772Ssam } 1397772Ssam if (first) { 1407772Ssam mode = ++cp; 1417772Ssam first = 0; 1427772Ssam goto again; 1437772Ssam } 1447772Ssam for (cp = mode; *cp; cp++) 1457772Ssam if (isupper(*cp)) 1467772Ssam *cp = tolower(*cp); 1477772Ssam for (pf = formats; pf->f_mode; pf++) 1487772Ssam if (strcmp(pf->f_mode, mode) == 0) 1497772Ssam break; 1507772Ssam if (pf->f_mode == 0) { 1517772Ssam nak(EBADOP); 1527772Ssam exit(1); 1537772Ssam } 1547772Ssam ecode = (*pf->f_validate)(filename, client, tp->th_opcode); 1557772Ssam if (ecode) { 1567772Ssam nak(ecode); 1577772Ssam exit(1); 1587772Ssam } 1597772Ssam if (tp->th_opcode == WRQ) 1607772Ssam (*pf->f_recv)(pf); 1617772Ssam else 1627772Ssam (*pf->f_send)(pf); 1637772Ssam exit(0); 1647772Ssam } 1657772Ssam 1667772Ssam /* 1677772Ssam * Validate file access. Since we 1687772Ssam * have no uid or gid, for now require 1697772Ssam * file to exist and be publicly 1707772Ssam * readable/writable. 1717772Ssam * Note also, full path name must be 1727772Ssam * given as we have no login directory. 1737772Ssam */ 1747772Ssam validate_access(file, client, mode) 1757772Ssam char *file; 1767772Ssam struct sockaddr_in *client; 1777772Ssam int mode; 1787772Ssam { 1797772Ssam struct stat stbuf; 1807772Ssam 1817772Ssam if (*file != '/') 1827772Ssam return (EACCESS); 1837772Ssam if (stat(file, &stbuf) < 0) 1847772Ssam return (errno == ENOENT ? ENOTFOUND : EACCESS); 1857772Ssam if (mode == RRQ) { 1867772Ssam if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) 1877772Ssam return (EACCESS); 1887772Ssam } else { 1897772Ssam if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) 1907772Ssam return (EACCESS); 1917772Ssam } 1927772Ssam fd = open(file, mode == RRQ ? 0 : 1); 1937772Ssam if (fd < 0) 1947772Ssam return (errno + 100); 1957772Ssam return (0); 1967772Ssam } 1977772Ssam 1987772Ssam int timeout; 1997772Ssam 2007772Ssam timer() 2017772Ssam { 2027772Ssam timeout += TIMEOUT; 2037772Ssam if (timeout >= MAXTIMEOUT) 2047772Ssam exit(1); 2057772Ssam alarm(TIMEOUT); 2067772Ssam } 2077772Ssam 2087772Ssam /* 2097772Ssam * Send the requested file. 2107772Ssam */ 2117772Ssam sendfile(pf) 2127772Ssam struct format *pf; 2137772Ssam { 2147772Ssam register struct tftphdr *tp; 2157772Ssam register int block = 1, size, n; 2167772Ssam 2177772Ssam sigset(SIGALRM, timer); 2187772Ssam tp = (struct tftphdr *)buf; 2197772Ssam do { 2207772Ssam size = read(fd, tp->th_data, SEGSIZE); 2217772Ssam if (size < 0) { 2227772Ssam nak(errno + 100); 2237772Ssam break; 2247772Ssam } 2257772Ssam tp->th_opcode = htons((u_short)DATA); 2267772Ssam tp->th_block = htons((u_short)block); 2277772Ssam timeout = 0; 2287772Ssam alarm(TIMEOUT); 2297772Ssam rexmt: 2307772Ssam if (write(f, buf, size + 4) != size + 4) { 2317772Ssam perror("send"); 2327772Ssam break; 2337772Ssam } 2347772Ssam again: 2357772Ssam n = read(f, buf, sizeof (buf)); 2367772Ssam if (n <= 0) { 2377772Ssam if (n == 0) 2387772Ssam goto again; 2397772Ssam if (errno == EINTR) 2407772Ssam goto rexmt; 2417772Ssam alarm(0); 2427772Ssam perror("receive"); 2437772Ssam break; 2447772Ssam } 2457772Ssam alarm(0); 2467772Ssam #if vax || pdp11 2479220Ssam tp->th_opcode = ntohs((u_short)tp->th_opcode); 2489220Ssam tp->th_block = ntohs((u_short)tp->th_block); 2497772Ssam #endif 2507772Ssam if (tp->th_opcode == ERROR) 2517772Ssam break; 2527772Ssam if (tp->th_opcode != ACK || tp->th_block != block) 2537772Ssam goto again; 2547772Ssam block++; 2557772Ssam } while (size == SEGSIZE); 2567772Ssam (void) close(fd); 2577772Ssam } 2587772Ssam 2597772Ssam /* 2607772Ssam * Receive a file. 2617772Ssam */ 2627772Ssam recvfile(pf) 2637772Ssam struct format *pf; 2647772Ssam { 2657772Ssam register struct tftphdr *tp; 2667772Ssam register int block = 0, n, size; 2677772Ssam 2687772Ssam sigset(SIGALRM, timer); 2697772Ssam tp = (struct tftphdr *)buf; 2707772Ssam do { 2717772Ssam timeout = 0; 2727772Ssam alarm(TIMEOUT); 2737772Ssam tp->th_opcode = htons((u_short)ACK); 2747772Ssam tp->th_block = htons((u_short)block); 2757772Ssam block++; 2767772Ssam rexmt: 2777772Ssam if (write(f, buf, 4) != 4) { 2787772Ssam perror("ack"); 2797772Ssam break; 2807772Ssam } 2817772Ssam again: 2827772Ssam n = read(f, buf, sizeof (buf)); 2837772Ssam if (n <= 0) { 2847772Ssam if (n == 0) 2857772Ssam goto again; 2867772Ssam if (errno == EINTR) 2877772Ssam goto rexmt; 2887772Ssam alarm(0); 2897772Ssam perror("receive"); 2907772Ssam break; 2917772Ssam } 2927772Ssam alarm(0); 2937772Ssam #if vax || pdp11 2949220Ssam tp->th_opcode = ntohs((u_short)tp->th_opcode); 2959220Ssam tp->th_block = ntohs((u_short)tp->th_block); 2967772Ssam #endif 2977772Ssam if (tp->th_opcode == ERROR) 2987772Ssam break; 2997772Ssam if (tp->th_opcode != DATA || block != tp->th_block) 3007772Ssam goto again; 3017772Ssam size = write(fd, tp->th_data, n - 4); 3027772Ssam if (size < 0) { 3037772Ssam nak(errno + 100); 3047772Ssam break; 3057772Ssam } 3067772Ssam } while (size == SEGSIZE); 3077772Ssam tp->th_opcode = htons((u_short)ACK); 3087772Ssam tp->th_block = htons((u_short)(block)); 3097772Ssam (void) write(f, buf, 4); 3107772Ssam (void) close(fd); 3117772Ssam } 3127772Ssam 3137772Ssam struct errmsg { 3147772Ssam int e_code; 3157772Ssam char *e_msg; 3167772Ssam } errmsgs[] = { 3177772Ssam { EUNDEF, "Undefined error code" }, 3187772Ssam { ENOTFOUND, "File not found" }, 3197772Ssam { EACCESS, "Access violation" }, 3207772Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 3217772Ssam { EBADOP, "Illegal TFTP operation" }, 3227772Ssam { EBADID, "Unknown transfer ID" }, 3237772Ssam { EEXISTS, "File already exists" }, 3247772Ssam { ENOUSER, "No such user" }, 3257772Ssam { -1, 0 } 3267772Ssam }; 3277772Ssam 3287772Ssam /* 3297772Ssam * Send a nak packet (error message). 3307772Ssam * Error code passed in is one of the 3317772Ssam * standard TFTP codes, or a UNIX errno 3327772Ssam * offset by 100. 3337772Ssam */ 3347772Ssam nak(error) 3357772Ssam int error; 3367772Ssam { 3377772Ssam register struct tftphdr *tp; 3387772Ssam int length; 3397772Ssam register struct errmsg *pe; 3407772Ssam extern char *sys_errlist[]; 3417772Ssam 3427772Ssam tp = (struct tftphdr *)buf; 3437772Ssam tp->th_opcode = htons((u_short)ERROR); 3447772Ssam tp->th_code = htons((u_short)error); 3457772Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 3467772Ssam if (pe->e_code == error) 3477772Ssam break; 3487772Ssam if (pe->e_code < 0) 3497772Ssam pe->e_msg = sys_errlist[error - 100]; 3507772Ssam strcpy(tp->th_msg, pe->e_msg); 3517772Ssam length = strlen(pe->e_msg); 3527772Ssam tp->th_msg[length] = '\0'; 3537772Ssam length += 5; 3547772Ssam if (write(f, buf, length) != length) 3557772Ssam perror("nak"); 3567772Ssam } 357