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