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