1 /* $NetBSD: tftp.c,v 1.34 2011/12/25 06:09:08 tsutsui Exp $ */
5 * Matthias Drochner. 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.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * Simple TFTP implementation for libsa.
32 * - socket descriptor (int) at open_file->f_devdata
33 * - server host IP in global servip
36 * - lseek only with SEEK_SET or SEEK_CUR
37 * - no big time differences between transfers (<tftp timeout)
41 * XXX Does not currently implement:
43 * XXX LIBSA_NO_FS_CLOSE
44 * XXX LIBSA_NO_FS_SEEK
45 * XXX LIBSA_NO_FS_WRITE
46 * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
47 * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
50 #include <sys/types.h>
52 #include <netinet/in.h>
53 #include <netinet/udp.h>
54 #include <netinet/in_systm.h>
55 #include <lib/libkern/libkern.h>
62 extern struct in_addr servip
;
64 static int tftpport
= 2000;
66 #define RSPACE 520 /* max data packet, rounded up */
69 struct iodesc
*iodesc
;
70 int currblock
; /* contents of lastdata */
71 int islastblock
; /* flag */
74 const char *path
; /* saved for re-requests */
76 u_char header
[UDP_TOTAL_HEADER_SIZE
];
82 static const int tftperrors
[8] = {
93 static ssize_t
recvtftp(struct iodesc
*, void *, size_t, saseconds_t
);
94 static int tftp_makereq(struct tftp_handle
*);
95 static int tftp_getnextblock(struct tftp_handle
*);
96 #ifndef TFTP_NOTERMINATE
97 static void tftp_terminate(struct tftp_handle
*);
99 static ssize_t
tftp_size_of_file(struct tftp_handle
*tftpfile
);
102 recvtftp(struct iodesc
*d
, void *pkt
, size_t len
, saseconds_t tleft
)
109 n
= readudp(d
, pkt
, len
, tleft
);
114 t
= (struct tftphdr
*)pkt
;
115 switch (ntohs(t
->th_opcode
)) {
117 if (htons(t
->th_block
) != d
->xid
) {
125 * First data packet from new port.
128 uh
= (struct udphdr
*)pkt
- 1;
129 d
->destport
= uh
->uh_sport
;
130 } /* else check uh_sport has not changed??? */
131 return (n
- (t
->th_data
- (char *)t
));
133 if ((unsigned int)ntohs(t
->th_code
) >= 8) {
134 printf("illegal tftp error %d\n", ntohs(t
->th_code
));
138 printf("tftp-error %d\n", ntohs(t
->th_code
));
140 errno
= tftperrors
[ntohs(t
->th_code
)];
145 printf("tftp type %d not handled\n", ntohs(t
->th_opcode
));
151 /* send request, expect first block (or error) */
153 tftp_makereq(struct tftp_handle
*h
)
156 u_char header
[UDP_TOTAL_HEADER_SIZE
];
158 u_char space
[FNAME_SIZE
+ 6];
165 wbuf
.t
.th_opcode
= htons((u_short
)RRQ
);
166 wtail
= wbuf
.t
.th_stuff
;
168 (void)memcpy(wtail
, h
->path
, l
+ 1);
170 (void)memcpy(wtail
, "octet", 6);
175 /* h->iodesc->myport = htons(--tftpport); */
176 h
->iodesc
->myport
= htons(tftpport
+ (getsecs() & 0x3ff));
177 h
->iodesc
->destport
= htons(IPPORT_TFTP
);
178 h
->iodesc
->xid
= 1; /* expected block */
180 res
= sendrecv(h
->iodesc
, sendudp
, &wbuf
.t
, wtail
- (char *)&wbuf
.t
,
181 recvtftp
, t
, sizeof(*t
) + RSPACE
);
190 h
->islastblock
= 1; /* very short file */
194 /* ack block, expect next */
196 tftp_getnextblock(struct tftp_handle
*h
)
199 u_char header
[UDP_TOTAL_HEADER_SIZE
];
206 wbuf
.t
.th_opcode
= htons((u_short
)ACK
);
207 wbuf
.t
.th_block
= htons((u_short
)h
->currblock
);
208 wtail
= (char *)&wbuf
.t
.th_data
;
212 h
->iodesc
->xid
= h
->currblock
+ 1; /* expected block */
214 res
= sendrecv(h
->iodesc
, sendudp
, &wbuf
.t
, wtail
- (char *)&wbuf
.t
,
215 recvtftp
, t
, sizeof(*t
) + RSPACE
);
217 if (res
== -1) /* 0 is OK! */
223 h
->islastblock
= 1; /* EOF */
227 #ifndef TFTP_NOTERMINATE
229 tftp_terminate(struct tftp_handle
*h
)
232 u_char header
[UDP_TOTAL_HEADER_SIZE
];
237 wtail
= (char *)&wbuf
.t
.th_data
;
238 if (h
->islastblock
) {
239 wbuf
.t
.th_opcode
= htons((u_short
)ACK
);
240 wbuf
.t
.th_block
= htons((u_short
)h
->currblock
);
242 wbuf
.t
.th_opcode
= htons((u_short
)ERROR
);
243 wbuf
.t
.th_code
= htons((u_short
)ENOSPACE
); /* ??? */
244 *wtail
++ = '\0'; /* empty error string */
247 (void)sendudp(h
->iodesc
, &wbuf
.t
, wtail
- (char *)&wbuf
.t
);
252 tftp_open(const char *path
, struct open_file
*f
)
254 struct tftp_handle
*tftpfile
;
258 tftpfile
= (struct tftp_handle
*)alloc(sizeof(*tftpfile
));
262 tftpfile
->iodesc
= io
= socktodesc(*(int *)(f
->f_devdata
));
265 tftpfile
->path
= path
; /* XXXXXXX we hope it's static */
267 res
= tftp_makereq(tftpfile
);
270 dealloc(tftpfile
, sizeof(*tftpfile
));
273 f
->f_fsdata
= (void *)tftpfile
;
279 tftp_read(struct open_file
*f
, void *addr
, size_t size
, size_t *resid
)
281 struct tftp_handle
*tftpfile
;
282 #if !defined(LIBSA_NO_TWIDDLE)
285 tftpfile
= (struct tftp_handle
*)f
->f_fsdata
;
291 #if !defined(LIBSA_NO_TWIDDLE)
296 needblock
= tftpfile
->off
/ SEGSIZE
+ 1;
298 if (tftpfile
->currblock
> needblock
) { /* seek backwards */
299 #ifndef TFTP_NOTERMINATE
300 tftp_terminate(tftpfile
);
302 tftp_makereq(tftpfile
); /* no error check, it worked
306 while (tftpfile
->currblock
< needblock
) {
309 res
= tftp_getnextblock(tftpfile
);
310 if (res
) { /* no answer */
312 printf("tftp: read error (block %d->%d)\n",
313 tftpfile
->currblock
, needblock
);
317 if (tftpfile
->islastblock
)
321 if (tftpfile
->currblock
== needblock
) {
322 size_t offinblock
, inbuffer
;
324 offinblock
= tftpfile
->off
% SEGSIZE
;
326 if (offinblock
> tftpfile
->validsize
) {
328 printf("tftp: invalid offset %d\n",
333 inbuffer
= tftpfile
->validsize
- offinblock
;
334 count
= (size
< inbuffer
? size
: inbuffer
);
336 tftpfile
->lastdata
.t
.th_data
+ offinblock
,
339 addr
= (char *)addr
+ count
;
340 tftpfile
->off
+= count
;
343 if ((tftpfile
->islastblock
) && (count
== inbuffer
))
347 printf("tftp: block %d not found\n", needblock
);
360 tftp_close(struct open_file
*f
)
362 struct tftp_handle
*tftpfile
;
363 tftpfile
= (struct tftp_handle
*)f
->f_fsdata
;
365 #ifdef TFTP_NOTERMINATE
366 /* let it time out ... */
368 tftp_terminate(tftpfile
);
371 dealloc(tftpfile
, sizeof(*tftpfile
));
376 tftp_write(struct open_file
*f
, void *start
, size_t size
, size_t *resid
)
383 tftp_size_of_file(struct tftp_handle
*tftpfile
)
387 if (tftpfile
->currblock
> 1) { /* move to start of file */
388 #ifndef TFTP_NOTERMINATE
389 tftp_terminate(tftpfile
);
391 tftp_makereq(tftpfile
); /* no error check, it worked
395 /* start with the size of block 1 */
396 filesize
= tftpfile
->validsize
;
398 /* and keep adding the sizes till we hit the last block */
399 while (!tftpfile
->islastblock
) {
402 res
= tftp_getnextblock(tftpfile
);
403 if (res
) { /* no answer */
405 printf("tftp: read error (block %d)\n",
406 tftpfile
->currblock
);
410 filesize
+= tftpfile
->validsize
;
413 printf("tftp_size_of_file: file is %zu bytes\n", filesize
);
419 tftp_stat(struct open_file
*f
, struct stat
*sb
)
421 struct tftp_handle
*tftpfile
;
422 tftpfile
= (struct tftp_handle
*)f
->f_fsdata
;
428 sb
->st_size
= tftp_size_of_file(tftpfile
);
432 #if defined(LIBSA_ENABLE_LS_OP)
434 tftp_ls(struct open_file
*f
, const char *pattern
,
435 void (*funcp
)(char* arg
), char* path
)
437 printf("Currently ls command is unsupported by tftp\n");
443 tftp_seek(struct open_file
*f
, off_t offset
, int where
)
445 struct tftp_handle
*tftpfile
;
446 tftpfile
= (struct tftp_handle
*)f
->f_fsdata
;
450 tftpfile
->off
= offset
;
453 tftpfile
->off
+= offset
;
459 return tftpfile
->off
;