1 /* $NetBSD: tftp.c,v 1.29 2008/12/11 18:40:02 seanb Exp $ */
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93";
37 __RCSID("$NetBSD: tftp.c,v 1.29 2008/12/11 18:40:02 seanb Exp $");
41 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
44 * TFTP User Program -- Protocol Machines
46 #include <sys/types.h>
47 #include <sys/param.h>
48 #include <sys/socket.h>
52 #include <netinet/in.h>
54 #include <arpa/tftp.h>
55 #include <arpa/inet.h>
75 static void nak
__P((int, struct sockaddr
*));
76 static int makerequest
__P((int, const char *, struct tftphdr
*, const char *, off_t
));
77 static void printstats
__P((const char *, unsigned long));
78 static void startclock
__P((void));
79 static void stopclock
__P((void));
80 static void timer
__P((int));
81 static void tpacket
__P((const char *, struct tftphdr
*, int));
82 static int cmpport
__P((struct sockaddr
*, struct sockaddr
*));
84 static void get_options(struct tftphdr
*, int);
85 static int tftp_igmp_join(void);
86 static void tftp_igmp_leave(int);
89 get_options(struct tftphdr
*ap
, int size
)
92 char *opt
, *endp
, *nextopt
, *valp
;
95 size
-= 2; /* skip over opcode */
97 endp
= opt
+ size
- 1;
104 ismulticast
= !strcasecmp(opt
, "multicast");
106 val
= strtoul(valp
, NULL
, 10);
107 l
= strlen(valp
) + 1;
110 if (val
== ULONG_MAX
&& errno
== ERANGE
) {
111 /* Report illegal value */
117 /* Badly formed OACK */
120 if (strcmp(opt
, "tsize") == 0) {
121 /* cool, but we'll ignore it */
122 } else if (strcmp(opt
, "timeout") == 0) {
123 if (val
>= 1 && val
<= 255) {
128 } else if (strcmp(opt
, "blksize") == 0) {
129 if (val
>= 8 && val
<= MAXSEGSIZE
) {
134 } else if (ismulticast
) {
139 strlcpy(multicast
, valp
, sizeof(multicast
));
140 pmulticast
= multicast
;
141 addr
= strsep(&pmulticast
, ",");
142 if (pmulticast
== NULL
)
143 continue; /* Report error? */
144 mcport
= atoi(strsep(&pmulticast
, ","));
145 if (pmulticast
== NULL
)
146 continue; /* Report error? */
147 mcmasterslave
= atoi(pmulticast
);
148 mcaddr
= inet_addr(addr
);
149 if (mcaddr
== INADDR_NONE
)
150 continue; /* Report error? */
162 struct sockaddr_in s
;
165 memset(&req
, 0, sizeof(struct ip_mreq
));
166 req
.imr_multiaddr
.s_addr
= mcaddr
;
167 req
.imr_interface
.s_addr
= INADDR_ANY
;
169 fd
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
175 memset(&s
, 0, sizeof(struct sockaddr_in
));
176 s
.sin_family
= AF_INET
;
177 s
.sin_port
= htons(mcport
);
178 s
.sin_len
= sizeof(struct sockaddr_in
);
179 rv
= bind(fd
, (struct sockaddr
*)&s
, sizeof(struct sockaddr_in
));
186 rv
= setsockopt(fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &req
,
187 sizeof(struct ip_mreq
));
189 perror("setsockopt");
198 tftp_igmp_leave(int fd
)
203 memset(&req
, 0, sizeof(struct ip_mreq
));
204 req
.imr_multiaddr
.s_addr
= mcaddr
;
205 req
.imr_interface
.s_addr
= INADDR_ANY
;
207 rv
= setsockopt(fd
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, &req
,
208 sizeof(struct ip_mreq
));
210 perror("setsockopt");
218 * Send the requested file.
221 sendfile(fd
, name
, mode
)
226 struct tftphdr
*ap
; /* data and ack packets */
229 volatile unsigned int block
;
230 volatile int size
, convert
;
231 volatile unsigned long amount
;
232 struct sockaddr_storage from
;
234 volatile off_t filesize
= 0;
237 struct sockaddr_storage peer
;
238 struct sockaddr_storage serv
; /* valid server port number */
240 startclock(); /* start stat's clock */
241 dp
= r_init(); /* reset fillbuf/read-ahead code */
242 ap
= (struct tftphdr
*)(void *)ackbuf
;
244 if (fstat(fd
, &sbuf
) == 0) {
245 filesize
= sbuf
.st_size
;
250 file
= fdopen(fd
, "r");
251 convert
= !strcmp(mode
, "netascii");
254 (void)memcpy(&peer
, &peeraddr
, (size_t)peeraddr
.ss_len
);
255 (void)memset(&serv
, 0, sizeof(serv
));
257 (void)signal(SIGALRM
, timer
);
260 size
= makerequest(WRQ
, name
, dp
, mode
, filesize
) - 4;
262 /* size = read(fd, dp->th_data, SEGSIZE); */
263 size
= readit(file
, &dp
, blksize
, convert
);
265 nak(errno
+ 100, (struct sockaddr
*)(void *)&peer
);
268 dp
->th_opcode
= htons((u_short
)DATA
);
269 dp
->th_block
= htons((u_short
)block
);
272 (void) setjmp(timeoutbuf
);
275 tpacket("sent", dp
, size
+ 4);
276 n
= sendto(f
, dp
, (socklen_t
)(size
+ 4), 0,
277 (struct sockaddr
*)(void *)&peer
, (socklen_t
)peer
.ss_len
);
283 read_ahead(file
, blksize
, convert
);
285 (void)alarm(rexmtval
);
288 fromlen
= sizeof(from
);
289 if (mcaddr
!= INADDR_NONE
)
293 n
= recvfrom(curf
, ackbuf
, sizeof(ackbuf
), 0,
294 (struct sockaddr
*)(void *)&from
, &fromlen
);
303 else if (!cmpport((struct sockaddr
*)(void *)&serv
,
304 (struct sockaddr
*)(void *)&from
)) {
305 warn("server port mismatch");
310 tpacket("received", ap
, n
);
311 /* should verify packet came from server */
312 ap
->th_opcode
= ntohs(ap
->th_opcode
);
313 if (ap
->th_opcode
== ERROR
) {
314 (void)printf("Error code %d: %s\n", ap
->th_code
,
318 if (ap
->th_opcode
== ACK
) {
319 ap
->th_block
= ntohs(ap
->th_block
);
321 if (ap
->th_block
== 0) {
323 * If the extended options are enabled,
324 * the server just refused 'em all.
325 * The only one that _really_
326 * matters is blksize, but we'll
327 * clear timeout and mcaddr, too.
329 blksize
= def_blksize
;
330 rexmtval
= def_rexmtval
;
331 mcaddr
= INADDR_NONE
;
333 if (ap
->th_block
== block
) {
336 /* On an error, try to synchronize
339 j
= synchnet(f
, blksize
+4);
341 (void)printf("discarded %d packets\n",
344 if (ap
->th_block
== (block
-1)) {
348 if (ap
->th_opcode
== OACK
) {
350 blksize
= def_blksize
;
351 rexmtval
= def_rexmtval
;
352 mcaddr
= INADDR_NONE
;
361 } while ((size_t)size
== blksize
|| block
== 1);
366 printstats("Sent", amount
);
373 recvfile(fd
, name
, mode
)
381 volatile int oack
= 0;
382 volatile unsigned int block
;
383 volatile int size
, firsttrip
;
384 volatile unsigned long amount
;
385 struct sockaddr_storage from
;
387 volatile size_t readlen
;
389 volatile int convert
; /* true if converting crlf -> lf */
390 struct sockaddr_storage peer
;
391 struct sockaddr_storage serv
; /* valid server port number */
395 ap
= (struct tftphdr
*)(void *)ackbuf
;
396 file
= fdopen(fd
, "w");
397 convert
= !strcmp(mode
, "netascii");
401 (void)memcpy(&peer
, &peeraddr
, (size_t)peeraddr
.ss_len
);
402 (void)memset(&serv
, 0, sizeof(serv
));
404 (void)signal(SIGALRM
, timer
);
407 size
= makerequest(RRQ
, name
, ap
, mode
, (off_t
)0);
411 ap
->th_opcode
= htons((u_short
)ACK
);
412 ap
->th_block
= htons((u_short
)(block
));
418 (void) setjmp(timeoutbuf
);
421 tpacket("sent", ap
, size
);
422 if (sendto(f
, ackbuf
, (socklen_t
)size
, 0,
423 (struct sockaddr
*)(void *)&peer
,
424 (socklen_t
)peer
.ss_len
) != size
) {
430 if (write_behind(file
, convert
) == -1)
433 (void)alarm(rexmtval
);
440 fromlen
= sizeof(from
);
441 n
= recvfrom(readfd
, dp
, readlen
, 0,
442 (struct sockaddr
*)(void *)&from
, &fromlen
);
451 else if (!cmpport((struct sockaddr
*)(void *)&serv
,
452 (struct sockaddr
*)(void *)&from
)) {
453 warn("server port mismatch");
458 tpacket("received", dp
, n
);
459 /* should verify client address */
460 dp
->th_opcode
= ntohs(dp
->th_opcode
);
461 if (dp
->th_opcode
== ERROR
) {
462 (void)printf("Error code %d: %s\n", dp
->th_code
,
466 if (dp
->th_opcode
== DATA
) {
467 dp
->th_block
= ntohs(dp
->th_block
);
469 if (dp
->th_block
== 1 && !oack
) {
470 /* no OACK, revert to defaults */
471 blksize
= def_blksize
;
472 rexmtval
= def_rexmtval
;
474 if (dp
->th_block
== block
) {
475 break; /* have next packet */
477 /* On an error, try to synchronize
480 j
= synchnet(f
, blksize
);
482 (void)printf("discarded %d packets\n", j
);
484 if (dp
->th_block
== (block
-1)) {
485 goto send_ack
; /* resend ack */
488 if (dp
->th_opcode
== OACK
) {
491 blksize
= def_blksize
;
492 rexmtval
= def_rexmtval
;
494 ap
->th_opcode
= htons(ACK
);
498 if (mcaddr
!= INADDR_NONE
) {
499 mf
= tftp_igmp_join();
502 if (mcmasterslave
== 0)
509 /* size = write(fd, dp->th_data, n - 4); */
510 size
= writeit(file
, &dp
, n
- 4, convert
);
512 nak(errno
+ 100, (struct sockaddr
*)(void *)&peer
);
516 } while ((size_t)size
== blksize
);
517 abort
: /* ok to ack, since user */
518 ap
->th_opcode
= htons((u_short
)ACK
); /* has seen err msg */
519 ap
->th_block
= htons((u_short
)block
);
520 if (mcaddr
!= INADDR_NONE
&& mf
>= 0) {
524 (void) sendto(f
, ackbuf
, 4, 0, (struct sockaddr
*)(void *)&peer
,
525 (socklen_t
)peer
.ss_len
);
528 * We do not check for failure because last buffer
529 * can be empty, thus returning an error.
530 * XXX maybe we should fix 'write_behind' instead.
532 (void)write_behind(file
, convert
);
536 printstats("Received", amount
);
540 makerequest(request
, name
, tp
, mode
, filesize
)
549 tp
->th_opcode
= htons((u_short
)request
);
553 cp
= (void *)&tp
->th_stuff
;
555 (void)strcpy(cp
, name
);
558 (void)strcpy(cp
, mode
);
562 (void)strcpy(cp
, "tsize");
565 (void)sprintf(cp
, "%lu", (unsigned long) filesize
);
570 (void)strcpy(cp
, "timeout");
573 (void)sprintf(cp
, "%d", rexmtval
);
577 if (blksize
!= SEGSIZE
) {
578 (void)strcpy(cp
, "blksize");
581 (void)sprintf(cp
, "%zd", blksize
);
585 return (cp
- (char *)(void *)tp
);
588 const struct errmsg
{
592 { EUNDEF
, "Undefined error code" },
593 { ENOTFOUND
, "File not found" },
594 { EACCESS
, "Access violation" },
595 { ENOSPACE
, "Disk full or allocation exceeded" },
596 { EBADOP
, "Illegal TFTP operation" },
597 { EBADID
, "Unknown transfer ID" },
598 { EEXISTS
, "File already exists" },
599 { ENOUSER
, "No such user" },
600 { EOPTNEG
, "Option negotiation failed" },
605 * Send a nak packet (error message).
606 * Error code passed in is one of the
607 * standard TFTP codes, or a UNIX errno
613 struct sockaddr
*peer
;
615 const struct errmsg
*pe
;
620 tp
= (struct tftphdr
*)(void *)ackbuf
;
621 tp
->th_opcode
= htons((u_short
)ERROR
);
622 msglen
= sizeof(ackbuf
) - (&tp
->th_msg
[0] - ackbuf
);
623 for (pe
= errmsgs
; pe
->e_code
>= 0; pe
++)
624 if (pe
->e_code
== error
)
626 if (pe
->e_code
< 0) {
627 tp
->th_code
= EUNDEF
;
628 (void)strlcpy(tp
->th_msg
, strerror(error
- 100), msglen
);
630 tp
->th_code
= htons((u_short
)error
);
631 (void)strlcpy(tp
->th_msg
, pe
->e_msg
, msglen
);
633 length
= strlen(tp
->th_msg
);
634 msglen
= &tp
->th_msg
[length
+ 1] - ackbuf
;
636 tpacket("sent", tp
, (int)msglen
);
637 if ((size_t)sendto(f
, ackbuf
, msglen
, 0, peer
, (socklen_t
)peer
->sa_len
) != msglen
)
647 static const char *opcodes
[] =
648 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
649 char *cp
, *file
, *endp
, *opt
= NULL
;
651 u_short op
= ntohs(tp
->th_opcode
);
654 if (op
< RRQ
|| op
> OACK
)
655 (void)printf("%s opcode=%x ", s
, op
);
657 (void)printf("%s %s ", s
, opcodes
[op
]);
666 cp
= (void *) &tp
->th_stuff
;
669 if (*endp
!= '\0') { /* Shouldn't happen, but... */
673 cp
= strchr(cp
, '\0') + 1;
674 (void)printf("<file=%s, mode=%s", file
, cp
);
675 cp
= strchr(cp
, '\0') + 1;
680 (void)printf(", %s=%s", opt
, cp
);
691 (void)printf("<block=%d, %d bytes>\n", ntohs(tp
->th_block
), n
- 4);
695 (void)printf("<block=%d>\n", ntohs(tp
->th_block
));
699 (void)printf("<code=%d, msg=%s>\n", ntohs(tp
->th_code
), tp
->th_msg
);
707 if (*endp
!= '\0') { /* Shouldn't happen, but... */
715 (void)printf("%s%s=%s", spc
, opt
, cp
);
728 struct timeval tstart
;
729 struct timeval tstop
;
735 (void)gettimeofday(&tstart
, NULL
);
742 (void)gettimeofday(&tstop
, NULL
);
746 printstats(direction
, amount
)
747 const char *direction
;
748 unsigned long amount
;
752 /* compute delta in 1/10's second units */
753 delta
= ((tstop
.tv_sec
*10.)+(tstop
.tv_usec
/100000)) -
754 ((tstart
.tv_sec
*10.)+(tstart
.tv_usec
/100000));
755 delta
= delta
/10.; /* back to seconds */
756 (void)printf("%s %ld bytes in %.1f seconds", direction
, amount
, delta
);
758 (void)printf(" [%.0f bits/sec]", (amount
*8.)/delta
);
769 if (timeout
>= maxtimeout
) {
770 (void)printf("Transfer timed out.\n");
771 longjmp(toplevel
, -1);
773 longjmp(timeoutbuf
, 1);
781 char a
[NI_MAXSERV
], b
[NI_MAXSERV
];
783 if (getnameinfo(sa
, (socklen_t
)sa
->sa_len
, NULL
, 0, a
, sizeof(a
), NI_NUMERICSERV
))
785 if (getnameinfo(sb
, (socklen_t
)sb
->sa_len
, NULL
, 0, b
, sizeof(b
), NI_NUMERICSERV
))
787 if (strcmp(a
, b
) != 0)