1 /* $Id: tftplib.c,v 1.1.1.1 2006/09/14 01:59:08 root Exp $ */
4 * Copyright (c) 2001-2002 Opsycon AB (www.opsycon.se / www.opsycon.com)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Opsycon AB, Sweden.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
21 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/socket.h>
38 #include <sys/ioctl.h>
41 #include <sys/syslog.h>
42 #include <sys/endian.h>
44 #include <netinet/in.h>
45 #include <arpa/tftp.h>
61 extern void log
__P((int kind
, const char *fmt
, ...));
62 extern in_addr_t inet_addr
__P((const char *));
68 static const char spinner
[] = "-\\|/";
70 static const int tftperrmap
[] = {
71 EIO
, /* EUNDEF not defined */
72 ENOENT
, /* ENOTFOUND file not found */
73 EACCES
, /* EACCESS access violation */
74 ENOSPC
, /* ENOSPACE disk full or allocation exceeded */
75 EIO
, /* EBADOP illegal TFTP operation */
76 EINVAL
, /* EBADID unknown transfer ID */
77 EEXIST
, /* EEXISTS file already exists */
78 EACCES
/* ENOUSER no such user */
81 #define TIMEOUT 2 /* secs between rexmts */
82 #define MAXREXMT 0x7fffffff /* no of rexmts */
83 #define PKTSIZE (SEGSIZE+4)
86 struct sockaddr_in sin
;
97 static int tftpopen (int, struct Url
*, int, int);
98 static int tftpread (int, void *, int);
99 static int tftpwrite (int, const void *, int);
100 static off_t
tftplseek (int, long, int);
101 static int tftpioctl (int, int, void *);
102 static int tftpclose (int);
104 static NetFileOps tftpops
= {
114 static void init_netfs
__P((void)) __attribute__ ((constructor
));
120 * Install ram based file system.
122 netfs_init(&tftpops
);
126 static int tftprrq (struct tftpfile
*tfp
, struct tftphdr
*req
, int size
);
127 static int tftpwrq (struct tftpfile
*tfp
, struct tftphdr
*req
, int size
);
128 static void tftpnxtblk (struct tftpfile
*tfp
);
129 static int makerequest (int request
, const char *name
, struct tftphdr
*tp
,
132 static void tpacket (char *s
, struct tftphdr
*tp
, int n
);
134 static int synchnet (int f
);
136 extern char activeif_name
[];
144 bzero (&ifr
, sizeof(ifr
));
145 strncpy(ifr
.ifr_name
, activeif_name
, IFNAMSIZ
);
146 ifr
.ifr_flags
=IFF_UP
;
147 ifioctl(0,SIOCSIFFLAGS
,&ifr
);
154 tftpopen (int fd
, struct Url
*url
, int flags
, int perms
)
160 char hbuf
[MAXHOSTNAMELEN
];
161 char reqbuf
[PKTSIZE
];
162 struct tftpfile
*tfp
;
165 NetFile
*nfp
= (NetFile
*)_file
[fd
].data
;
167 oflags
= flags
& O_ACCMODE
;
168 if (oflags
!= O_RDONLY
&& oflags
!= O_WRONLY
) {
173 sp
= getservbyname("tftp", "udp");
175 errno
= EPROTONOSUPPORT
;
180 tftptrace
= getenv ("tftptrace") ? 1 : 0;
183 if (strlen(url
->hostname
) != 0) {
184 host
= url
->hostname
;
186 host
= getenv ("tftphost");
188 log (LOG_INFO
, "tftp: missing/bad host name: %s\n", url
->filename
);
189 errno
= EDESTADDRREQ
;
194 tfp
= (struct tftpfile
*) malloc (sizeof (struct tftpfile
));
199 bzero (tfp
, sizeof (struct tftpfile
));
200 nfp
->data
= (void *)tfp
;
202 tfp
->sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
206 tfp
->sin
.sin_family
= AF_INET
;
207 if (bind(tfp
->sock
, (struct sockaddr
*)&tfp
->sin
, sizeof (tfp
->sin
)) < 0)
210 hp
= gethostbyname(host
);
212 tfp
->sin
.sin_family
= hp
->h_addrtype
;
213 bcopy(hp
->h_addr
, (void *)&tfp
->sin
.sin_addr
, hp
->h_length
);
214 strncpy (hbuf
, hp
->h_name
, sizeof(hbuf
) - 1);
215 hbuf
[sizeof(hbuf
)-1] = '\0';
218 tfp
->sin
.sin_family
= AF_INET
;
219 tfp
->sin
.sin_addr
.s_addr
= inet_addr (host
);
220 if (tfp
->sin
.sin_addr
.s_addr
== -1) {
221 log (LOG_INFO
, "tftp: bad internet address: %s\n", host
);
222 errno
= EADDRNOTAVAIL
;
227 tfp
->sin
.sin_port
= sp
->s_port
;
229 rp
= (struct tftphdr
*)reqbuf
;
234 if (oflags
== O_RDONLY
) {
236 size
= makerequest(RRQ
, url
->filename
, rp
, mode
);
237 size
= tftprrq (tfp
, rp
, size
);
238 tfp
->end
= tfp
->start
+ size
;
241 size
= makerequest(WRQ
, url
->filename
, rp
, mode
);
242 size
= tftpwrq (tfp
, rp
, size
- 4);
257 tftpread (fd
, buf
, nread
)
263 struct tftpfile
*tfp
;
267 nfp
= (NetFile
*)_file
[fd
].data
;
268 tfp
= (struct tftpfile
*)nfp
->data
;
270 if ((tfp
->flags
& O_ACCMODE
) != O_RDONLY
) {
275 rp
= (struct tftphdr
*) tfp
->buf
;
277 /* continue while more bytes wanted, and more available */
278 for (nb
= nread
; nb
!= 0 && tfp
->start
< tfp
->end
; ) {
280 if (tfp
->foffs
>= tfp
->start
&& tfp
->foffs
< tfp
->end
) {
281 /* got some data that's in range */
282 n
= tfp
->end
- tfp
->foffs
;
284 bcopy (&rp
->th_data
[tfp
->foffs
- tfp
->start
], buf
, n
);
290 if (tfp
->foffs
>= tfp
->end
) {
291 /* buffer is empty, ack last packet and refill */
293 ack
.th_opcode
= htons((u_short
)ACK
);
294 ack
.th_block
= htons((u_short
)tfp
->block
);
296 n
= tftprrq (tfp
, &ack
, 4);
299 tfp
->start
= tfp
->end
;
300 tfp
->end
= tfp
->start
+ n
;
308 tftpwrite (fd
, buf
, nwrite
)
314 struct tftpfile
*tfp
;
318 nfp
= (NetFile
*)_file
[fd
].data
;
319 tfp
= (struct tftpfile
*)nfp
->data
;
321 if ((tfp
->flags
& O_ACCMODE
) != O_WRONLY
) {
326 dp
= (struct tftphdr
*)tfp
->buf
;
327 for (nb
= nwrite
; nb
!= 0; ) {
328 n
= SEGSIZE
- tfp
->end
;
330 bcopy (buf
, &dp
->th_data
[tfp
->end
], n
);
336 if (tfp
->end
== SEGSIZE
) {
337 /* buffer is full, send it */
339 dp
->th_opcode
= htons((u_short
)DATA
);
340 dp
->th_block
= htons((u_short
)tfp
->block
);
341 n
= tftpwrq (tfp
, dp
, tfp
->end
);
351 tftplseek (fd
, offs
, how
)
357 struct tftpfile
*tfp
;
360 nfp
= (NetFile
*)_file
[fd
].data
;
361 tfp
= (struct tftpfile
*)nfp
->data
;
368 noffs
= tfp
->foffs
+ offs
;
375 if ((tfp
->flags
& O_ACCMODE
) == O_WRONLY
) {
376 if (noffs
!= tfp
->foffs
) {
381 if (noffs
< tfp
->start
) {
393 tftpioctl (fd
, op
, argp
)
408 struct tftpfile
*tfp
;
411 nfp
= (NetFile
*)_file
[fd
].data
;
412 tfp
= (struct tftpfile
*)nfp
->data
;
414 tp
= (struct tftphdr
*) tfp
->buf
;
415 if ((tfp
->flags
& O_ACCMODE
) == O_WRONLY
) {
417 /* flush last block */
419 tp
->th_opcode
= htons((u_short
)DATA
);
420 tp
->th_block
= htons((u_short
)tfp
->block
);
421 n
= tftpwrq (tfp
, tp
, tfp
->end
);
422 if (n
>= 0 && tfp
->end
== SEGSIZE
) {
423 /* last block == SEGSIZE, so send empty one to terminate */
425 tp
->th_opcode
= htons((u_short
)DATA
);
426 tp
->th_block
= htons((u_short
)tfp
->block
);
427 (void) tftpwrq (tfp
, tp
, 0);
430 if (tfp
->foffs
< tfp
->end
|| !tfp
->eof
) {
434 /* didn't reach eof, so send a nak to terminate */
435 tp
->th_opcode
= htons((u_short
)ERROR
);
436 tp
->th_code
= htons((u_short
)EUNDEF
);
438 strcpy(tp
->th_msg
, msg
);
439 length
= strlen(msg
) + 4;
442 tpacket("sent", tp
, length
);
444 if (sendto(tfp
->sock
, tp
, length
, 0, (struct sockaddr
*)&tfp
->sin
,
445 sizeof (tfp
->sin
)) != length
)
446 perror("sendto(eof)");
458 struct tftpfile
*tfp
;
461 if (tfp
->flags
& O_NONBLOCK
)
467 tftprrq (tfp
, req
, size
)
468 struct tftpfile
*tfp
;
473 struct sockaddr_in from
;
482 tpacket("sent", req
, size
);
484 if (sendto(tfp
->sock
, req
, size
, 0, (struct sockaddr
*)&tfp
->sin
,
485 sizeof (tfp
->sin
)) != size
) {
486 perror("tftp: sendto");
491 /* reached eof, no more to read */
495 FD_SET(tfp
->sock
, &ifds
);
496 timo
.tv_sec
= TIMEOUT
; timo
.tv_usec
= 0;
497 switch (select (tfp
->sock
+ 1, &ifds
, 0, 0, &timo
)) {
499 perror("tftp: select");
502 if(rexmt
==(MAXREXMT
>>1))myifup();
503 if (++rexmt
> MAXREXMT
) {
507 log (LOG_INFO
, "tftp: timeout, retry %d\n", rexmt
);
511 fromlen
= sizeof (from
);
512 rp
= (struct tftphdr
*) tfp
->buf
;
513 n
= recvfrom(tfp
->sock
, rp
, PKTSIZE
, 0,
514 (struct sockaddr
*)&from
, &fromlen
);
516 perror("tftp: recvfrom");
521 tpacket("received", rp
, n
);
525 tfp
->sin
.sin_port
= from
.sin_port
;
526 else if (from
.sin_port
!= tfp
->sin
.sin_port
)
528 /* should verify client address completely? */
530 rp
->th_opcode
= ntohs(rp
->th_opcode
);
531 rp
->th_block
= ntohs(rp
->th_block
);
532 if (rp
->th_opcode
== ERROR
) {
534 log(LOG_INFO
, "tftp: %s\n", rp
->th_msg
);
535 errno
= tftperrmap
[rp
->th_code
& 7];
539 if (rp
->th_opcode
== DATA
) {
541 if (rp
->th_block
== tfp
->block
) {
549 /* On an error, try to synchronize
552 j
= synchnet(tfp
->sock
);
554 log (LOG_INFO
, "tftp: discarded %d packets\n", j
);
556 if (rp
->th_block
!= tfp
->block
- 1)
563 tftpwrq (tfp
, req
, size
)
564 struct tftpfile
*tfp
;
568 char ackbuf
[PKTSIZE
];
570 struct sockaddr_in from
;
579 tpacket("sent", req
, size
);
581 if (sendto(tfp
->sock
, req
, size
+4, 0, (struct sockaddr
*)&tfp
->sin
,
582 sizeof (tfp
->sin
)) != size
+4) {
583 perror("tftp: sendto");
588 FD_SET(tfp
->sock
, &ifds
);
589 timo
.tv_sec
= TIMEOUT
; timo
.tv_usec
= 0;
590 switch (select (tfp
->sock
+ 1, &ifds
, 0, 0, &timo
)) {
592 perror("tftp: select");
595 if(rexmt
==(MAXREXMT
>>1))myifup();
596 if (++rexmt
> MAXREXMT
) {
600 log (LOG_INFO
, "tftp: timeout, retry %d\n", rexmt
);
604 fromlen
= sizeof (from
);
605 rp
= (struct tftphdr
*) ackbuf
;
606 n
= recvfrom(tfp
->sock
, rp
, PKTSIZE
, 0,
607 (struct sockaddr
*)&from
, &fromlen
);
609 perror("tftp: recvfrom");
614 tpacket("received", rp
, n
);
618 tfp
->sin
.sin_port
= from
.sin_port
;
619 else if (from
.sin_port
!= tfp
->sin
.sin_port
)
621 /* should verify client address completely? */
623 rp
->th_opcode
= ntohs(rp
->th_opcode
);
624 rp
->th_block
= ntohs(rp
->th_block
);
625 if (rp
->th_opcode
== ERROR
) {
627 log(LOG_INFO
, "tftp: %s\n", rp
->th_msg
);
628 errno
= tftperrmap
[rp
->th_code
& 7];
632 if (rp
->th_opcode
== ACK
) {
634 if (rp
->th_block
== tfp
->block
)
635 /* acknowledged packet */
638 /* On an error, try to synchronize
641 j
= synchnet(tfp
->sock
);
643 log (LOG_INFO
, "tftp: discarded %d packets\n", j
);
645 if (rp
->th_block
!= tfp
->block
- 1)
652 makerequest(request
, name
, tp
, mode
)
654 const char *name
, *mode
;
659 tp
->th_opcode
= htons((u_short
)request
);
667 return (cp
- (char *)tp
);
678 static const char * const opcodes
[] =
679 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
680 register char *cp
, *file
;
681 u_short op
= ntohs(tp
->th_opcode
);
683 if (op
< RRQ
|| op
> ERROR
)
684 printf("%s opcode=%x ", s
, op
);
686 printf("%s %s ", s
, opcodes
[op
]);
692 file
= cp
= tp
->th_stuff
;
694 printf("<file=%s, mode=%s>\n", file
, cp
+ 1);
698 printf("<block=%d, %d bytes>\n", ntohs(tp
->th_block
), n
- 4);
702 printf("<block=%d>\n", ntohs(tp
->th_block
));
706 printf("<code=%d, msg=%s>\n", ntohs(tp
->th_code
), tp
->th_msg
);
713 /* When an error has occurred, it is possible that the two sides
714 * are out of synch. Ie: that what I think is the other side's
715 * response to packet N is really their response to packet N-1.
717 * So, to try to prevent that, we flush all the input queued up
718 * for us on the network connection on our host.
720 * We return the number of packets we flushed (mostly for reporting
721 * when trace is active).
726 int f
; /* socket to flush */
730 struct sockaddr_in from
;
733 (void) ioctl(f
, FIONREAD
, &i
);
736 fromlen
= sizeof from
;
737 (void) recvfrom(f
, rbuf
, sizeof (rbuf
), 0,
738 (struct sockaddr
*)&from
, &fromlen
);