tftp.c revision 1.6 1 /* $NetBSD: tftp.c,v 1.6 1997/10/07 09:19:43 mrg 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.6 1997/10/07 09:19:43 mrg 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 <errno.h>
59 #include <setjmp.h>
60 #include <signal.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <unistd.h>
64
65 #include "extern.h"
66 #include "tftpsubs.h"
67
68 extern int errno;
69
70 extern struct sockaddr_in peeraddr; /* filled in by main */
71 extern int f; /* the opened socket */
72 extern int trace;
73 extern int verbose;
74 extern int rexmtval;
75 extern int maxtimeout;
76
77 #define PKTSIZE SEGSIZE+4
78 char ackbuf[PKTSIZE];
79 int timeout;
80 jmp_buf toplevel;
81 jmp_buf timeoutbuf;
82
83 static void nak __P((int));
84 static int makerequest __P((int, const char *, struct tftphdr *, const char *));
85 static void printstats __P((const char *, unsigned long));
86 static void startclock __P((void));
87 static void stopclock __P((void));
88 static void timer __P((int));
89 static void tpacket __P((const char *, struct tftphdr *, int));
90
91 /*
92 * Send the requested file.
93 */
94 void
95 sendfile(fd, name, mode)
96 int fd;
97 char *name;
98 char *mode;
99 {
100 register struct tftphdr *ap; /* data and ack packets */
101 struct tftphdr *dp;
102 register int n;
103 volatile int block, size, convert;
104 volatile unsigned long amount;
105 struct sockaddr_in from;
106 int fromlen;
107 FILE *file;
108
109 startclock(); /* start stat's clock */
110 dp = r_init(); /* reset fillbuf/read-ahead code */
111 ap = (struct tftphdr *)ackbuf;
112 file = fdopen(fd, "r");
113 convert = !strcmp(mode, "netascii");
114 block = 0;
115 amount = 0;
116
117 signal(SIGALRM, timer);
118 do {
119 if (block == 0)
120 size = makerequest(WRQ, name, dp, mode) - 4;
121 else {
122 /* size = read(fd, dp->th_data, SEGSIZE); */
123 size = readit(file, &dp, convert);
124 if (size < 0) {
125 nak(errno + 100);
126 break;
127 }
128 dp->th_opcode = htons((u_short)DATA);
129 dp->th_block = htons((u_short)block);
130 }
131 timeout = 0;
132 (void) setjmp(timeoutbuf);
133 send_data:
134 if (trace)
135 tpacket("sent", dp, size + 4);
136 n = sendto(f, dp, size + 4, 0,
137 (struct sockaddr *)&peeraddr, sizeof(peeraddr));
138 if (n != size + 4) {
139 perror("tftp: sendto");
140 goto abort;
141 }
142 read_ahead(file, convert);
143 for ( ; ; ) {
144 alarm(rexmtval);
145 do {
146 fromlen = sizeof(from);
147 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
148 (struct sockaddr *)&from, &fromlen);
149 } while (n <= 0);
150 alarm(0);
151 if (n < 0) {
152 perror("tftp: recvfrom");
153 goto abort;
154 }
155 peeraddr.sin_port = from.sin_port; /* added */
156 if (trace)
157 tpacket("received", ap, n);
158 /* should verify packet came from server */
159 ap->th_opcode = ntohs(ap->th_opcode);
160 ap->th_block = ntohs(ap->th_block);
161 if (ap->th_opcode == ERROR) {
162 printf("Error code %d: %s\n", ap->th_code,
163 ap->th_msg);
164 goto abort;
165 }
166 if (ap->th_opcode == ACK) {
167 int j;
168
169 if (ap->th_block == block) {
170 break;
171 }
172 /* On an error, try to synchronize
173 * both sides.
174 */
175 j = synchnet(f);
176 if (j && trace) {
177 printf("discarded %d packets\n",
178 j);
179 }
180 if (ap->th_block == (block-1)) {
181 goto send_data;
182 }
183 }
184 }
185 if (block > 0)
186 amount += size;
187 block++;
188 } while (size == SEGSIZE || block == 1);
189 abort:
190 fclose(file);
191 stopclock();
192 if (amount > 0)
193 printstats("Sent", amount);
194 }
195
196 /*
197 * Receive a file.
198 */
199 void
200 recvfile(fd, name, mode)
201 int fd;
202 char *name;
203 char *mode;
204 {
205 register struct tftphdr *ap;
206 struct tftphdr *dp;
207 register int n;
208 volatile int block, size, firsttrip;
209 volatile unsigned long amount;
210 struct sockaddr_in from;
211 int fromlen;
212 FILE *file;
213 volatile int convert; /* true if converting crlf -> lf */
214
215 startclock();
216 dp = w_init();
217 ap = (struct tftphdr *)ackbuf;
218 file = fdopen(fd, "w");
219 convert = !strcmp(mode, "netascii");
220 block = 1;
221 firsttrip = 1;
222 amount = 0;
223
224 signal(SIGALRM, timer);
225 do {
226 if (firsttrip) {
227 size = makerequest(RRQ, name, ap, mode);
228 firsttrip = 0;
229 } else {
230 ap->th_opcode = htons((u_short)ACK);
231 ap->th_block = htons((u_short)(block));
232 size = 4;
233 block++;
234 }
235 timeout = 0;
236 (void) setjmp(timeoutbuf);
237 send_ack:
238 if (trace)
239 tpacket("sent", ap, size);
240 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
241 sizeof(peeraddr)) != size) {
242 alarm(0);
243 perror("tftp: sendto");
244 goto abort;
245 }
246 write_behind(file, convert);
247 for ( ; ; ) {
248 alarm(rexmtval);
249 do {
250 fromlen = sizeof(from);
251 n = recvfrom(f, dp, PKTSIZE, 0,
252 (struct sockaddr *)&from, &fromlen);
253 } while (n <= 0);
254 alarm(0);
255 if (n < 0) {
256 perror("tftp: recvfrom");
257 goto abort;
258 }
259 peeraddr.sin_port = from.sin_port; /* added */
260 if (trace)
261 tpacket("received", dp, n);
262 /* should verify client address */
263 dp->th_opcode = ntohs(dp->th_opcode);
264 dp->th_block = ntohs(dp->th_block);
265 if (dp->th_opcode == ERROR) {
266 printf("Error code %d: %s\n", dp->th_code,
267 dp->th_msg);
268 goto abort;
269 }
270 if (dp->th_opcode == DATA) {
271 int j;
272
273 if (dp->th_block == block) {
274 break; /* have next packet */
275 }
276 /* On an error, try to synchronize
277 * both sides.
278 */
279 j = synchnet(f);
280 if (j && trace) {
281 printf("discarded %d packets\n", j);
282 }
283 if (dp->th_block == (block-1)) {
284 goto send_ack; /* resend ack */
285 }
286 }
287 }
288 /* size = write(fd, dp->th_data, n - 4); */
289 size = writeit(file, &dp, n - 4, convert);
290 if (size < 0) {
291 nak(errno + 100);
292 break;
293 }
294 amount += size;
295 } while (size == SEGSIZE);
296 abort: /* ok to ack, since user */
297 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
298 ap->th_block = htons((u_short)block);
299 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
300 sizeof(peeraddr));
301 write_behind(file, convert); /* flush last buffer */
302 fclose(file);
303 stopclock();
304 if (amount > 0)
305 printstats("Received", amount);
306 }
307
308 static int
309 makerequest(request, name, tp, mode)
310 int request;
311 const char *name;
312 struct tftphdr *tp;
313 const char *mode;
314 {
315 register char *cp;
316
317 tp->th_opcode = htons((u_short)request);
318 cp = tp->th_stuff;
319 strcpy(cp, name);
320 cp += strlen(name);
321 *cp++ = '\0';
322 strcpy(cp, mode);
323 cp += strlen(mode);
324 *cp++ = '\0';
325 return (cp - (char *)tp);
326 }
327
328 struct errmsg {
329 int e_code;
330 char *e_msg;
331 } errmsgs[] = {
332 { EUNDEF, "Undefined error code" },
333 { ENOTFOUND, "File not found" },
334 { EACCESS, "Access violation" },
335 { ENOSPACE, "Disk full or allocation exceeded" },
336 { EBADOP, "Illegal TFTP operation" },
337 { EBADID, "Unknown transfer ID" },
338 { EEXISTS, "File already exists" },
339 { ENOUSER, "No such user" },
340 { -1, 0 }
341 };
342
343 /*
344 * Send a nak packet (error message).
345 * Error code passed in is one of the
346 * standard TFTP codes, or a UNIX errno
347 * offset by 100.
348 */
349 static void
350 nak(error)
351 int error;
352 {
353 register struct errmsg *pe;
354 register struct tftphdr *tp;
355 int length;
356
357 tp = (struct tftphdr *)ackbuf;
358 tp->th_opcode = htons((u_short)ERROR);
359 tp->th_code = htons((u_short)error);
360 for (pe = errmsgs; pe->e_code >= 0; pe++)
361 if (pe->e_code == error)
362 break;
363 if (pe->e_code < 0) {
364 pe->e_msg = strerror(error - 100);
365 tp->th_code = EUNDEF;
366 }
367 strcpy(tp->th_msg, pe->e_msg);
368 length = strlen(pe->e_msg) + 4;
369 if (trace)
370 tpacket("sent", tp, length);
371 if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
372 sizeof(peeraddr)) != length)
373 perror("nak");
374 }
375
376 static void
377 tpacket(s, tp, n)
378 const char *s;
379 struct tftphdr *tp;
380 int n;
381 {
382 static char *opcodes[] =
383 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
384 register char *cp, *file;
385 u_short op = ntohs(tp->th_opcode);
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
439 /* compute delta in 1/10's second units */
440 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
441 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
442 delta = delta/10.; /* back to seconds */
443 printf("%s %ld bytes in %.1f seconds", direction, amount, delta);
444 if (verbose)
445 printf(" [%.0f bits/sec]", (amount*8.)/delta);
446 putchar('\n');
447 }
448
449 static void
450 timer(sig)
451 int sig;
452 {
453
454 timeout += rexmtval;
455 if (timeout >= maxtimeout) {
456 printf("Transfer timed out.\n");
457 longjmp(toplevel, -1);
458 }
459 longjmp(timeoutbuf, 1);
460 }
461