1 /* $OpenBSD: tftp.c,v 1.7 2021/10/25 15:59:46 patrick 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 <lib/libkern/libkern.h> 55 56 #include "stand.h" 57 #include "net.h" 58 #include "netif.h" 59 60 #include "tftp.h" 61 62 extern struct in_addr servip; 63 64 static int tftpport = 2000; 65 66 #define RSPACE 520 /* max data packet, rounded up */ 67 68 struct tftp_handle { 69 struct iodesc *iodesc; 70 int currblock; /* contents of lastdata */ 71 int islastblock; /* flag */ 72 int validsize; 73 int off; 74 const char *path; /* saved for re-requests */ 75 struct { 76 struct packet_header header; 77 struct tftphdr t; 78 u_char space[RSPACE]; 79 } lastdata; 80 }; 81 82 static const int tftperrors[8] = { 83 0, /* ??? */ 84 ENOENT, 85 EPERM, 86 ENOSPC, 87 EINVAL, /* ??? */ 88 EINVAL, /* ??? */ 89 EEXIST, 90 EINVAL /* ??? */ 91 }; 92 93 ssize_t recvtftp(struct iodesc *, void *, size_t, time_t); 94 int tftp_makereq(struct tftp_handle *); 95 int tftp_getnextblock(struct tftp_handle *); 96 #ifndef TFTP_NOTERMINATE 97 void tftp_terminate(struct tftp_handle *); 98 #endif 99 100 ssize_t 101 recvtftp(struct iodesc *d, void *pkt, size_t len, time_t tleft) 102 { 103 ssize_t n; 104 struct tftphdr *t; 105 106 errno = 0; 107 108 n = readudp(d, pkt, len, tleft); 109 110 if (n < 4) 111 return -1; 112 113 t = (struct tftphdr *) pkt; 114 switch (ntohs(t->th_opcode)) { 115 case DATA: 116 if (htons(t->th_block) != d->xid) { 117 /* 118 * Expected block? 119 */ 120 return -1; 121 } 122 if (d->xid == 1) { 123 /* 124 * First data packet from new port. 125 */ 126 struct udphdr *uh; 127 uh = (struct udphdr *) pkt - 1; 128 d->destport = uh->uh_sport; 129 } /* else check uh_sport has not changed??? */ 130 return (n - (t->th_data - (char *)t)); 131 case ERROR: 132 if ((unsigned) ntohs(t->th_code) >= 8) { 133 printf("illegal tftp error %d\n", ntohs(t->th_code)); 134 errno = EIO; 135 } else { 136 #ifdef DEBUG 137 printf("tftp-error %d\n", ntohs(t->th_code)); 138 #endif 139 errno = tftperrors[ntohs(t->th_code)]; 140 } 141 return -1; 142 default: 143 #ifdef DEBUG 144 printf("tftp type %d not handled\n", ntohs(t->th_opcode)); 145 #endif 146 return -1; 147 } 148 } 149 150 /* send request, expect first block (or error) */ 151 int 152 tftp_makereq(struct tftp_handle *h) 153 { 154 struct { 155 struct packet_header header; 156 struct tftphdr t; 157 u_char space[FNAME_SIZE + 6]; 158 } wbuf; 159 char *wtail; 160 int l; 161 ssize_t res; 162 struct tftphdr *t; 163 164 bzero(&wbuf, sizeof(wbuf)); 165 166 wbuf.t.th_opcode = htons((u_short) RRQ); 167 wtail = wbuf.t.th_stuff; 168 l = strlen(h->path); 169 bcopy(h->path, wtail, l + 1); 170 wtail += l + 1; 171 bcopy("octet", wtail, 6); 172 wtail += 6; 173 174 t = &h->lastdata.t; 175 176 /* h->iodesc->myport = htons(--tftpport); */ 177 h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff)); 178 h->iodesc->destport = htons(IPPORT_TFTP); 179 h->iodesc->xid = 1; /* expected block */ 180 181 res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t, 182 recvtftp, t, sizeof(*t) + RSPACE); 183 184 if (res == -1) 185 return errno; 186 187 h->currblock = 1; 188 h->validsize = res; 189 h->islastblock = 0; 190 if (res < SEGSIZE) 191 h->islastblock = 1; /* very short file */ 192 return 0; 193 } 194 195 /* ack block, expect next */ 196 int 197 tftp_getnextblock(struct tftp_handle *h) 198 { 199 struct { 200 struct packet_header header; 201 struct tftphdr t; 202 } wbuf; 203 char *wtail; 204 int res; 205 struct tftphdr *t; 206 207 bzero(&wbuf, sizeof(wbuf)); 208 209 wbuf.t.th_opcode = htons((u_short) ACK); 210 wbuf.t.th_block = htons((u_short) h->currblock); 211 wtail = (char *) &wbuf.t.th_data; 212 213 t = &h->lastdata.t; 214 215 h->iodesc->xid = h->currblock + 1; /* expected block */ 216 217 res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t, 218 recvtftp, t, sizeof(*t) + RSPACE); 219 220 if (res == -1) /* 0 is OK! */ 221 return errno; 222 223 h->currblock++; 224 h->validsize = res; 225 if (res < SEGSIZE) 226 h->islastblock = 1; /* EOF */ 227 return 0; 228 } 229 230 #ifndef TFTP_NOTERMINATE 231 void 232 tftp_terminate(struct tftp_handle *h) 233 { 234 struct { 235 struct packet_header header; 236 struct tftphdr t; 237 } wbuf; 238 char *wtail; 239 240 bzero(&wbuf, sizeof(wbuf)); 241 wtail = (char *) &wbuf.t.th_data; 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 wtail++; /* ERROR data is a string, thus needs NUL. */ 250 } 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 if (offinblock > tftpfile->validsize) { 330 #ifdef DEBUG 331 printf("tftp: invalid offset %d\n", 332 tftpfile->off); 333 #endif 334 return EINVAL; 335 } 336 inbuffer = tftpfile->validsize - offinblock; 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 sb->st_mode = 0444; 387 sb->st_nlink = 1; 388 sb->st_uid = 0; 389 sb->st_gid = 0; 390 sb->st_size = -1; 391 392 return 0; 393 } 394 395 off_t 396 tftp_seek(struct open_file *f, off_t offset, int where) 397 { 398 struct tftp_handle *tftpfile; 399 tftpfile = (struct tftp_handle *) f->f_fsdata; 400 401 switch (where) { 402 case SEEK_SET: 403 tftpfile->off = offset; 404 break; 405 case SEEK_CUR: 406 tftpfile->off += offset; 407 break; 408 default: 409 errno = EOFFSET; 410 return -1; 411 } 412 413 return (tftpfile->off); 414 } 415 416 /* 417 * Not implemented. 418 */ 419 #ifndef NO_READDIR 420 int 421 tftp_readdir(struct open_file *f, char *name) 422 { 423 return EROFS; 424 } 425 #endif 426