tftp.c revision 1.13 1 /* $NetBSD: tftp.c,v 1.13 2000/10/22 01:42:15 dogcow 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 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93";
40 #else
41 __RCSID("$NetBSD: tftp.c,v 1.13 2000/10/22 01:42:15 dogcow Exp $");
42 #endif
43 #endif /* not lint */
44
45 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
46
47 /*
48 * TFTP User Program -- Protocol Machines
49 */
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <sys/time.h>
53
54 #include <netinet/in.h>
55
56 #include <arpa/tftp.h>
57
58 #include <err.h>
59 #include <errno.h>
60 #include <setjmp.h>
61 #include <signal.h>
62 #include <stdio.h>
63 #include <string.h>
64 #include <unistd.h>
65
66 #include "extern.h"
67 #include "tftpsubs.h"
68
69 extern struct sockaddr_storage peeraddr; /* filled in by main */
70 extern int f; /* the opened socket */
71 extern int trace;
72 extern int verbose;
73 extern int rexmtval;
74 extern int maxtimeout;
75
76 #define PKTSIZE SEGSIZE+4
77 char ackbuf[PKTSIZE];
78 int timeout;
79 jmp_buf toplevel;
80 jmp_buf timeoutbuf;
81
82 static void nak __P((int, struct sockaddr *));
83 static int makerequest __P((int, const char *, struct tftphdr *, const char *));
84 static void printstats __P((const char *, unsigned long));
85 static void startclock __P((void));
86 static void stopclock __P((void));
87 static void timer __P((int));
88 static void tpacket __P((const char *, struct tftphdr *, int));
89
90 /*
91 * Send the requested file.
92 */
93 void
94 sendfile(fd, name, mode)
95 int fd;
96 char *name;
97 char *mode;
98 {
99 struct tftphdr *ap; /* data and ack packets */
100 struct tftphdr *dp;
101 int n;
102 volatile unsigned int block;
103 volatile int size, convert;
104 volatile unsigned long amount;
105 struct sockaddr_storage from;
106 int fromlen;
107 FILE *file;
108 struct sockaddr_storage peer;
109
110 startclock(); /* start stat's clock */
111 dp = r_init(); /* reset fillbuf/read-ahead code */
112 ap = (struct tftphdr *)ackbuf;
113 file = fdopen(fd, "r");
114 convert = !strcmp(mode, "netascii");
115 block = 0;
116 amount = 0;
117 memcpy(&peer, &peeraddr, peeraddr.ss_len);
118
119 signal(SIGALRM, timer);
120 do {
121 if (block == 0)
122 size = makerequest(WRQ, name, dp, mode) - 4;
123 else {
124 /* size = read(fd, dp->th_data, SEGSIZE); */
125 size = readit(file, &dp, convert);
126 if (size < 0) {
127 nak(errno + 100, (struct sockaddr *)&peer);
128 break;
129 }
130 dp->th_opcode = htons((u_short)DATA);
131 dp->th_block = htons((u_short)block);
132 }
133 timeout = 0;
134 (void) setjmp(timeoutbuf);
135 send_data:
136 if (trace)
137 tpacket("sent", dp, size + 4);
138 n = sendto(f, dp, size + 4, 0,
139 (struct sockaddr *)&peer, peer.ss_len);
140 if (n != size + 4) {
141 warn("sendto");
142 goto abort;
143 }
144 read_ahead(file, convert);
145 for ( ; ; ) {
146 alarm(rexmtval);
147 do {
148 fromlen = sizeof(from);
149 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
150 (struct sockaddr *)&from, &fromlen);
151 } while (n <= 0);
152 alarm(0);
153 if (n < 0) {
154 warn("recvfrom");
155 goto abort;
156 }
157 switch (peer.ss_family) { /* added */
158 case AF_INET:
159 ((struct sockaddr_in *)&peer)->sin_port =
160 ((struct sockaddr_in *)&from)->sin_port;
161 break;
162 case AF_INET6:
163 ((struct sockaddr_in6 *)&peer)->sin6_port =
164 ((struct sockaddr_in6 *)&from)->sin6_port;
165 break;
166 default:
167 /* unsupported */
168 break;
169 }
170 if (trace)
171 tpacket("received", ap, n);
172 /* should verify packet came from server */
173 ap->th_opcode = ntohs(ap->th_opcode);
174 ap->th_block = ntohs(ap->th_block);
175 if (ap->th_opcode == ERROR) {
176 printf("Error code %d: %s\n", ap->th_code,
177 ap->th_msg);
178 goto abort;
179 }
180 if (ap->th_opcode == ACK) {
181 int j;
182
183 if (ap->th_block == block) {
184 break;
185 }
186 /* On an error, try to synchronize
187 * both sides.
188 */
189 j = synchnet(f);
190 if (j && trace) {
191 printf("discarded %d packets\n",
192 j);
193 }
194 if (ap->th_block == (block-1)) {
195 goto send_data;
196 }
197 }
198 }
199 if (block > 0)
200 amount += size;
201 block++;
202 } while (size == SEGSIZE || block == 1);
203 abort:
204 fclose(file);
205 stopclock();
206 if (amount > 0)
207 printstats("Sent", amount);
208 }
209
210 /*
211 * Receive a file.
212 */
213 void
214 recvfile(fd, name, mode)
215 int fd;
216 char *name;
217 char *mode;
218 {
219 struct tftphdr *ap;
220 struct tftphdr *dp;
221 int n;
222 volatile unsigned int block;
223 volatile int size, firsttrip;
224 volatile unsigned long amount;
225 struct sockaddr_storage from;
226 int fromlen;
227 FILE *file;
228 volatile int convert; /* true if converting crlf -> lf */
229 struct sockaddr_storage peer;
230
231 startclock();
232 dp = w_init();
233 ap = (struct tftphdr *)ackbuf;
234 file = fdopen(fd, "w");
235 convert = !strcmp(mode, "netascii");
236 block = 1;
237 firsttrip = 1;
238 amount = 0;
239 memcpy(&peer, &peeraddr, peeraddr.ss_len);
240
241 signal(SIGALRM, timer);
242 do {
243 if (firsttrip) {
244 size = makerequest(RRQ, name, ap, mode);
245 firsttrip = 0;
246 } else {
247 ap->th_opcode = htons((u_short)ACK);
248 ap->th_block = htons((u_short)(block));
249 size = 4;
250 block++;
251 }
252 timeout = 0;
253 (void) setjmp(timeoutbuf);
254 send_ack:
255 if (trace)
256 tpacket("sent", ap, size);
257 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peer,
258 peer.ss_len) != size) {
259 alarm(0);
260 warn("sendto");
261 goto abort;
262 }
263 write_behind(file, convert);
264 for ( ; ; ) {
265 alarm(rexmtval);
266 do {
267 fromlen = sizeof(from);
268 n = recvfrom(f, dp, PKTSIZE, 0,
269 (struct sockaddr *)&from, &fromlen);
270 } while (n <= 0);
271 alarm(0);
272 if (n < 0) {
273 warn("recvfrom");
274 goto abort;
275 }
276 switch (peer.ss_family) { /* added */
277 case AF_INET:
278 ((struct sockaddr_in *)&peer)->sin_port =
279 ((struct sockaddr_in *)&from)->sin_port;
280 break;
281 case AF_INET6:
282 ((struct sockaddr_in6 *)&peer)->sin6_port =
283 ((struct sockaddr_in6 *)&from)->sin6_port;
284 break;
285 default:
286 /* unsupported */
287 break;
288 }
289 if (trace)
290 tpacket("received", dp, n);
291 /* should verify client address */
292 dp->th_opcode = ntohs(dp->th_opcode);
293 dp->th_block = ntohs(dp->th_block);
294 if (dp->th_opcode == ERROR) {
295 printf("Error code %d: %s\n", dp->th_code,
296 dp->th_msg);
297 goto abort;
298 }
299 if (dp->th_opcode == DATA) {
300 int j;
301
302 if (dp->th_block == block) {
303 break; /* have next packet */
304 }
305 /* On an error, try to synchronize
306 * both sides.
307 */
308 j = synchnet(f);
309 if (j && trace) {
310 printf("discarded %d packets\n", j);
311 }
312 if (dp->th_block == (block-1)) {
313 goto send_ack; /* resend ack */
314 }
315 }
316 }
317 /* size = write(fd, dp->th_data, n - 4); */
318 size = writeit(file, &dp, n - 4, convert);
319 if (size < 0) {
320 nak(errno + 100, (struct sockaddr *)&peer);
321 break;
322 }
323 amount += size;
324 } while (size == SEGSIZE);
325 abort: /* ok to ack, since user */
326 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
327 ap->th_block = htons((u_short)block);
328 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer,
329 peer.ss_len);
330 write_behind(file, convert); /* flush last buffer */
331 fclose(file);
332 stopclock();
333 if (amount > 0)
334 printstats("Received", amount);
335 }
336
337 static int
338 makerequest(request, name, tp, mode)
339 int request;
340 const char *name;
341 struct tftphdr *tp;
342 const char *mode;
343 {
344 char *cp;
345
346 tp->th_opcode = htons((u_short)request);
347 #ifndef __SVR4
348 cp = tp->th_stuff;
349 #else
350 cp = (void *)&tp->th_stuff;
351 #endif
352 strcpy(cp, name);
353 cp += strlen(name);
354 *cp++ = '\0';
355 strcpy(cp, mode);
356 cp += strlen(mode);
357 *cp++ = '\0';
358 return (cp - (char *)tp);
359 }
360
361 const struct errmsg {
362 int e_code;
363 const char *e_msg;
364 } errmsgs[] = {
365 { EUNDEF, "Undefined error code" },
366 { ENOTFOUND, "File not found" },
367 { EACCESS, "Access violation" },
368 { ENOSPACE, "Disk full or allocation exceeded" },
369 { EBADOP, "Illegal TFTP operation" },
370 { EBADID, "Unknown transfer ID" },
371 { EEXISTS, "File already exists" },
372 { ENOUSER, "No such user" },
373 { -1, 0 }
374 };
375
376 /*
377 * Send a nak packet (error message).
378 * Error code passed in is one of the
379 * standard TFTP codes, or a UNIX errno
380 * offset by 100.
381 */
382 static void
383 nak(error, peer)
384 int error;
385 struct sockaddr *peer;
386 {
387 const struct errmsg *pe;
388 struct tftphdr *tp;
389 int length;
390
391 tp = (struct tftphdr *)ackbuf;
392 tp->th_opcode = htons((u_short)ERROR);
393 for (pe = errmsgs; pe->e_code >= 0; pe++)
394 if (pe->e_code == error)
395 break;
396 if (pe->e_code < 0) {
397 tp->th_code = EUNDEF;
398 strcpy(tp->th_msg, strerror(error - 100));
399 } else {
400 tp->th_code = htons((u_short)error);
401 strcpy(tp->th_msg, pe->e_msg);
402 }
403 length = strlen(pe->e_msg) + 4;
404 if (trace)
405 tpacket("sent", tp, length);
406 if (sendto(f, ackbuf, length, 0, peer, peer->sa_len) != length)
407 warn("nak");
408 }
409
410 static void
411 tpacket(s, tp, n)
412 const char *s;
413 struct tftphdr *tp;
414 int n;
415 {
416 static char *opcodes[] =
417 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
418 char *cp, *file;
419 u_short op = ntohs(tp->th_opcode);
420
421 if (op < RRQ || op > ERROR)
422 printf("%s opcode=%x ", s, op);
423 else
424 printf("%s %s ", s, opcodes[op]);
425 switch (op) {
426
427 case RRQ:
428 case WRQ:
429 n -= 2;
430 #ifndef __SVR4
431 cp = tp->th_stuff;
432 #else
433 cp = (void *) &tp->th_stuff;
434 #endif
435 file = cp;
436 cp = strchr(cp, '\0');
437 printf("<file=%s, mode=%s>\n", file, cp + 1);
438 break;
439
440 case DATA:
441 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
442 break;
443
444 case ACK:
445 printf("<block=%d>\n", ntohs(tp->th_block));
446 break;
447
448 case ERROR:
449 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
450 break;
451 }
452 }
453
454 struct timeval tstart;
455 struct timeval tstop;
456
457 static void
458 startclock()
459 {
460
461 (void)gettimeofday(&tstart, NULL);
462 }
463
464 static void
465 stopclock()
466 {
467
468 (void)gettimeofday(&tstop, NULL);
469 }
470
471 static void
472 printstats(direction, amount)
473 const char *direction;
474 unsigned long amount;
475 {
476 double delta;
477
478 /* compute delta in 1/10's second units */
479 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
480 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
481 delta = delta/10.; /* back to seconds */
482 printf("%s %ld bytes in %.1f seconds", direction, amount, delta);
483 if (verbose)
484 printf(" [%.0f bits/sec]", (amount*8.)/delta);
485 putchar('\n');
486 }
487
488 static void
489 timer(sig)
490 int sig;
491 {
492
493 timeout += rexmtval;
494 if (timeout >= maxtimeout) {
495 printf("Transfer timed out.\n");
496 longjmp(toplevel, -1);
497 }
498 longjmp(timeoutbuf, 1);
499 }
500