]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - lib/libstand/tftp.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / lib / libstand / tftp.c
1 /*      $NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner 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 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 /*
38  * Simple TFTP implementation for libsa.
39  * Assumes:
40  *  - socket descriptor (int) at open_file->f_devdata
41  *  - server host IP in global servip
42  * Restrictions:
43  *  - read only
44  *  - lseek only with SEEK_SET or SEEK_CUR
45  *  - no big time differences between transfers (<tftp timeout)
46  */
47
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <netinet/in.h>
51 #include <netinet/udp.h>
52 #include <netinet/in_systm.h>
53 #include <arpa/tftp.h>
54
55 #include <string.h>
56
57 #include "stand.h"
58 #include "net.h"
59 #include "netif.h"
60
61 #include "tftp.h"
62
63 static int      tftp_open(const char *path, struct open_file *f);
64 static int      tftp_close(struct open_file *f);
65 static int      tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid);
66 static int      tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid);
67 static off_t    tftp_seek(struct open_file *f, off_t offset, int where);
68 static int      tftp_stat(struct open_file *f, struct stat *sb);
69
70 struct fs_ops tftp_fsops = {
71         "tftp",
72         tftp_open,
73         tftp_close,
74         tftp_read,
75         tftp_write,
76         tftp_seek,
77         tftp_stat,
78         null_readdir
79 };
80
81 extern struct in_addr servip;
82
83 static int      tftpport = 2000;
84
85 #define RSPACE 520              /* max data packet, rounded up */
86
87 struct tftp_handle {
88         struct iodesc  *iodesc;
89         int             currblock;      /* contents of lastdata */
90         int             islastblock;    /* flag */
91         int             validsize;
92         int             off;
93         char           *path;   /* saved for re-requests */
94         struct {
95                 u_char header[HEADER_SIZE];
96                 struct tftphdr t;
97                 u_char space[RSPACE];
98         } __packed __aligned(4) lastdata;
99 };
100
101 static const int tftperrors[8] = {
102         0,                      /* ??? */
103         ENOENT,
104         EPERM,
105         ENOSPC,
106         EINVAL,                 /* ??? */
107         EINVAL,                 /* ??? */
108         EEXIST,
109         EINVAL                  /* ??? */
110 };
111
112 static ssize_t 
113 recvtftp(d, pkt, len, tleft)
114         struct iodesc *d;
115         void  *pkt;
116         ssize_t len;
117         time_t          tleft;
118 {
119         struct tftphdr *t;
120
121         errno = 0;
122
123         len = readudp(d, pkt, len, tleft);
124
125         if (len < 4)
126                 return (-1);
127
128         t = (struct tftphdr *) pkt;
129         switch (ntohs(t->th_opcode)) {
130         case DATA: {
131                 int got;
132
133                 if (htons(t->th_block) != d->xid) {
134                         /*
135                          * Expected block?
136                          */
137                         return (-1);
138                 }
139                 if (d->xid == 1) {
140                         /*
141                          * First data packet from new port.
142                          */
143                         struct udphdr *uh;
144                         uh = (struct udphdr *) pkt - 1;
145                         d->destport = uh->uh_sport;
146                 } /* else check uh_sport has not changed??? */
147                 got = len - (t->th_data - (char *) t);
148                 return got;
149         }
150         case ERROR:
151                 if ((unsigned) ntohs(t->th_code) >= 8) {
152                         printf("illegal tftp error %d\n", ntohs(t->th_code));
153                         errno = EIO;
154                 } else {
155 #ifdef DEBUG
156                         printf("tftp-error %d\n", ntohs(t->th_code));
157 #endif
158                         errno = tftperrors[ntohs(t->th_code)];
159                 }
160                 return (-1);
161         default:
162 #ifdef DEBUG
163                 printf("tftp type %d not handled\n", ntohs(t->th_opcode));
164 #endif
165                 return (-1);
166         }
167 }
168
169 /* send request, expect first block (or error) */
170 static int 
171 tftp_makereq(h)
172         struct tftp_handle *h;
173 {
174         struct {
175                 u_char header[HEADER_SIZE];
176                 struct tftphdr  t;
177                 u_char space[FNAME_SIZE + 6];
178         } __packed __aligned(4) wbuf;
179         char           *wtail;
180         int             l;
181         ssize_t         res;
182         struct tftphdr *t;
183
184         wbuf.t.th_opcode = htons((u_short) RRQ);
185         wtail = wbuf.t.th_stuff;
186         l = strlen(h->path);
187         bcopy(h->path, wtail, l + 1);
188         wtail += l + 1;
189         bcopy("octet", wtail, 6);
190         wtail += 6;
191
192         t = &h->lastdata.t;
193
194         /* h->iodesc->myport = htons(--tftpport); */
195         h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
196         h->iodesc->destport = htons(IPPORT_TFTP);
197         h->iodesc->xid = 1;     /* expected block */
198
199         res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
200                        recvtftp, t, sizeof(*t) + RSPACE);
201
202         if (res == -1)
203                 return (errno);
204
205         h->currblock = 1;
206         h->validsize = res;
207         h->islastblock = 0;
208         if (res < SEGSIZE)
209                 h->islastblock = 1;     /* very short file */
210         return (0);
211 }
212
213 /* ack block, expect next */
214 static int 
215 tftp_getnextblock(h)
216         struct tftp_handle *h;
217 {
218         struct {
219                 u_char header[HEADER_SIZE];
220                 struct tftphdr t;
221         } __packed __aligned(4) wbuf;
222         char           *wtail;
223         int             res;
224         struct tftphdr *t;
225
226         wbuf.t.th_opcode = htons((u_short) ACK);
227         wtail = (char *) &wbuf.t.th_block;
228         wbuf.t.th_block = htons((u_short) h->currblock);
229         wtail += 2;
230
231         t = &h->lastdata.t;
232
233         h->iodesc->xid = h->currblock + 1;      /* expected block */
234
235         res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
236                        recvtftp, t, sizeof(*t) + RSPACE);
237
238         if (res == -1)          /* 0 is OK! */
239                 return (errno);
240
241         h->currblock++;
242         h->validsize = res;
243         if (res < SEGSIZE)
244                 h->islastblock = 1;     /* EOF */
245         return (0);
246 }
247
248 static int 
249 tftp_open(path, f)
250         const char *path;
251         struct open_file *f;
252 {
253         struct tftp_handle *tftpfile;
254         struct iodesc  *io;
255         int             res;
256
257 #ifndef __i386__
258         if (strcmp(f->f_dev->dv_name, "net") != 0)
259                 return (EINVAL);
260 #endif
261
262         tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile));
263         if (!tftpfile)
264                 return (ENOMEM);
265
266         tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
267         if (io == NULL)
268                 return (EINVAL);
269
270         io->destip = servip;
271         tftpfile->off = 0;
272         tftpfile->path = strdup(path);
273         if (tftpfile->path == NULL) {
274             free(tftpfile);
275             return(ENOMEM);
276         }
277
278         res = tftp_makereq(tftpfile);
279
280         if (res) {
281                 free(tftpfile->path);
282                 free(tftpfile);
283                 return (res);
284         }
285         f->f_fsdata = (void *) tftpfile;
286         return (0);
287 }
288
289 static int 
290 tftp_read(f, addr, size, resid)
291         struct open_file *f;
292         void           *addr;
293         size_t          size;
294         size_t         *resid;  /* out */
295 {
296         struct tftp_handle *tftpfile;
297         static int      tc = 0;
298         tftpfile = (struct tftp_handle *) f->f_fsdata;
299
300         while (size > 0) {
301                 int needblock, count;
302
303                 if (!(tc++ % 16))
304                         twiddle();
305
306                 needblock = tftpfile->off / SEGSIZE + 1;
307
308                 if (tftpfile->currblock > needblock)    /* seek backwards */
309                         tftp_makereq(tftpfile); /* no error check, it worked
310                                                  * for open */
311
312                 while (tftpfile->currblock < needblock) {
313                         int res;
314
315                         res = tftp_getnextblock(tftpfile);
316                         if (res) {      /* no answer */
317 #ifdef DEBUG
318                                 printf("tftp: read error\n");
319 #endif
320                                 return (res);
321                         }
322                         if (tftpfile->islastblock)
323                                 break;
324                 }
325
326                 if (tftpfile->currblock == needblock) {
327                         int offinblock, inbuffer;
328
329                         offinblock = tftpfile->off % SEGSIZE;
330
331                         inbuffer = tftpfile->validsize - offinblock;
332                         if (inbuffer < 0) {
333 #ifdef DEBUG
334                                 printf("tftp: invalid offset %d\n",
335                                     tftpfile->off);
336 #endif
337                                 return (EINVAL);
338                         }
339                         count = (size < inbuffer ? size : inbuffer);
340                         bcopy(tftpfile->lastdata.t.th_data + offinblock,
341                             addr, count);
342
343                         addr = (char *)addr + count;
344                         tftpfile->off += count;
345                         size -= count;
346
347                         if ((tftpfile->islastblock) && (count == inbuffer))
348                                 break;  /* EOF */
349                 } else {
350 #ifdef DEBUG
351                         printf("tftp: block %d not found\n", needblock);
352 #endif
353                         return (EINVAL);
354                 }
355
356         }
357
358         if (resid)
359                 *resid = size;
360         return (0);
361 }
362
363 static int 
364 tftp_close(f)
365         struct open_file *f;
366 {
367         struct tftp_handle *tftpfile;
368         tftpfile = (struct tftp_handle *) f->f_fsdata;
369
370         /* let it time out ... */
371
372         if (tftpfile) {
373                 free(tftpfile->path);
374                 free(tftpfile);
375         }
376         return (0);
377 }
378
379 static int 
380 tftp_write(f, start, size, resid)
381         struct open_file *f;
382         void           *start;
383         size_t          size;
384         size_t         *resid;  /* out */
385 {
386         return (EROFS);
387 }
388
389 static int 
390 tftp_stat(f, sb)
391         struct open_file *f;
392         struct stat    *sb;
393 {
394         struct tftp_handle *tftpfile;
395         tftpfile = (struct tftp_handle *) f->f_fsdata;
396
397         sb->st_mode = 0444 | S_IFREG;
398         sb->st_nlink = 1;
399         sb->st_uid = 0;
400         sb->st_gid = 0;
401         sb->st_size = -1;
402         return (0);
403 }
404
405 static off_t 
406 tftp_seek(f, offset, where)
407         struct open_file *f;
408         off_t           offset;
409         int             where;
410 {
411         struct tftp_handle *tftpfile;
412         tftpfile = (struct tftp_handle *) f->f_fsdata;
413
414         switch (where) {
415         case SEEK_SET:
416                 tftpfile->off = offset;
417                 break;
418         case SEEK_CUR:
419                 tftpfile->off += offset;
420                 break;
421         default:
422                 errno = EOFFSET;
423                 return (-1);
424         }
425         return (tftpfile->off);
426 }