tftp.c revision 1.8 1 /* $NetBSD: tftp.c,v 1.8 1999/11/11 20:23:17 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1996
5 * Matthias Drochner. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the NetBSD Project
18 * by Matthias Drochner.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 /*
36 * Simple TFTP implementation for libsa.
37 * Assumes:
38 * - socket descriptor (int) at open_file->f_devdata
39 * - server host IP in global servip
40 * Restrictions:
41 * - read only
42 * - lseek only with SEEK_SET or SEEK_CUR
43 * - no big time differences between transfers (<tftp timeout)
44 */
45
46 /*
47 * XXX Does not currently implement:
48 * XXX
49 * XXX LIBSA_NO_FS_CLOSE
50 * XXX LIBSA_NO_FS_SEEK
51 * XXX LIBSA_NO_FS_WRITE
52 * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
53 * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
54 */
55
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <netinet/in.h>
59 #include <netinet/udp.h>
60 #include <netinet/in_systm.h>
61 #ifdef _STANDALONE
62 #include "/usr/include/arpa/tftp.h" /* XXX */
63 #else
64 #include <arpa/tftp.h>
65 #endif
66
67 #include "stand.h"
68 #include "net.h"
69 #include "netif.h"
70
71 #include "tftp.h"
72
73 extern struct in_addr servip;
74
75 static int tftpport = 2000;
76
77 #define RSPACE 520 /* max data packet, rounded up */
78
79 struct tftp_handle {
80 struct iodesc *iodesc;
81 int currblock; /* contents of lastdata */
82 int islastblock; /* flag */
83 int validsize;
84 int off;
85 char *path; /* saved for re-requests */
86 struct {
87 u_char header[HEADER_SIZE];
88 struct tftphdr t;
89 u_char space[RSPACE];
90 } lastdata;
91 };
92
93 static int tftperrors[8] = {
94 0, /* ??? */
95 ENOENT,
96 EPERM,
97 ENOSPC,
98 EINVAL, /* ??? */
99 EINVAL, /* ??? */
100 EEXIST,
101 EINVAL /* ??? */
102 };
103
104 static ssize_t recvtftp __P((struct iodesc *, void *, size_t, time_t));
105 static int tftp_makereq __P((struct tftp_handle *));
106 static int tftp_getnextblock __P((struct tftp_handle *));
107 #ifndef TFTP_NOTERMINATE
108 static void tftp_terminate __P((struct tftp_handle *));
109 #endif
110
111 static ssize_t
112 recvtftp(d, pkt, len, tleft)
113 register struct iodesc *d;
114 register void *pkt;
115 register size_t len;
116 time_t tleft;
117 {
118 ssize_t n;
119 struct tftphdr *t;
120
121 errno = 0;
122
123 n = readudp(d, pkt, len, tleft);
124
125 if (n < 4)
126 return (-1);
127
128 t = (struct tftphdr *) pkt;
129 switch (ntohs(t->th_opcode)) {
130 case DATA:
131 if (htons(t->th_block) != d->xid) {
132 /*
133 * Expected block?
134 */
135 return (-1);
136 }
137 if (d->xid == 1) {
138 /*
139 * First data packet from new port.
140 */
141 register struct udphdr *uh;
142 uh = (struct udphdr *) pkt - 1;
143 d->destport = uh->uh_sport;
144 } /* else check uh_sport has not changed??? */
145 return (n - (t->th_data - (char *)t));
146 case ERROR:
147 if ((unsigned) ntohs(t->th_code) >= 8) {
148 printf("illegal tftp error %d\n", ntohs(t->th_code));
149 errno = EIO;
150 } else {
151 #ifdef DEBUG
152 printf("tftp-error %d\n", ntohs(t->th_code));
153 #endif
154 errno = tftperrors[ntohs(t->th_code)];
155 }
156 return (-1);
157 default:
158 #ifdef DEBUG
159 printf("tftp type %d not handled\n", ntohs(t->th_opcode));
160 #endif
161 return (-1);
162 }
163 }
164
165 /* send request, expect first block (or error) */
166 static int
167 tftp_makereq(h)
168 struct tftp_handle *h;
169 {
170 struct {
171 u_char header[HEADER_SIZE];
172 struct tftphdr t;
173 u_char space[FNAME_SIZE + 6];
174 } wbuf;
175 char *wtail;
176 int l;
177 ssize_t res;
178 struct tftphdr *t;
179
180 wbuf.t.th_opcode = htons((u_short) RRQ);
181 wtail = wbuf.t.th_stuff;
182 l = strlen(h->path);
183 bcopy(h->path, wtail, l + 1);
184 wtail += l + 1;
185 bcopy("octet", wtail, 6);
186 wtail += 6;
187
188 t = &h->lastdata.t;
189
190 /* h->iodesc->myport = htons(--tftpport); */
191 h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
192 h->iodesc->destport = htons(IPPORT_TFTP);
193 h->iodesc->xid = 1; /* expected block */
194
195 res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
196 recvtftp, t, sizeof(*t) + RSPACE);
197
198 if (res == -1)
199 return (errno);
200
201 h->currblock = 1;
202 h->validsize = res;
203 h->islastblock = 0;
204 if (res < SEGSIZE)
205 h->islastblock = 1; /* very short file */
206 return (0);
207 }
208
209 /* ack block, expect next */
210 static int
211 tftp_getnextblock(h)
212 struct tftp_handle *h;
213 {
214 struct {
215 u_char header[HEADER_SIZE];
216 struct tftphdr t;
217 } wbuf;
218 char *wtail;
219 int res;
220 struct tftphdr *t;
221
222 wbuf.t.th_opcode = htons((u_short) ACK);
223 wbuf.t.th_block = htons((u_short) h->currblock);
224 wtail = (char *) &wbuf.t.th_data;
225
226 t = &h->lastdata.t;
227
228 h->iodesc->xid = h->currblock + 1; /* expected block */
229
230 res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
231 recvtftp, t, sizeof(*t) + RSPACE);
232
233 if (res == -1) /* 0 is OK! */
234 return (errno);
235
236 h->currblock++;
237 h->validsize = res;
238 if (res < SEGSIZE)
239 h->islastblock = 1; /* EOF */
240 return (0);
241 }
242
243 #ifndef TFTP_NOTERMINATE
244 static void
245 tftp_terminate(h)
246 struct tftp_handle *h;
247 {
248 struct {
249 u_char header[HEADER_SIZE];
250 struct tftphdr t;
251 } wbuf;
252 char *wtail;
253
254 if (h->islastblock) {
255 wbuf.t.th_opcode = htons((u_short) ACK);
256 wbuf.t.th_block = htons((u_short) h->currblock);
257 } else {
258 wbuf.t.th_opcode = htons((u_short) ERROR);
259 wbuf.t.th_code = htons((u_short) ENOSPACE); /* ??? */
260 }
261 wtail = (char *) &wbuf.t.th_data;
262
263 (void) sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
264 }
265 #endif
266
267 int
268 tftp_open(path, f)
269 char *path;
270 struct open_file *f;
271 {
272 struct tftp_handle *tftpfile;
273 struct iodesc *io;
274 int res;
275
276 tftpfile = (struct tftp_handle *) alloc(sizeof(*tftpfile));
277 if (!tftpfile)
278 return (ENOMEM);
279
280 tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
281 io->destip = servip;
282 tftpfile->off = 0;
283 tftpfile->path = path; /* XXXXXXX we hope it's static */
284
285 res = tftp_makereq(tftpfile);
286
287 if (res) {
288 free(tftpfile, sizeof(*tftpfile));
289 return (res);
290 }
291 f->f_fsdata = (void *) tftpfile;
292 return (0);
293 }
294
295 int
296 tftp_read(f, addr, size, resid)
297 struct open_file *f;
298 void *addr;
299 size_t size;
300 size_t *resid; /* out */
301 {
302 struct tftp_handle *tftpfile;
303 #if !defined(LIBSA_NO_TWIDDLE)
304 static int tc = 0;
305 #endif
306 tftpfile = (struct tftp_handle *) f->f_fsdata;
307
308 while (size > 0) {
309 int needblock, count;
310
311 #if !defined(LIBSA_NO_TWIDDLE)
312 if (!(tc++ % 16))
313 twiddle();
314 #endif
315
316 needblock = tftpfile->off / SEGSIZE + 1;
317
318 if (tftpfile->currblock > needblock) { /* seek backwards */
319 #ifndef TFTP_NOTERMINATE
320 tftp_terminate(tftpfile);
321 #endif
322 tftp_makereq(tftpfile); /* no error check, it worked
323 * for open */
324 }
325
326 while (tftpfile->currblock < needblock) {
327 int res;
328
329 res = tftp_getnextblock(tftpfile);
330 if (res) { /* no answer */
331 #ifdef DEBUG
332 printf("tftp: read error (block %d->%d)\n",
333 tftpfile->currblock, needblock);
334 #endif
335 return (res);
336 }
337 if (tftpfile->islastblock)
338 break;
339 }
340
341 if (tftpfile->currblock == needblock) {
342 int offinblock, inbuffer;
343
344 offinblock = tftpfile->off % SEGSIZE;
345
346 inbuffer = tftpfile->validsize - offinblock;
347 if (inbuffer < 0) {
348 #ifdef DEBUG
349 printf("tftp: invalid offset %d\n",
350 tftpfile->off);
351 #endif
352 return (EINVAL);
353 }
354 count = (size < inbuffer ? size : inbuffer);
355 bcopy(tftpfile->lastdata.t.th_data + offinblock,
356 addr, count);
357
358 addr = (caddr_t)addr + count;
359 tftpfile->off += count;
360 size -= count;
361
362 if ((tftpfile->islastblock) && (count == inbuffer))
363 break; /* EOF */
364 } else {
365 #ifdef DEBUG
366 printf("tftp: block %d not found\n", needblock);
367 #endif
368 return (EINVAL);
369 }
370
371 }
372
373 if (resid)
374 *resid = size;
375 return (0);
376 }
377
378 int
379 tftp_close(f)
380 struct open_file *f;
381 {
382 struct tftp_handle *tftpfile;
383 tftpfile = (struct tftp_handle *) f->f_fsdata;
384
385 #ifdef TFTP_NOTERMINATE
386 /* let it time out ... */
387 #else
388 tftp_terminate(tftpfile);
389 #endif
390
391 free(tftpfile, sizeof(*tftpfile));
392 return (0);
393 }
394
395 int
396 tftp_write(f, start, size, resid)
397 struct open_file *f;
398 void *start;
399 size_t size;
400 size_t *resid; /* out */
401 {
402 return (EROFS);
403 }
404
405 int
406 tftp_stat(f, sb)
407 struct open_file *f;
408 struct stat *sb;
409 {
410 struct tftp_handle *tftpfile;
411 tftpfile = (struct tftp_handle *) f->f_fsdata;
412
413 sb->st_mode = 0444;
414 sb->st_nlink = 1;
415 sb->st_uid = 0;
416 sb->st_gid = 0;
417 sb->st_size = -1;
418 return (0);
419 }
420
421 off_t
422 tftp_seek(f, offset, where)
423 struct open_file *f;
424 off_t offset;
425 int where;
426 {
427 struct tftp_handle *tftpfile;
428 tftpfile = (struct tftp_handle *) f->f_fsdata;
429
430 switch (where) {
431 case SEEK_SET:
432 tftpfile->off = offset;
433 break;
434 case SEEK_CUR:
435 tftpfile->off += offset;
436 break;
437 default:
438 errno = EOFFSET;
439 return (-1);
440 }
441 return (tftpfile->off);
442 }
443