1 /* $OpenBSD: tftp.c,v 1.3 2009/03/02 00:00:56 krw 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 wtail = (char *) &wbuf.t.th_data; 243 244 if (h->islastblock) { 245 wbuf.t.th_opcode = htons((u_short) ACK); 246 wbuf.t.th_block = htons((u_short) h->currblock); 247 } else { 248 wbuf.t.th_opcode = htons((u_short) ERROR); 249 wbuf.t.th_code = htons((u_short) ENOSPACE); /* ??? */ 250 wtail++; /* ERROR data is a string, thus needs NUL. */ 251 } 252 253 (void) sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t); 254 } 255 #endif 256 257 int 258 tftp_open(char *path, struct open_file *f) 259 { 260 struct tftp_handle *tftpfile; 261 struct iodesc *io; 262 int res; 263 264 tftpfile = (struct tftp_handle *) alloc(sizeof(*tftpfile)); 265 if (tftpfile == NULL) 266 return ENOMEM; 267 268 tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata)); 269 io->destip = servip; 270 tftpfile->off = 0; 271 tftpfile->path = path; /* XXXXXXX we hope it's static */ 272 273 res = tftp_makereq(tftpfile); 274 275 if (res) { 276 free(tftpfile, sizeof(*tftpfile)); 277 return res; 278 } 279 f->f_fsdata = (void *) tftpfile; 280 return 0; 281 } 282 283 int 284 tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid) 285 { 286 struct tftp_handle *tftpfile; 287 #if !defined(LIBSA_NO_TWIDDLE) 288 static int tc = 0; 289 #endif 290 tftpfile = (struct tftp_handle *) f->f_fsdata; 291 292 while (size > 0) { 293 int needblock; 294 size_t count; 295 296 needblock = tftpfile->off / SEGSIZE + 1; 297 298 if (tftpfile->currblock > needblock) { /* seek backwards */ 299 #ifndef TFTP_NOTERMINATE 300 tftp_terminate(tftpfile); 301 #endif 302 /* Don't bother to check retval: it worked for open() */ 303 tftp_makereq(tftpfile); 304 } 305 306 while (tftpfile->currblock < needblock) { 307 int res; 308 309 #if !defined(LIBSA_NO_TWIDDLE) 310 if ((tc++ % 16) == 0) 311 twiddle(); 312 #endif 313 res = tftp_getnextblock(tftpfile); 314 if (res) { /* no answer */ 315 #ifdef DEBUG 316 printf("tftp: read error (block %d->%d)\n", 317 tftpfile->currblock, needblock); 318 #endif 319 return res; 320 } 321 if (tftpfile->islastblock) 322 break; 323 } 324 325 if (tftpfile->currblock == needblock) { 326 size_t offinblock, inbuffer; 327 328 offinblock = tftpfile->off % SEGSIZE; 329 330 inbuffer = tftpfile->validsize - offinblock; 331 if (inbuffer < 0) { 332 #ifdef DEBUG 333 printf("tftp: invalid offset %d\n", 334 tftpfile->off); 335 #endif 336 return EINVAL; 337 } 338 count = (size < inbuffer ? size : inbuffer); 339 bcopy(tftpfile->lastdata.t.th_data + offinblock, 340 addr, count); 341 342 addr = (caddr_t)addr + count; 343 tftpfile->off += count; 344 size -= count; 345 346 if ((tftpfile->islastblock) && (count == inbuffer)) 347 break; /* EOF */ 348 } else { 349 #ifdef DEBUG 350 printf("tftp: block %d not found\n", needblock); 351 #endif 352 return EINVAL; 353 } 354 355 } 356 357 if (resid != NULL) 358 *resid = size; 359 return 0; 360 } 361 362 int 363 tftp_close(struct open_file *f) 364 { 365 struct tftp_handle *tftpfile; 366 tftpfile = (struct tftp_handle *) f->f_fsdata; 367 368 #ifdef TFTP_NOTERMINATE 369 /* let it time out ... */ 370 #else 371 tftp_terminate(tftpfile); 372 #endif 373 374 free(tftpfile, sizeof(*tftpfile)); 375 return 0; 376 } 377 378 int 379 tftp_write(struct open_file *f, void *start, size_t size, size_t *resid) 380 { 381 return EROFS; 382 } 383 384 int 385 tftp_stat(struct open_file *f, struct stat *sb) 386 { 387 struct tftp_handle *tftpfile; 388 tftpfile = (struct tftp_handle *) f->f_fsdata; 389 390 sb->st_mode = 0444; 391 sb->st_nlink = 1; 392 sb->st_uid = 0; 393 sb->st_gid = 0; 394 sb->st_size = -1; 395 396 return 0; 397 } 398 399 off_t 400 tftp_seek(struct open_file *f, off_t offset, int where) 401 { 402 struct tftp_handle *tftpfile; 403 tftpfile = (struct tftp_handle *) f->f_fsdata; 404 405 switch (where) { 406 case SEEK_SET: 407 tftpfile->off = offset; 408 break; 409 case SEEK_CUR: 410 tftpfile->off += offset; 411 break; 412 default: 413 errno = EOFFSET; 414 return -1; 415 } 416 417 return (tftpfile->off); 418 } 419 420 /* 421 * Not implemented. 422 */ 423 #ifndef NO_READDIR 424 int 425 tftp_readdir(struct open_file *f, char *name) 426 { 427 return EROFS; 428 } 429 #endif 430