Annotation of src/usr.bin/tftp/tftp.c, Revision 1.18
1.18 ! mglocker 1: /* $OpenBSD: tftp.c,v 1.17 2006/07/12 16:58:51 mglocker 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.17 mglocker 37: static const char rcsid[] =
1.18 ! mglocker 38: "$OpenBSD: tftp.c,v 1.17 2006/07/12 16:58:51 mglocker Exp $";
1.1 deraadt 39: #endif /* not lint */
40:
41: /*
42: * TFTP User Program -- Protocol Machines
1.17 mglocker 43: *
44: * This version includes many modifications by Jim Guyton <guyton@rand-unix>
1.1 deraadt 45: */
1.16 claudio 46:
1.1 deraadt 47: #include <sys/types.h>
48: #include <sys/socket.h>
49: #include <sys/time.h>
50:
51: #include <netinet/in.h>
52: #include <arpa/tftp.h>
53:
1.17 mglocker 54: #include <err.h>
1.1 deraadt 55: #include <errno.h>
1.16 claudio 56: #include <poll.h>
1.1 deraadt 57: #include <signal.h>
58: #include <stdio.h>
1.11 henning 59: #include <stddef.h>
1.1 deraadt 60: #include <string.h>
61: #include <unistd.h>
62:
63: #include "extern.h"
64: #include "tftpsubs.h"
65:
1.16 claudio 66: #define PKTSIZE SEGSIZE + 4
67:
1.17 mglocker 68: static int makerequest(int, const char *, struct tftphdr *, const char *);
69: static void nak(int);
70: static void tpacket(const char *, struct tftphdr *, int);
71: static void startclock(void);
72: static void stopclock(void);
73: static void printstats(const char *, unsigned long);
74: static void printtimeout(void);
75:
76: extern struct sockaddr_in peeraddr; /* filled in by main */
77: extern int f; /* the opened socket */
78: extern int trace;
79: extern int verbose;
80: extern int rexmtval;
81: extern int maxtimeout;
1.16 claudio 82: extern FILE *file;
1.17 mglocker 83: extern volatile sig_atomic_t intrflag;
1.16 claudio 84:
1.17 mglocker 85: char ackbuf[PKTSIZE];
1.16 claudio 86: struct timeval tstart;
87: struct timeval tstop;
88:
89: struct errmsg {
1.17 mglocker 90: int e_code;
1.16 claudio 91: char *e_msg;
92: } errmsgs[] = {
93: { EUNDEF, "Undefined error code" },
94: { ENOTFOUND, "File not found" },
95: { EACCESS, "Access violation" },
96: { ENOSPACE, "Disk full or allocation exceeded" },
97: { EBADOP, "Illegal TFTP operation" },
98: { EBADID, "Unknown transfer ID" },
99: { EEXISTS, "File already exists" },
100: { ENOUSER, "No such user" },
101: { -1, NULL }
102: };
103:
1.1 deraadt 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.17 mglocker 110: struct tftphdr *dp, *ap; /* data and ack packets */
111: struct sockaddr_in from;
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 {
1.18 ! mglocker 130: size = readit(file, &dp, convert, SEGSIZE);
1.1 deraadt 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: }
1.18 ! mglocker 155: read_ahead(file, convert, SEGSIZE);
1.16 claudio 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.17 mglocker 233: struct tftphdr *dp, *ap; /* data and ack packets */
234: struct sockaddr_in from;
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;
239: int firsttrip;
1.1 deraadt 240:
1.16 claudio 241: startclock(); /* start stat's clock */
242: dp = w_init(); /* reset fillbuf/read-ahead code */
1.1 deraadt 243: ap = (struct tftphdr *)ackbuf;
244: file = fdopen(fd, "w");
245: convert = !strcmp(mode, "netascii");
1.16 claudio 246: n = 0;
1.1 deraadt 247: block = 1;
1.16 claudio 248: amount = 0;
1.1 deraadt 249: firsttrip = 1;
250:
251: do {
1.16 claudio 252: /* create new ACK packet */
1.1 deraadt 253: if (firsttrip) {
254: size = makerequest(RRQ, name, ap, mode);
255: firsttrip = 0;
256: } else {
257: ap->th_opcode = htons((u_short)ACK);
258: ap->th_block = htons((u_short)(block));
259: size = 4;
260: block++;
261: }
1.16 claudio 262:
263: /* send ACK to server and wait for server data */
264: for (timeouts = 0, error = 0; !intrflag;) {
265: if (timeouts == maxtimeout) {
266: printtimeout();
267: goto abort;
268: }
269:
270: if (!error) {
271: if (trace)
272: tpacket("sent", ap, size);
273: if (sendto(f, ackbuf, size, 0,
274: (struct sockaddr *)&peeraddr,
275: sizeof(peeraddr)) != size) {
276: warn("sendto");
277: goto abort;
278: }
279: write_behind(file, convert);
280: }
281: error = 0;
282:
283: pfd[0].fd = f;
284: pfd[0].events = POLLIN;
285: nfds = poll(pfd, 1, rexmtval * 1000);
286: if (nfds == 0) {
287: timeouts++;
288: continue;
289: }
290: if (nfds == -1) {
291: error = 1;
292: if (errno == EINTR)
293: continue;
294: warn("poll");
295: goto abort;
296: }
297: fromlen = sizeof(from);
298: n = recvfrom(f, dp, PKTSIZE, 0,
299: (struct sockaddr *)&from, &fromlen);
300: if (n == 0) {
301: warn("recvfrom");
302: goto abort;
303: }
304: if (n == -1) {
305: error = 1;
306: if (errno == EINTR)
307: continue;
1.7 mickey 308: warn("recvfrom");
1.1 deraadt 309: goto abort;
310: }
311: peeraddr.sin_port = from.sin_port; /* added */
312: if (trace)
313: tpacket("received", dp, n);
314: dp->th_opcode = ntohs(dp->th_opcode);
315: dp->th_block = ntohs(dp->th_block);
1.16 claudio 316:
1.1 deraadt 317: if (dp->th_opcode == ERROR) {
1.16 claudio 318: printf("Error code %d: %s\n",
319: dp->th_code, dp->th_msg);
1.1 deraadt 320: goto abort;
321: }
322: if (dp->th_opcode == DATA) {
323: int j;
1.16 claudio 324: if (dp->th_block == block)
325: break;
326: /* re-synchronize with other side */
1.1 deraadt 327: j = synchnet(f);
1.15 deraadt 328: if (j && trace)
1.1 deraadt 329: printf("discarded %d packets\n", j);
1.16 claudio 330: if (dp->th_block == (block - 1))
331: continue;
1.1 deraadt 332: }
1.16 claudio 333: error = 1; /* received packet does not match */
1.1 deraadt 334: }
1.16 claudio 335:
336: /* write data to file */
1.1 deraadt 337: size = writeit(file, &dp, n - 4, convert);
338: if (size < 0) {
339: nak(errno + 100);
340: break;
341: }
342: amount += size;
1.16 claudio 343: } while (size == SEGSIZE && !intrflag);
344:
345: abort:
346: /* ok to ack, since user has seen err msg */
347: ap->th_opcode = htons((u_short)ACK);
1.1 deraadt 348: ap->th_block = htons((u_short)block);
1.17 mglocker 349: (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
1.1 deraadt 350: sizeof(peeraddr));
1.16 claudio 351: write_behind(file, convert); /* flush last buffer */
352:
1.1 deraadt 353: fclose(file);
354: stopclock();
1.16 claudio 355: if (amount > 0) {
356: if (intrflag)
357: putchar('\n');
1.1 deraadt 358: printstats("Received", amount);
1.16 claudio 359: }
1.1 deraadt 360: }
361:
362: static int
1.14 deraadt 363: makerequest(int request, const char *name, struct tftphdr *tp,
364: const char *mode)
1.1 deraadt 365: {
1.17 mglocker 366: char *cp;
367: int len, pktlen;
1.1 deraadt 368:
369: tp->th_opcode = htons((u_short)request);
370: cp = tp->th_stuff;
1.11 henning 371: pktlen = PKTSIZE - offsetof(struct tftphdr, th_stuff);
372: len = strlen(name) + 1;
373: strlcpy(cp, name, pktlen);
374: strlcpy(cp + len, mode, pktlen - len);
375: len += strlen(mode) + 1;
1.17 mglocker 376:
1.11 henning 377: return (cp + len - (char *)tp);
1.1 deraadt 378: }
379:
380: /*
381: * Send a nak packet (error message).
382: * Error code passed in is one of the
383: * standard TFTP codes, or a UNIX errno
384: * offset by 100.
385: */
386: static void
1.14 deraadt 387: nak(int error)
1.1 deraadt 388: {
1.17 mglocker 389: struct errmsg *pe;
390: struct tftphdr *tp;
391: int length;
1.1 deraadt 392:
393: tp = (struct tftphdr *)ackbuf;
394: tp->th_opcode = htons((u_short)ERROR);
395: tp->th_code = htons((u_short)error);
396: for (pe = errmsgs; pe->e_code >= 0; pe++)
397: if (pe->e_code == error)
398: break;
399: if (pe->e_code < 0) {
400: pe->e_msg = strerror(error - 100);
401: tp->th_code = EUNDEF;
402: }
1.8 mpech 403: length = strlcpy(tp->th_msg, pe->e_msg, sizeof(ackbuf)) + 5;
404: if (length > sizeof(ackbuf))
405: length = sizeof(ackbuf);
1.1 deraadt 406: if (trace)
407: tpacket("sent", tp, length);
408: if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
409: sizeof(peeraddr)) != length)
1.7 mickey 410: warn("nak");
1.1 deraadt 411: }
412:
413: static void
1.14 deraadt 414: tpacket(const char *s, struct tftphdr *tp, int n)
1.1 deraadt 415: {
1.17 mglocker 416: char *cp, *file;
417: static char *opcodes[] =
1.16 claudio 418: { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
1.17 mglocker 419:
1.1 deraadt 420: u_short op = ntohs(tp->th_opcode);
421:
422: if (op < RRQ || op > ERROR)
423: printf("%s opcode=%x ", s, op);
424: else
425: printf("%s %s ", s, opcodes[op]);
1.16 claudio 426:
1.1 deraadt 427: switch (op) {
428: case RRQ:
429: case WRQ:
430: n -= 2;
431: file = cp = tp->th_stuff;
1.3 millert 432: cp = strchr(cp, '\0');
1.1 deraadt 433: printf("<file=%s, mode=%s>\n", file, cp + 1);
434: break;
435: case DATA:
436: printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
437: break;
438: case ACK:
439: printf("<block=%d>\n", ntohs(tp->th_block));
440: break;
441: case ERROR:
442: printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
443: break;
444: }
445: }
446:
447: static void
1.13 deraadt 448: startclock(void)
1.1 deraadt 449: {
1.17 mglocker 450: (void)gettimeofday(&tstart, NULL);
1.1 deraadt 451: }
452:
453: static void
1.13 deraadt 454: stopclock(void)
1.1 deraadt 455: {
1.17 mglocker 456: (void)gettimeofday(&tstop, NULL);
1.1 deraadt 457: }
458:
459: static void
1.14 deraadt 460: printstats(const char *direction, unsigned long amount)
1.1 deraadt 461: {
1.17 mglocker 462: double delta;
1.15 deraadt 463:
464: /* compute delta in 1/10's second units */
1.16 claudio 465: delta = ((tstop.tv_sec * 10.) + (tstop.tv_usec / 100000)) -
466: ((tstart.tv_sec * 10.) + (tstart.tv_usec / 100000));
467: delta = delta / 10.; /* back to seconds */
1.6 deraadt 468: printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
1.1 deraadt 469: if (verbose)
1.16 claudio 470: printf(" [%.0f bits/sec]", (amount * 8.) / delta);
1.1 deraadt 471: putchar('\n');
472: }
473:
474: static void
1.16 claudio 475: printtimeout(void)
1.1 deraadt 476: {
1.16 claudio 477: printf("Transfer timed out.\n");
1.1 deraadt 478: }