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