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