|
|
1.1 ! root 1: #ifndef lint ! 2: static char sccsid[] = "@(#)tftpd.c 4.11 (Berkeley) 7/2/83"; ! 3: #endif ! 4: ! 5: /* ! 6: * Trivial file transfer protocol server. ! 7: */ ! 8: #include <sys/types.h> ! 9: #include <sys/socket.h> ! 10: #include <sys/ioctl.h> ! 11: #include <sys/wait.h> ! 12: #include <sys/stat.h> ! 13: ! 14: #include <netinet/in.h> ! 15: ! 16: #include <arpa/tftp.h> ! 17: ! 18: #include <signal.h> ! 19: #include <stdio.h> ! 20: #include <errno.h> ! 21: #include <ctype.h> ! 22: #include <netdb.h> ! 23: #include <setjmp.h> ! 24: ! 25: #define TIMEOUT 5 ! 26: ! 27: extern int errno; ! 28: struct sockaddr_in sin = { AF_INET }; ! 29: int f; ! 30: int rexmtval = TIMEOUT; ! 31: int maxtimeout = 5*TIMEOUT; ! 32: char buf[BUFSIZ]; ! 33: int reapchild(); ! 34: ! 35: main(argc, argv) ! 36: char *argv[]; ! 37: { ! 38: struct sockaddr_in from; ! 39: register struct tftphdr *tp; ! 40: register int n; ! 41: struct servent *sp; ! 42: ! 43: sp = getservbyname("tftp", "udp"); ! 44: if (sp == 0) { ! 45: fprintf(stderr, "tftpd: udp/tftp: unknown service\n"); ! 46: exit(1); ! 47: } ! 48: sin.sin_port = sp->s_port; ! 49: #ifndef DEBUG ! 50: if (fork()) ! 51: exit(0); ! 52: for (f = 0; f < 10; f++) ! 53: (void) close(f); ! 54: (void) open("/", 0); ! 55: (void) dup2(0, 1); ! 56: (void) dup2(0, 2); ! 57: { int t = open("/dev/tty", 2); ! 58: if (t >= 0) { ! 59: ioctl(t, TIOCNOTTY, (char *)0); ! 60: (void) close(t); ! 61: } ! 62: } ! 63: #endif ! 64: signal(SIGCHLD, reapchild); ! 65: for (;;) { ! 66: int fromlen; ! 67: ! 68: f = socket(AF_INET, SOCK_DGRAM, 0); ! 69: if (f < 0) { ! 70: perror("tftpd: socket"); ! 71: sleep(5); ! 72: continue; ! 73: } ! 74: if (setsockopt(f, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) ! 75: perror("tftpd: setsockopt (SO_REUSEADDR)"); ! 76: sleep(1); /* let child do connect */ ! 77: while (bind(f, (caddr_t)&sin, sizeof (sin), 0) < 0) { ! 78: perror("tftpd: bind"); ! 79: sleep(5); ! 80: } ! 81: do { ! 82: fromlen = sizeof (from); ! 83: n = recvfrom(f, buf, sizeof (buf), 0, ! 84: (caddr_t)&from, &fromlen); ! 85: } while (n <= 0); ! 86: tp = (struct tftphdr *)buf; ! 87: tp->th_opcode = ntohs(tp->th_opcode); ! 88: if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) ! 89: if (fork() == 0) ! 90: tftp(&from, tp, n); ! 91: (void) close(f); ! 92: } ! 93: } ! 94: ! 95: reapchild() ! 96: { ! 97: union wait status; ! 98: ! 99: while (wait3(&status, WNOHANG, 0) > 0) ! 100: ; ! 101: } ! 102: ! 103: int validate_access(); ! 104: int sendfile(), recvfile(); ! 105: ! 106: struct formats { ! 107: char *f_mode; ! 108: int (*f_validate)(); ! 109: int (*f_send)(); ! 110: int (*f_recv)(); ! 111: } formats[] = { ! 112: { "netascii", validate_access, sendfile, recvfile }, ! 113: { "octet", validate_access, sendfile, recvfile }, ! 114: #ifdef notdef ! 115: { "mail", validate_user, sendmail, recvmail }, ! 116: #endif ! 117: { 0 } ! 118: }; ! 119: ! 120: int fd; /* file being transferred */ ! 121: ! 122: /* ! 123: * Handle initial connection protocol. ! 124: */ ! 125: tftp(client, tp, size) ! 126: struct sockaddr_in *client; ! 127: struct tftphdr *tp; ! 128: int size; ! 129: { ! 130: register char *cp; ! 131: int first = 1, ecode; ! 132: register struct formats *pf; ! 133: char *filename, *mode; ! 134: ! 135: if (connect(f, (caddr_t)client, sizeof (*client), 0) < 0) { ! 136: perror("connect"); ! 137: exit(1); ! 138: } ! 139: filename = cp = tp->th_stuff; ! 140: again: ! 141: while (cp < buf + size) { ! 142: if (*cp == '\0') ! 143: break; ! 144: cp++; ! 145: } ! 146: if (*cp != '\0') { ! 147: nak(EBADOP); ! 148: exit(1); ! 149: } ! 150: if (first) { ! 151: mode = ++cp; ! 152: first = 0; ! 153: goto again; ! 154: } ! 155: for (cp = mode; *cp; cp++) ! 156: if (isupper(*cp)) ! 157: *cp = tolower(*cp); ! 158: for (pf = formats; pf->f_mode; pf++) ! 159: if (strcmp(pf->f_mode, mode) == 0) ! 160: break; ! 161: if (pf->f_mode == 0) { ! 162: nak(EBADOP); ! 163: exit(1); ! 164: } ! 165: ecode = (*pf->f_validate)(filename, client, tp->th_opcode); ! 166: if (ecode) { ! 167: nak(ecode); ! 168: exit(1); ! 169: } ! 170: if (tp->th_opcode == WRQ) ! 171: (*pf->f_recv)(pf); ! 172: else ! 173: (*pf->f_send)(pf); ! 174: exit(0); ! 175: } ! 176: ! 177: /* ! 178: * Validate file access. Since we ! 179: * have no uid or gid, for now require ! 180: * file to exist and be publicly ! 181: * readable/writable. ! 182: * Note also, full path name must be ! 183: * given as we have no login directory. ! 184: */ ! 185: validate_access(file, client, mode) ! 186: char *file; ! 187: struct sockaddr_in *client; ! 188: int mode; ! 189: { ! 190: struct stat stbuf; ! 191: ! 192: if (*file != '/') ! 193: return (EACCESS); ! 194: if (stat(file, &stbuf) < 0) ! 195: return (errno == ENOENT ? ENOTFOUND : EACCESS); ! 196: if (mode == RRQ) { ! 197: if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) ! 198: return (EACCESS); ! 199: } else { ! 200: if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) ! 201: return (EACCESS); ! 202: } ! 203: fd = open(file, mode == RRQ ? 0 : 1); ! 204: if (fd < 0) ! 205: return (errno + 100); ! 206: return (0); ! 207: } ! 208: ! 209: int timeout; ! 210: jmp_buf timeoutbuf; ! 211: ! 212: timer() ! 213: { ! 214: ! 215: timeout += rexmtval; ! 216: if (timeout >= maxtimeout) ! 217: exit(1); ! 218: longjmp(timeoutbuf, 1); ! 219: } ! 220: ! 221: /* ! 222: * Send the requested file. ! 223: */ ! 224: sendfile(pf) ! 225: struct format *pf; ! 226: { ! 227: register struct tftphdr *tp; ! 228: register int block = 1, size, n; ! 229: ! 230: signal(SIGALRM, timer); ! 231: tp = (struct tftphdr *)buf; ! 232: do { ! 233: size = read(fd, tp->th_data, SEGSIZE); ! 234: if (size < 0) { ! 235: nak(errno + 100); ! 236: goto abort; ! 237: } ! 238: tp->th_opcode = htons((u_short)DATA); ! 239: tp->th_block = htons((u_short)block); ! 240: timeout = 0; ! 241: (void) setjmp(timeoutbuf); ! 242: if (write(f, buf, size + 4) != size + 4) { ! 243: perror("tftpd: write"); ! 244: goto abort; ! 245: } ! 246: do { ! 247: alarm(rexmtval); ! 248: n = read(f, buf, sizeof (buf)); ! 249: alarm(0); ! 250: if (n < 0) { ! 251: perror("tftpd: read"); ! 252: goto abort; ! 253: } ! 254: tp->th_opcode = ntohs((u_short)tp->th_opcode); ! 255: tp->th_block = ntohs((u_short)tp->th_block); ! 256: if (tp->th_opcode == ERROR) ! 257: goto abort; ! 258: } while (tp->th_opcode != ACK || tp->th_block != block); ! 259: block++; ! 260: } while (size == SEGSIZE); ! 261: abort: ! 262: (void) close(fd); ! 263: } ! 264: ! 265: /* ! 266: * Receive a file. ! 267: */ ! 268: recvfile(pf) ! 269: struct format *pf; ! 270: { ! 271: register struct tftphdr *tp; ! 272: register int block = 0, n, size; ! 273: ! 274: signal(SIGALRM, timer); ! 275: tp = (struct tftphdr *)buf; ! 276: do { ! 277: timeout = 0; ! 278: tp->th_opcode = htons((u_short)ACK); ! 279: tp->th_block = htons((u_short)block); ! 280: block++; ! 281: (void) setjmp(timeoutbuf); ! 282: if (write(f, buf, 4) != 4) { ! 283: perror("tftpd: write"); ! 284: goto abort; ! 285: } ! 286: do { ! 287: alarm(rexmtval); ! 288: n = read(f, buf, sizeof (buf)); ! 289: alarm(0); ! 290: if (n < 0) { ! 291: perror("tftpd: read"); ! 292: goto abort; ! 293: } ! 294: tp->th_opcode = ntohs((u_short)tp->th_opcode); ! 295: tp->th_block = ntohs((u_short)tp->th_block); ! 296: if (tp->th_opcode == ERROR) ! 297: goto abort; ! 298: } while (tp->th_opcode != DATA || block != tp->th_block); ! 299: size = write(fd, tp->th_data, n - 4); ! 300: if (size < 0) { ! 301: nak(errno + 100); ! 302: goto abort; ! 303: } ! 304: } while (size == SEGSIZE); ! 305: abort: ! 306: tp->th_opcode = htons((u_short)ACK); ! 307: tp->th_block = htons((u_short)(block)); ! 308: (void) write(f, buf, 4); ! 309: (void) close(fd); ! 310: } ! 311: ! 312: struct errmsg { ! 313: int e_code; ! 314: char *e_msg; ! 315: } errmsgs[] = { ! 316: { EUNDEF, "Undefined error code" }, ! 317: { ENOTFOUND, "File not found" }, ! 318: { EACCESS, "Access violation" }, ! 319: { ENOSPACE, "Disk full or allocation exceeded" }, ! 320: { EBADOP, "Illegal TFTP operation" }, ! 321: { EBADID, "Unknown transfer ID" }, ! 322: { EEXISTS, "File already exists" }, ! 323: { ENOUSER, "No such user" }, ! 324: { -1, 0 } ! 325: }; ! 326: ! 327: /* ! 328: * Send a nak packet (error message). ! 329: * Error code passed in is one of the ! 330: * standard TFTP codes, or a UNIX errno ! 331: * offset by 100. ! 332: */ ! 333: nak(error) ! 334: int error; ! 335: { ! 336: register struct tftphdr *tp; ! 337: int length; ! 338: register struct errmsg *pe; ! 339: extern char *sys_errlist[]; ! 340: ! 341: tp = (struct tftphdr *)buf; ! 342: tp->th_opcode = htons((u_short)ERROR); ! 343: tp->th_code = htons((u_short)error); ! 344: for (pe = errmsgs; pe->e_code >= 0; pe++) ! 345: if (pe->e_code == error) ! 346: break; ! 347: if (pe->e_code < 0) ! 348: pe->e_msg = sys_errlist[error - 100]; ! 349: strcpy(tp->th_msg, pe->e_msg); ! 350: length = strlen(pe->e_msg); ! 351: tp->th_msg[length] = '\0'; ! 352: length += 5; ! 353: if (write(f, buf, length) != length) ! 354: perror("nak"); ! 355: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.