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