tftp.c revision 1.10 1 /* $NetBSD: tftp.c,v 1.10 1999/07/12 20:19:20 itojun 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.10 1999/07/12 20:19:20 itojun 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 int errno;
70
71 extern struct sockaddr_storage peeraddr; /* filled in by main */
72 extern int f; /* the opened socket */
73 extern int trace;
74 extern int verbose;
75 extern int rexmtval;
76 extern int maxtimeout;
77
78 #define PKTSIZE SEGSIZE+4
79 char ackbuf[PKTSIZE];
80 int timeout;
81 jmp_buf toplevel;
82 jmp_buf timeoutbuf;
83
84 static void nak __P((int, struct sockaddr *));
85 static int makerequest __P((int, const char *, struct tftphdr *, const char *));
86 static void printstats __P((const char *, unsigned long));
87 static void startclock __P((void));
88 static void stopclock __P((void));
89 static void timer __P((int));
90 static void tpacket __P((const char *, struct tftphdr *, int));
91
92 /*
93 * Send the requested file.
94 */
95 void
96 sendfile(fd, name, mode)
97 int fd;
98 char *name;
99 char *mode;
100 {
101 struct tftphdr *ap; /* data and ack packets */
102 struct tftphdr *dp;
103 int n;
104 volatile int block, size, convert;
105 volatile unsigned long amount;
106 struct sockaddr_storage from;
107 int fromlen;
108 FILE *file;
109 struct sockaddr_storage peer;
110
111 startclock(); /* start stat's clock */
112 dp = r_init(); /* reset fillbuf/read-ahead code */
113 ap = (struct tftphdr *)ackbuf;
114 file = fdopen(fd, "r");
115 convert = !strcmp(mode, "netascii");
116 block = 0;
117 amount = 0;
118 memcpy(&peer, &peeraddr, peeraddr.ss_len);
119
120 signal(SIGALRM, timer);
121 do {
122 if (block == 0)
123 size = makerequest(WRQ, name, dp, mode) - 4;
124 else {
125 /* size = read(fd, dp->th_data, SEGSIZE); */
126 size = readit(file, &dp, convert);
127 if (size < 0) {
128 nak(errno + 100, (struct sockaddr *)&peer);
129 break;
130 }
131 dp->th_opcode = htons((u_short)DATA);
132 dp->th_block = htons((u_short)block);
133 }
134 timeout = 0;
135 (void) setjmp(timeoutbuf);
136 send_data:
137 if (trace)
138 tpacket("sent", dp, size + 4);
139 n = sendto(f, dp, size + 4, 0,
140 (struct sockaddr *)&peer, peer.ss_len);
141 if (n != size + 4) {
142 warn("sendto");
143 goto abort;
144 }
145 read_ahead(file, convert);
146 for ( ; ; ) {
147 alarm(rexmtval);
148 do {
149 fromlen = sizeof(from);
150 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
151 (struct sockaddr *)&from, &fromlen);
152 } while (n <= 0);
153 alarm(0);
154 if (n < 0) {
155 warn("recvfrom");
156 goto abort;
157 }
158 switch (peer.ss_family) { /* added */
159 case AF_INET:
160 ((struct sockaddr_in *)&peer)->sin_port =
161 ((struct sockaddr_in *)&from)->sin_port;
162 break;
163 case AF_INET6:
164 ((struct sockaddr_in6 *)&peer)->sin6_port =
165 ((struct sockaddr_in6 *)&from)->sin6_port;
166 break;
167 default:
168 /* unsupported */
169 break;
170 }
171 if (trace)
172 tpacket("received", ap, n);
173 /* should verify packet came from server */
174 ap->th_opcode = ntohs(ap->th_opcode);
175 ap->th_block = ntohs(ap->th_block);
176 if (ap->th_opcode == ERROR) {
177 printf("Error code %d: %s\n", ap->th_code,
178 ap->th_msg);
179 goto abort;
180 }
181 if (ap->th_opcode == ACK) {
182 int j;
183
184 if (ap->th_block == block) {
185 break;
186 }
187 /* On an error, try to synchronize
188 * both sides.
189 */
190 j = synchnet(f);
191 if (j && trace) {
192 printf("discarded %d packets\n",
193 j);
194 }
195 if (ap->th_block == (block-1)) {
196 goto send_data;
197 }
198 }
199 }
200 if (block > 0)
201 amount += size;
202 block++;
203 } while (size == SEGSIZE || block == 1);
204 abort:
205 fclose(file);
206 stopclock();
207 if (amount > 0)
208 printstats("Sent", amount);
209 }
210
211 /*
212 * Receive a file.
213 */
214 void
215 recvfile(fd, name, mode)
216 int fd;
217 char *name;
218 char *mode;
219 {
220 struct tftphdr *ap;
221 struct tftphdr *dp;
222 int n;
223 volatile int block, 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