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 429