1 /* $NetBSD: tftp.c,v 1.28 2009/01/12 11:32:45 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
[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
[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
[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
[HEADER_SIZE
];
237 if (h
->islastblock
) {
238 wbuf
.t
.th_opcode
= htons((u_short
)ACK
);
239 wbuf
.t
.th_block
= htons((u_short
)h
->currblock
);
241 wbuf
.t
.th_opcode
= htons((u_short
)ERROR
);
242 wbuf
.t
.th_code
= htons((u_short
)ENOSPACE
); /* ??? */
244 wtail
= (char *)&wbuf
.t
.th_data
;
246 (void)sendudp(h
->iodesc
, &wbuf
.t
, wtail
- (char *)&wbuf
.t
);
251 tftp_open(const char *path
, struct open_file
*f
)
253 struct tftp_handle
*tftpfile
;
257 tftpfile
= (struct tftp_handle
*)alloc(sizeof(*tftpfile
));
261 tftpfile
->iodesc
= io
= socktodesc(*(int *)(f
->f_devdata
));
264 tftpfile
->path
= path
; /* XXXXXXX we hope it's static */
266 res
= tftp_makereq(tftpfile
);
269 dealloc(tftpfile
, sizeof(*tftpfile
));
272 f
->f_fsdata
= (void *)tftpfile
;
278 tftp_read(struct open_file
*f
, void *addr
, size_t size
, size_t *resid
)
280 struct tftp_handle
*tftpfile
;
281 #if !defined(LIBSA_NO_TWIDDLE)
284 tftpfile
= (struct tftp_handle
*)f
->f_fsdata
;
290 #if !defined(LIBSA_NO_TWIDDLE)
295 needblock
= tftpfile
->off
/ SEGSIZE
+ 1;
297 if (tftpfile
->currblock
> needblock
) { /* seek backwards */
298 #ifndef TFTP_NOTERMINATE
299 tftp_terminate(tftpfile
);
301 tftp_makereq(tftpfile
); /* no error check, it worked
305 while (tftpfile
->currblock
< needblock
) {
308 res
= tftp_getnextblock(tftpfile
);
309 if (res
) { /* no answer */
311 printf("tftp: read error (block %d->%d)\n",
312 tftpfile
->currblock
, needblock
);
316 if (tftpfile
->islastblock
)
320 if (tftpfile
->currblock
== needblock
) {
321 size_t offinblock
, inbuffer
;
323 offinblock
= tftpfile
->off
% SEGSIZE
;
325 if (offinblock
> tftpfile
->validsize
) {
327 printf("tftp: invalid offset %d\n",
332 inbuffer
= tftpfile
->validsize
- offinblock
;
333 count
= (size
< inbuffer
? size
: inbuffer
);
335 tftpfile
->lastdata
.t
.th_data
+ offinblock
,
338 addr
= (char *)addr
+ count
;
339 tftpfile
->off
+= count
;
342 if ((tftpfile
->islastblock
) && (count
== inbuffer
))
346 printf("tftp: block %d not found\n", needblock
);
359 tftp_close(struct open_file
*f
)
361 struct tftp_handle
*tftpfile
;
362 tftpfile
= (struct tftp_handle
*)f
->f_fsdata
;
364 #ifdef TFTP_NOTERMINATE
365 /* let it time out ... */
367 tftp_terminate(tftpfile
);
370 dealloc(tftpfile
, sizeof(*tftpfile
));
375 tftp_write(struct open_file
*f
, void *start
, size_t size
, size_t *resid
)
382 tftp_size_of_file(struct tftp_handle
*tftpfile
)
386 if (tftpfile
->currblock
> 1) { /* move to start of file */
387 #ifndef TFTP_NOTERMINATE
388 tftp_terminate(tftpfile
);
390 tftp_makereq(tftpfile
); /* no error check, it worked
394 /* start with the size of block 1 */
395 filesize
= tftpfile
->validsize
;
397 /* and keep adding the sizes till we hit the last block */
398 while (!tftpfile
->islastblock
) {
401 res
= tftp_getnextblock(tftpfile
);
402 if (res
) { /* no answer */
404 printf("tftp: read error (block %d)\n",
405 tftpfile
->currblock
);
409 filesize
+= tftpfile
->validsize
;
412 printf("tftp_size_of_file: file is %d bytes\n", filesize
);
418 tftp_stat(struct open_file
*f
, struct stat
*sb
)
420 struct tftp_handle
*tftpfile
;
421 tftpfile
= (struct tftp_handle
*)f
->f_fsdata
;
427 sb
->st_size
= tftp_size_of_file(tftpfile
);
432 tftp_seek(struct open_file
*f
, off_t offset
, int where
)
434 struct tftp_handle
*tftpfile
;
435 tftpfile
= (struct tftp_handle
*)f
->f_fsdata
;
439 tftpfile
->off
= offset
;
442 tftpfile
->off
+= offset
;
448 return tftpfile
->off
;