Annotation of src/usr.bin/tftp/tftp.c, Revision 1.1
1.1 ! deraadt 1: /* $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1983, 1993
! 5: * The Regents of the University of California. All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: * 3. All advertising materials mentioning features or use of this software
! 16: * must display the following acknowledgement:
! 17: * This product includes software developed by the University of
! 18: * California, Berkeley and its contributors.
! 19: * 4. Neither the name of the University nor the names of its contributors
! 20: * may be used to endorse or promote products derived from this software
! 21: * without specific prior written permission.
! 22: *
! 23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 33: * SUCH DAMAGE.
! 34: */
! 35:
! 36: #ifndef lint
! 37: #if 0
! 38: static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93";
! 39: #endif
! 40: static char rcsid[] = "$NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $";
! 41: #endif /* not lint */
! 42:
! 43: /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
! 44:
! 45: /*
! 46: * TFTP User Program -- Protocol Machines
! 47: */
! 48: #include <sys/types.h>
! 49: #include <sys/socket.h>
! 50: #include <sys/time.h>
! 51:
! 52: #include <netinet/in.h>
! 53:
! 54: #include <arpa/tftp.h>
! 55:
! 56: #include <errno.h>
! 57: #include <setjmp.h>
! 58: #include <signal.h>
! 59: #include <stdio.h>
! 60: #include <string.h>
! 61: #include <unistd.h>
! 62:
! 63: #include "extern.h"
! 64: #include "tftpsubs.h"
! 65:
! 66: extern int errno;
! 67:
! 68: extern struct sockaddr_in peeraddr; /* filled in by main */
! 69: extern int f; /* the opened socket */
! 70: extern int trace;
! 71: extern int verbose;
! 72: extern int rexmtval;
! 73: extern int maxtimeout;
! 74:
! 75: #define PKTSIZE SEGSIZE+4
! 76: char ackbuf[PKTSIZE];
! 77: int timeout;
! 78: jmp_buf toplevel;
! 79: jmp_buf timeoutbuf;
! 80:
! 81: static void nak __P((int));
! 82: static int makerequest __P((int, const char *, struct tftphdr *, const char *));
! 83: static void printstats __P((const char *, unsigned long));
! 84: static void startclock __P((void));
! 85: static void stopclock __P((void));
! 86: static void timer __P((int));
! 87: static void tpacket __P((const char *, struct tftphdr *, int));
! 88:
! 89: /*
! 90: * Send the requested file.
! 91: */
! 92: void
! 93: sendfile(fd, name, mode)
! 94: int fd;
! 95: char *name;
! 96: char *mode;
! 97: {
! 98: register struct tftphdr *ap; /* data and ack packets */
! 99: struct tftphdr *r_init(), *dp;
! 100: register int n;
! 101: volatile int block, size, convert;
! 102: volatile unsigned long amount;
! 103: struct sockaddr_in from;
! 104: int fromlen;
! 105: FILE *file;
! 106:
! 107: startclock(); /* start stat's clock */
! 108: dp = r_init(); /* reset fillbuf/read-ahead code */
! 109: ap = (struct tftphdr *)ackbuf;
! 110: file = fdopen(fd, "r");
! 111: convert = !strcmp(mode, "netascii");
! 112: block = 0;
! 113: amount = 0;
! 114:
! 115: signal(SIGALRM, timer);
! 116: do {
! 117: if (block == 0)
! 118: size = makerequest(WRQ, name, dp, mode) - 4;
! 119: else {
! 120: /* size = read(fd, dp->th_data, SEGSIZE); */
! 121: size = readit(file, &dp, convert);
! 122: if (size < 0) {
! 123: nak(errno + 100);
! 124: break;
! 125: }
! 126: dp->th_opcode = htons((u_short)DATA);
! 127: dp->th_block = htons((u_short)block);
! 128: }
! 129: timeout = 0;
! 130: (void) setjmp(timeoutbuf);
! 131: send_data:
! 132: if (trace)
! 133: tpacket("sent", dp, size + 4);
! 134: n = sendto(f, dp, size + 4, 0,
! 135: (struct sockaddr *)&peeraddr, sizeof(peeraddr));
! 136: if (n != size + 4) {
! 137: perror("tftp: sendto");
! 138: goto abort;
! 139: }
! 140: read_ahead(file, convert);
! 141: for ( ; ; ) {
! 142: alarm(rexmtval);
! 143: do {
! 144: fromlen = sizeof(from);
! 145: n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
! 146: (struct sockaddr *)&from, &fromlen);
! 147: } while (n <= 0);
! 148: alarm(0);
! 149: if (n < 0) {
! 150: perror("tftp: recvfrom");
! 151: goto abort;
! 152: }
! 153: peeraddr.sin_port = from.sin_port; /* added */
! 154: if (trace)
! 155: tpacket("received", ap, n);
! 156: /* should verify packet came from server */
! 157: ap->th_opcode = ntohs(ap->th_opcode);
! 158: ap->th_block = ntohs(ap->th_block);
! 159: if (ap->th_opcode == ERROR) {
! 160: printf("Error code %d: %s\n", ap->th_code,
! 161: ap->th_msg);
! 162: goto abort;
! 163: }
! 164: if (ap->th_opcode == ACK) {
! 165: int j;
! 166:
! 167: if (ap->th_block == block) {
! 168: break;
! 169: }
! 170: /* On an error, try to synchronize
! 171: * both sides.
! 172: */
! 173: j = synchnet(f);
! 174: if (j && trace) {
! 175: printf("discarded %d packets\n",
! 176: j);
! 177: }
! 178: if (ap->th_block == (block-1)) {
! 179: goto send_data;
! 180: }
! 181: }
! 182: }
! 183: if (block > 0)
! 184: amount += size;
! 185: block++;
! 186: } while (size == SEGSIZE || block == 1);
! 187: abort:
! 188: fclose(file);
! 189: stopclock();
! 190: if (amount > 0)
! 191: printstats("Sent", amount);
! 192: }
! 193:
! 194: /*
! 195: * Receive a file.
! 196: */
! 197: void
! 198: recvfile(fd, name, mode)
! 199: int fd;
! 200: char *name;
! 201: char *mode;
! 202: {
! 203: register struct tftphdr *ap;
! 204: struct tftphdr *dp, *w_init();
! 205: register int n;
! 206: volatile int block, size, firsttrip;
! 207: volatile unsigned long amount;
! 208: struct sockaddr_in from;
! 209: int fromlen;
! 210: FILE *file;
! 211: volatile int convert; /* true if converting crlf -> lf */
! 212:
! 213: startclock();
! 214: dp = w_init();
! 215: ap = (struct tftphdr *)ackbuf;
! 216: file = fdopen(fd, "w");
! 217: convert = !strcmp(mode, "netascii");
! 218: block = 1;
! 219: firsttrip = 1;
! 220: amount = 0;
! 221:
! 222: signal(SIGALRM, timer);
! 223: do {
! 224: if (firsttrip) {
! 225: size = makerequest(RRQ, name, ap, mode);
! 226: firsttrip = 0;
! 227: } else {
! 228: ap->th_opcode = htons((u_short)ACK);
! 229: ap->th_block = htons((u_short)(block));
! 230: size = 4;
! 231: block++;
! 232: }
! 233: timeout = 0;
! 234: (void) setjmp(timeoutbuf);
! 235: send_ack:
! 236: if (trace)
! 237: tpacket("sent", ap, size);
! 238: if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
! 239: sizeof(peeraddr)) != size) {
! 240: alarm(0);
! 241: perror("tftp: sendto");
! 242: goto abort;
! 243: }
! 244: write_behind(file, convert);
! 245: for ( ; ; ) {
! 246: alarm(rexmtval);
! 247: do {
! 248: fromlen = sizeof(from);
! 249: n = recvfrom(f, dp, PKTSIZE, 0,
! 250: (struct sockaddr *)&from, &fromlen);
! 251: } while (n <= 0);
! 252: alarm(0);
! 253: if (n < 0) {
! 254: perror("tftp: recvfrom");
! 255: goto abort;
! 256: }
! 257: peeraddr.sin_port = from.sin_port; /* added */
! 258: if (trace)
! 259: tpacket("received", dp, n);
! 260: /* should verify client address */
! 261: dp->th_opcode = ntohs(dp->th_opcode);
! 262: dp->th_block = ntohs(dp->th_block);
! 263: if (dp->th_opcode == ERROR) {
! 264: printf("Error code %d: %s\n", dp->th_code,
! 265: dp->th_msg);
! 266: goto abort;
! 267: }
! 268: if (dp->th_opcode == DATA) {
! 269: int j;
! 270:
! 271: if (dp->th_block == block) {
! 272: break; /* have next packet */
! 273: }
! 274: /* On an error, try to synchronize
! 275: * both sides.
! 276: */
! 277: j = synchnet(f);
! 278: if (j && trace) {
! 279: printf("discarded %d packets\n", j);
! 280: }
! 281: if (dp->th_block == (block-1)) {
! 282: goto send_ack; /* resend ack */
! 283: }
! 284: }
! 285: }
! 286: /* size = write(fd, dp->th_data, n - 4); */
! 287: size = writeit(file, &dp, n - 4, convert);
! 288: if (size < 0) {
! 289: nak(errno + 100);
! 290: break;
! 291: }
! 292: amount += size;
! 293: } while (size == SEGSIZE);
! 294: abort: /* ok to ack, since user */
! 295: ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
! 296: ap->th_block = htons((u_short)block);
! 297: (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
! 298: sizeof(peeraddr));
! 299: write_behind(file, convert); /* flush last buffer */
! 300: fclose(file);
! 301: stopclock();
! 302: if (amount > 0)
! 303: printstats("Received", amount);
! 304: }
! 305:
! 306: static int
! 307: makerequest(request, name, tp, mode)
! 308: int request;
! 309: const char *name;
! 310: struct tftphdr *tp;
! 311: const char *mode;
! 312: {
! 313: register char *cp;
! 314:
! 315: tp->th_opcode = htons((u_short)request);
! 316: cp = tp->th_stuff;
! 317: strcpy(cp, name);
! 318: cp += strlen(name);
! 319: *cp++ = '\0';
! 320: strcpy(cp, mode);
! 321: cp += strlen(mode);
! 322: *cp++ = '\0';
! 323: return (cp - (char *)tp);
! 324: }
! 325:
! 326: struct errmsg {
! 327: int e_code;
! 328: char *e_msg;
! 329: } errmsgs[] = {
! 330: { EUNDEF, "Undefined error code" },
! 331: { ENOTFOUND, "File not found" },
! 332: { EACCESS, "Access violation" },
! 333: { ENOSPACE, "Disk full or allocation exceeded" },
! 334: { EBADOP, "Illegal TFTP operation" },
! 335: { EBADID, "Unknown transfer ID" },
! 336: { EEXISTS, "File already exists" },
! 337: { ENOUSER, "No such user" },
! 338: { -1, 0 }
! 339: };
! 340:
! 341: /*
! 342: * Send a nak packet (error message).
! 343: * Error code passed in is one of the
! 344: * standard TFTP codes, or a UNIX errno
! 345: * offset by 100.
! 346: */
! 347: static void
! 348: nak(error)
! 349: int error;
! 350: {
! 351: register struct errmsg *pe;
! 352: register struct tftphdr *tp;
! 353: int length;
! 354: char *strerror();
! 355:
! 356: tp = (struct tftphdr *)ackbuf;
! 357: tp->th_opcode = htons((u_short)ERROR);
! 358: tp->th_code = htons((u_short)error);
! 359: for (pe = errmsgs; pe->e_code >= 0; pe++)
! 360: if (pe->e_code == error)
! 361: break;
! 362: if (pe->e_code < 0) {
! 363: pe->e_msg = strerror(error - 100);
! 364: tp->th_code = EUNDEF;
! 365: }
! 366: strcpy(tp->th_msg, pe->e_msg);
! 367: length = strlen(pe->e_msg) + 4;
! 368: if (trace)
! 369: tpacket("sent", tp, length);
! 370: if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
! 371: sizeof(peeraddr)) != length)
! 372: perror("nak");
! 373: }
! 374:
! 375: static void
! 376: tpacket(s, tp, n)
! 377: const char *s;
! 378: struct tftphdr *tp;
! 379: int n;
! 380: {
! 381: static char *opcodes[] =
! 382: { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
! 383: register char *cp, *file;
! 384: u_short op = ntohs(tp->th_opcode);
! 385: char *index();
! 386:
! 387: if (op < RRQ || op > ERROR)
! 388: printf("%s opcode=%x ", s, op);
! 389: else
! 390: printf("%s %s ", s, opcodes[op]);
! 391: switch (op) {
! 392:
! 393: case RRQ:
! 394: case WRQ:
! 395: n -= 2;
! 396: file = cp = tp->th_stuff;
! 397: cp = index(cp, '\0');
! 398: printf("<file=%s, mode=%s>\n", file, cp + 1);
! 399: break;
! 400:
! 401: case DATA:
! 402: printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
! 403: break;
! 404:
! 405: case ACK:
! 406: printf("<block=%d>\n", ntohs(tp->th_block));
! 407: break;
! 408:
! 409: case ERROR:
! 410: printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
! 411: break;
! 412: }
! 413: }
! 414:
! 415: struct timeval tstart;
! 416: struct timeval tstop;
! 417:
! 418: static void
! 419: startclock()
! 420: {
! 421:
! 422: (void)gettimeofday(&tstart, NULL);
! 423: }
! 424:
! 425: static void
! 426: stopclock()
! 427: {
! 428:
! 429: (void)gettimeofday(&tstop, NULL);
! 430: }
! 431:
! 432: static void
! 433: printstats(direction, amount)
! 434: const char *direction;
! 435: unsigned long amount;
! 436: {
! 437: double delta;
! 438: /* compute delta in 1/10's second units */
! 439: delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
! 440: ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
! 441: delta = delta/10.; /* back to seconds */
! 442: printf("%s %d bytes in %.1f seconds", direction, amount, delta);
! 443: if (verbose)
! 444: printf(" [%.0f bits/sec]", (amount*8.)/delta);
! 445: putchar('\n');
! 446: }
! 447:
! 448: static void
! 449: timer(sig)
! 450: int sig;
! 451: {
! 452:
! 453: timeout += rexmtval;
! 454: if (timeout >= maxtimeout) {
! 455: printf("Transfer timed out.\n");
! 456: longjmp(toplevel, -1);
! 457: }
! 458: longjmp(timeoutbuf, 1);
! 459: }