K&R style function removal. Update functions to ANSI style.
[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.3 2003/10/04 20:36:52 hmp 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         register struct tftphdr *ap;       /* data and ack packets */
93         struct tftphdr *r_init(), *dp;
94         register 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         register struct tftphdr *ap;
208         struct tftphdr *dp, *w_init();
209         register 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         register 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         register struct errmsg *pe;
364         register 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         register char *cp, *file;
392         u_short op = ntohs(tp->th_opcode);
393         char *index();
394
395         if (op < RRQ || op > ERROR)
396                 printf("%s opcode=%x ", s, op);
397         else
398                 printf("%s %s ", s, opcodes[op]);
399         switch (op) {
400
401         case RRQ:
402         case WRQ:
403                 n -= 2;
404                 file = cp = tp->th_stuff;
405                 cp = index(cp, '\0');
406                 printf("<file=%s, mode=%s>\n", file, cp + 1);
407                 break;
408
409         case DATA:
410                 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
411                 break;
412
413         case ACK:
414                 printf("<block=%d>\n", ntohs(tp->th_block));
415                 break;
416
417         case ERROR:
418                 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
419                 break;
420         }
421 }
422
423 struct timeval tstart;
424 struct timeval tstop;
425
426 static void
427 startclock(void)
428 {
429
430         (void)gettimeofday(&tstart, NULL);
431 }
432
433 static void
434 stopclock(void)
435 {
436
437         (void)gettimeofday(&tstop, NULL);
438 }
439
440 static void
441 printstats(const char *direction, unsigned long amount)
442 {
443         double delta;
444                         /* compute delta in 1/10's second units */
445         delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
446                 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
447         delta = delta/10.;      /* back to seconds */
448         printf("%s %d bytes in %.1f seconds", direction, amount, delta);
449         if (verbose)
450                 printf(" [%.0f bits/sec]", (amount*8.)/delta);
451         putchar('\n');
452 }
453
454 static void
455 timer(int sig)
456 {
457
458         timeout += rexmtval;
459         if (timeout >= maxtimeout) {
460                 printf("Transfer timed out.\n");
461                 longjmp(toplevel, -1);
462         }
463         txrx_error = 1;
464         longjmp(timeoutbuf, 1);
465 }
466
467 static int
468 cmpport(struct sockaddr *sa, struct sockaddr *sb)
469 {
470         char a[NI_MAXSERV], b[NI_MAXSERV];
471
472         if (getnameinfo(sa, sa->sa_len, NULL, 0, a, sizeof(a), NI_NUMERICSERV))
473                 return 0;
474         if (getnameinfo(sb, sb->sa_len, NULL, 0, b, sizeof(b), NI_NUMERICSERV))
475                 return 0;
476         if (strcmp(a, b) != 0)
477                 return 0;
478
479         return 1;
480 }