Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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.2 2003/06/17 04:29:32 dillon 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 __P((int, struct sockaddr *));
78 static int makerequest __P((int, const char *, struct tftphdr *, const char *));
79 static void printstats __P((const char *, unsigned long));
80 static void startclock __P((void));
81 static void stopclock __P((void));
82 static void timer __P((int));
83 static void tpacket __P((const char *, struct tftphdr *, int));
84 static int cmpport __P((struct sockaddr *, struct sockaddr *));
85
86 /*
87  * Send the requested file.
88  */
89 void
90 xmitfile(fd, name, mode)
91         int fd;
92         char *name;
93         char *mode;
94 {
95         register struct tftphdr *ap;       /* data and ack packets */
96         struct tftphdr *r_init(), *dp;
97         register int n;
98         volatile unsigned short block;
99         volatile int size, convert;
100         volatile unsigned long amount;
101         struct sockaddr_storage from;
102         int fromlen;
103         FILE *file;
104         struct sockaddr_storage peer;
105         struct sockaddr_storage serv;   /* valid server port number */
106
107         startclock();           /* start stat's clock */
108         dp = r_init();          /* reset fillbuf/read-ahead code */
109         ap = (struct tftphdr *)ackbuf;
110         file = fdopen(fd, "r");
111         convert = !strcmp(mode, "netascii");
112         block = 0;
113         amount = 0;
114         memcpy(&peer, &peeraddr, peeraddr.ss_len);
115         memset(&serv, 0, sizeof(serv));
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, (struct sockaddr *)&peer);
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 *)&peer, peer.ss_len);
138                 if (n != size + 4) {
139                         warn("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                                 warn("recvfrom");
153                                 goto abort;
154                         }
155                         if (!serv.ss_family)
156                                 serv = from;
157                         else if (!cmpport((struct sockaddr *)&serv,
158                             (struct sockaddr *)&from)) {
159                                 warn("server port mismatch");
160                                 goto abort;
161                         }
162                         peer = from;
163                         if (trace)
164                                 tpacket("received", ap, n);
165                         /* should verify packet came from server */
166                         ap->th_opcode = ntohs(ap->th_opcode);
167                         ap->th_block = ntohs(ap->th_block);
168                         if (ap->th_opcode == ERROR) {
169                                 printf("Error code %d: %s\n", ap->th_code,
170                                         ap->th_msg);
171                                 txrx_error = 1;
172                                 goto abort;
173                         }
174                         if (ap->th_opcode == ACK) {
175                                 int j;
176
177                                 if (ap->th_block == block) {
178                                         break;
179                                 }
180                                 /* On an error, try to synchronize
181                                  * both sides.
182                                  */
183                                 j = synchnet(f);
184                                 if (j && trace) {
185                                         printf("discarded %d packets\n",
186                                                         j);
187                                 }
188                                 if (ap->th_block == (block-1)) {
189                                         goto send_data;
190                                 }
191                         }
192                 }
193                 if (block > 0)
194                         amount += size;
195                 block++;
196         } while (size == SEGSIZE || block == 1);
197 abort:
198         fclose(file);
199         stopclock();
200         if (amount > 0)
201                 printstats("Sent", amount);
202 }
203
204 /*
205  * Receive a file.
206  */
207 void
208 recvfile(fd, name, mode)
209         int fd;
210         char *name;
211         char *mode;
212 {
213         register struct tftphdr *ap;
214         struct tftphdr *dp, *w_init();
215         register int n;
216         volatile unsigned short block;
217         volatile int size, firsttrip;
218         volatile unsigned long amount;
219         struct sockaddr_storage from;
220         int fromlen;
221         FILE *file;
222         volatile int convert;           /* true if converting crlf -> lf */
223         struct sockaddr_storage peer;
224         struct sockaddr_storage serv;   /* valid server port number */
225
226         startclock();
227         dp = w_init();
228         ap = (struct tftphdr *)ackbuf;
229         file = fdopen(fd, "w");
230         convert = !strcmp(mode, "netascii");
231         block = 1;
232         firsttrip = 1;
233         amount = 0;
234         memcpy(&peer, &peeraddr, peeraddr.ss_len);
235         memset(&serv, 0, sizeof(serv));
236
237         signal(SIGALRM, timer);
238         do {
239                 if (firsttrip) {
240                         size = makerequest(RRQ, name, ap, mode);
241                         firsttrip = 0;
242                 } else {
243                         ap->th_opcode = htons((u_short)ACK);
244                         ap->th_block = htons((u_short)(block));
245                         size = 4;
246                         block++;
247                 }
248                 timeout = 0;
249                 (void) setjmp(timeoutbuf);
250 send_ack:
251                 if (trace)
252                         tpacket("sent", ap, size);
253                 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peer,
254                     peer.ss_len) != size) {
255                         alarm(0);
256                         warn("sendto");
257                         goto abort;
258                 }
259                 write_behind(file, convert);
260                 for ( ; ; ) {
261                         alarm(rexmtval);
262                         do  {
263                                 fromlen = sizeof(from);
264                                 n = recvfrom(f, dp, PKTSIZE, 0,
265                                     (struct sockaddr *)&from, &fromlen);
266                         } while (n <= 0);
267                         alarm(0);
268                         if (n < 0) {
269                                 warn("recvfrom");
270                                 goto abort;
271                         }
272                         if (!serv.ss_family)
273                                 serv = from;
274                         else if (!cmpport((struct sockaddr *)&serv,
275                             (struct sockaddr *)&from)) {
276                                 warn("server port mismatch");
277                                 goto abort;
278                         }
279                         peer = from;
280                         if (trace)
281                                 tpacket("received", dp, n);
282                         /* should verify client address */
283                         dp->th_opcode = ntohs(dp->th_opcode);
284                         dp->th_block = ntohs(dp->th_block);
285                         if (dp->th_opcode == ERROR) {
286                                 printf("Error code %d: %s\n", dp->th_code,
287                                         dp->th_msg);
288                                 txrx_error = 1;
289                                 goto abort;
290                         }
291                         if (dp->th_opcode == DATA) {
292                                 int j;
293
294                                 if (dp->th_block == block) {
295                                         break;          /* have next packet */
296                                 }
297                                 /* On an error, try to synchronize
298                                  * both sides.
299                                  */
300                                 j = synchnet(f);
301                                 if (j && trace) {
302                                         printf("discarded %d packets\n", j);
303                                 }
304                                 if (dp->th_block == (block-1)) {
305                                         goto send_ack;  /* resend ack */
306                                 }
307                         }
308                 }
309         /*      size = write(fd, dp->th_data, n - 4); */
310                 size = writeit(file, &dp, n - 4, convert);
311                 if (size < 0) {
312                         nak(errno + 100, (struct sockaddr *)&peer);
313                         break;
314                 }
315                 amount += size;
316         } while (size == SEGSIZE);
317 abort:                                          /* ok to ack, since user */
318         ap->th_opcode = htons((u_short)ACK);    /* has seen err msg */
319         ap->th_block = htons((u_short)block);
320         (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer,
321             peer.ss_len);
322         write_behind(file, convert);            /* flush last buffer */
323         fclose(file);
324         stopclock();
325         if (amount > 0)
326                 printstats("Received", amount);
327 }
328
329 static int
330 makerequest(request, name, tp, mode)
331         int request;
332         const char *name;
333         struct tftphdr *tp;
334         const char *mode;
335 {
336         register char *cp;
337
338         tp->th_opcode = htons((u_short)request);
339         cp = tp->th_stuff;
340         strcpy(cp, name);
341         cp += strlen(name);
342         *cp++ = '\0';
343         strcpy(cp, mode);
344         cp += strlen(mode);
345         *cp++ = '\0';
346         return (cp - (char *)tp);
347 }
348
349 struct errmsg {
350         int     e_code;
351         char    *e_msg;
352 } errmsgs[] = {
353         { EUNDEF,       "Undefined error code" },
354         { ENOTFOUND,    "File not found" },
355         { EACCESS,      "Access violation" },
356         { ENOSPACE,     "Disk full or allocation exceeded" },
357         { EBADOP,       "Illegal TFTP operation" },
358         { EBADID,       "Unknown transfer ID" },
359         { EEXISTS,      "File already exists" },
360         { ENOUSER,      "No such user" },
361         { -1,           0 }
362 };
363
364 /*
365  * Send a nak packet (error message).
366  * Error code passed in is one of the
367  * standard TFTP codes, or a UNIX errno
368  * offset by 100.
369  */
370 static void
371 nak(error, peer)
372         int error;
373         struct sockaddr *peer;
374 {
375         register struct errmsg *pe;
376         register struct tftphdr *tp;
377         int length;
378         char *strerror();
379
380         tp = (struct tftphdr *)ackbuf;
381         tp->th_opcode = htons((u_short)ERROR);
382         tp->th_code = htons((u_short)error);
383         for (pe = errmsgs; pe->e_code >= 0; pe++)
384                 if (pe->e_code == error)
385                         break;
386         if (pe->e_code < 0) {
387                 pe->e_msg = strerror(error - 100);
388                 tp->th_code = EUNDEF;
389         }
390         strcpy(tp->th_msg, pe->e_msg);
391         length = strlen(pe->e_msg) + 4;
392         if (trace)
393                 tpacket("sent", tp, length);
394         if (sendto(f, ackbuf, length, 0, peer, peer->sa_len) != length)
395                 warn("nak");
396 }
397
398 static void
399 tpacket(s, tp, n)
400         const char *s;
401         struct tftphdr *tp;
402         int n;
403 {
404         static char *opcodes[] =
405            { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
406         register char *cp, *file;
407         u_short op = ntohs(tp->th_opcode);
408         char *index();
409
410         if (op < RRQ || op > ERROR)
411                 printf("%s opcode=%x ", s, op);
412         else
413                 printf("%s %s ", s, opcodes[op]);
414         switch (op) {
415
416         case RRQ:
417         case WRQ:
418                 n -= 2;
419                 file = cp = tp->th_stuff;
420                 cp = index(cp, '\0');
421                 printf("<file=%s, mode=%s>\n", file, cp + 1);
422                 break;
423
424         case DATA:
425                 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
426                 break;
427
428         case ACK:
429                 printf("<block=%d>\n", ntohs(tp->th_block));
430                 break;
431
432         case ERROR:
433                 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
434                 break;
435         }
436 }
437
438 struct timeval tstart;
439 struct timeval tstop;
440
441 static void
442 startclock()
443 {
444
445         (void)gettimeofday(&tstart, NULL);
446 }
447
448 static void
449 stopclock()
450 {
451
452         (void)gettimeofday(&tstop, NULL);
453 }
454
455 static void
456 printstats(direction, amount)
457         const char *direction;
458         unsigned long amount;
459 {
460         double delta;
461                         /* compute delta in 1/10's second units */
462         delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
463                 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
464         delta = delta/10.;      /* back to seconds */
465         printf("%s %d bytes in %.1f seconds", direction, amount, delta);
466         if (verbose)
467                 printf(" [%.0f bits/sec]", (amount*8.)/delta);
468         putchar('\n');
469 }
470
471 static void
472 timer(sig)
473         int sig;
474 {
475
476         timeout += rexmtval;
477         if (timeout >= maxtimeout) {
478                 printf("Transfer timed out.\n");
479                 longjmp(toplevel, -1);
480         }
481         txrx_error = 1;
482         longjmp(timeoutbuf, 1);
483 }
484
485 static int
486 cmpport(sa, sb)
487         struct sockaddr *sa;
488         struct sockaddr *sb;
489 {
490         char a[NI_MAXSERV], b[NI_MAXSERV];
491
492         if (getnameinfo(sa, sa->sa_len, NULL, 0, a, sizeof(a), NI_NUMERICSERV))
493                 return 0;
494         if (getnameinfo(sb, sb->sa_len, NULL, 0, b, sizeof(b), NI_NUMERICSERV))
495                 return 0;
496         if (strcmp(a, b) != 0)
497                 return 0;
498
499         return 1;
500 }