1 /* $NetBSD: tftp.c,v 1.29 2009/01/17 14:00:36 tsutsui Exp $ */ 2 3 /* 4 * Copyright (c) 1996 5 * Matthias Drochner. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 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 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 u_char header[HEADER_SIZE]; 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 static ssize_t recvtftp(struct iodesc *, void *, size_t, saseconds_t); 94 static int tftp_makereq(struct tftp_handle *); 95 static int tftp_getnextblock(struct tftp_handle *); 96 #ifndef TFTP_NOTERMINATE 97 static void tftp_terminate(struct tftp_handle *); 98 #endif 99 static ssize_t tftp_size_of_file(struct tftp_handle *tftpfile); 100 101 static ssize_t 102 recvtftp(struct iodesc *d, void *pkt, size_t len, saseconds_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 int)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 static 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 wbuf.t.th_opcode = htons((u_short)RRQ); 166 wtail = wbuf.t.th_stuff; 167 l = strlen(h->path); 168 (void)memcpy(wtail, h->path, l + 1); 169 wtail += l + 1; 170 (void)memcpy(wtail, "octet", 6); 171 wtail += 6; 172 173 t = &h->lastdata.t; 174 175 /* h->iodesc->myport = htons(--tftpport); */ 176 h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff)); 177 h->iodesc->destport = htons(IPPORT_TFTP); 178 h->iodesc->xid = 1; /* expected block */ 179 180 res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t, 181 recvtftp, t, sizeof(*t) + RSPACE); 182 183 if (res == -1) 184 return errno; 185 186 h->currblock = 1; 187 h->validsize = res; 188 h->islastblock = 0; 189 if (res < SEGSIZE) 190 h->islastblock = 1; /* very short file */ 191 return 0; 192 } 193 194 /* ack block, expect next */ 195 static int 196 tftp_getnextblock(struct tftp_handle *h) 197 { 198 struct { 199 u_char header[HEADER_SIZE]; 200 struct tftphdr t; 201 } wbuf; 202 char *wtail; 203 int res; 204 struct tftphdr *t; 205 206 wbuf.t.th_opcode = htons((u_short)ACK); 207 wbuf.t.th_block = htons((u_short)h->currblock); 208 wtail = (char *)&wbuf.t.th_data; 209 210 t = &h->lastdata.t; 211 212 h->iodesc->xid = h->currblock + 1; /* expected block */ 213 214 res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t, 215 recvtftp, t, sizeof(*t) + RSPACE); 216 217 if (res == -1) /* 0 is OK! */ 218 return errno; 219 220 h->currblock++; 221 h->validsize = res; 222 if (res < SEGSIZE) 223 h->islastblock = 1; /* EOF */ 224 return 0; 225 } 226 227 #ifndef TFTP_NOTERMINATE 228 static void 229 tftp_terminate(struct tftp_handle *h) 230 { 231 struct { 232 u_char header[HEADER_SIZE]; 233 struct tftphdr t; 234 } wbuf; 235 char *wtail; 236 237 if (h->islastblock) { 238 wbuf.t.th_opcode = htons((u_short)ACK); 239 wbuf.t.th_block = htons((u_short)h->currblock); 240 } else { 241 wbuf.t.th_opcode = htons((u_short)ERROR); 242 wbuf.t.th_code = htons((u_short)ENOSPACE); /* ??? */ 243 } 244 wtail = (char *)&wbuf.t.th_data; 245 246 (void)sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); 247 } 248 #endif 249 250 int 251 tftp_open(const char *path, struct open_file *f) 252 { 253 struct tftp_handle *tftpfile; 254 struct iodesc *io; 255 int res; 256 257 tftpfile = (struct tftp_handle *)alloc(sizeof(*tftpfile)); 258 if (!tftpfile) 259 return ENOMEM; 260 261 tftpfile->iodesc = io = socktodesc(*(int *)(f->f_devdata)); 262 io->destip = servip; 263 tftpfile->off = 0; 264 tftpfile->path = path; /* XXXXXXX we hope it's static */ 265 266 res = tftp_makereq(tftpfile); 267 268 if (res) { 269 dealloc(tftpfile, sizeof(*tftpfile)); 270 return res; 271 } 272 f->f_fsdata = (void *)tftpfile; 273 fsmod = "nfs"; 274 return 0; 275 } 276 277 int 278 tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid) 279 { 280 struct tftp_handle *tftpfile; 281 #if !defined(LIBSA_NO_TWIDDLE) 282 static int tc = 0; 283 #endif 284 tftpfile = (struct tftp_handle *)f->f_fsdata; 285 286 while (size > 0) { 287 int needblock; 288 size_t count; 289 290 #if !defined(LIBSA_NO_TWIDDLE) 291 if (!(tc++ % 16)) 292 twiddle(); 293 #endif 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 tftp_makereq(tftpfile); /* no error check, it worked 302 * for open */ 303 } 304 305 while (tftpfile->currblock < needblock) { 306 int res; 307 308 res = tftp_getnextblock(tftpfile); 309 if (res) { /* no answer */ 310 #ifdef DEBUG 311 printf("tftp: read error (block %d->%d)\n", 312 tftpfile->currblock, needblock); 313 #endif 314 return res; 315 } 316 if (tftpfile->islastblock) 317 break; 318 } 319 320 if (tftpfile->currblock == needblock) { 321 size_t offinblock, inbuffer; 322 323 offinblock = tftpfile->off % SEGSIZE; 324 325 if (offinblock > tftpfile->validsize) { 326 #ifdef DEBUG 327 printf("tftp: invalid offset %d\n", 328 tftpfile->off); 329 #endif 330 return EINVAL; 331 } 332 inbuffer = tftpfile->validsize - offinblock; 333 count = (size < inbuffer ? size : inbuffer); 334 (void)memcpy(addr, 335 tftpfile->lastdata.t.th_data + offinblock, 336 count); 337 338 addr = (char *)addr + count; 339 tftpfile->off += count; 340 size -= count; 341 342 if ((tftpfile->islastblock) && (count == inbuffer)) 343 break; /* EOF */ 344 } else { 345 #ifdef DEBUG 346 printf("tftp: block %d not found\n", needblock); 347 #endif 348 return EINVAL; 349 } 350 351 } 352 353 if (resid) 354 *resid = size; 355 return 0; 356 } 357 358 int 359 tftp_close(struct open_file *f) 360 { 361 struct tftp_handle *tftpfile; 362 tftpfile = (struct tftp_handle *)f->f_fsdata; 363 364 #ifdef TFTP_NOTERMINATE 365 /* let it time out ... */ 366 #else 367 tftp_terminate(tftpfile); 368 #endif 369 370 dealloc(tftpfile, sizeof(*tftpfile)); 371 return 0; 372 } 373 374 int 375 tftp_write(struct open_file *f, void *start, size_t size, size_t *resid) 376 { 377 378 return EROFS; 379 } 380 381 static ssize_t 382 tftp_size_of_file(struct tftp_handle *tftpfile) 383 { 384 ssize_t filesize; 385 386 if (tftpfile->currblock > 1) { /* move to start of file */ 387 #ifndef TFTP_NOTERMINATE 388 tftp_terminate(tftpfile); 389 #endif 390 tftp_makereq(tftpfile); /* no error check, it worked 391 * for open */ 392 } 393 394 /* start with the size of block 1 */ 395 filesize = tftpfile->validsize; 396 397 /* and keep adding the sizes till we hit the last block */ 398 while (!tftpfile->islastblock) { 399 int res; 400 401 res = tftp_getnextblock(tftpfile); 402 if (res) { /* no answer */ 403 #ifdef DEBUG 404 printf("tftp: read error (block %d)\n", 405 tftpfile->currblock); 406 #endif 407 return -1; 408 } 409 filesize += tftpfile->validsize; 410 } 411 #ifdef DEBUG 412 printf("tftp_size_of_file: file is %d bytes\n", filesize); 413 #endif 414 return filesize; 415 } 416 417 int 418 tftp_stat(struct open_file *f, struct stat *sb) 419 { 420 struct tftp_handle *tftpfile; 421 tftpfile = (struct tftp_handle *)f->f_fsdata; 422 423 sb->st_mode = 0444; 424 sb->st_nlink = 1; 425 sb->st_uid = 0; 426 sb->st_gid = 0; 427 sb->st_size = tftp_size_of_file(tftpfile); 428 return 0; 429 } 430 431 off_t 432 tftp_seek(struct open_file *f, off_t offset, int where) 433 { 434 struct tftp_handle *tftpfile; 435 tftpfile = (struct tftp_handle *)f->f_fsdata; 436 437 switch (where) { 438 case SEEK_SET: 439 tftpfile->off = offset; 440 break; 441 case SEEK_CUR: 442 tftpfile->off += offset; 443 break; 444 default: 445 errno = EOFFSET; 446 return -1; 447 } 448 return tftpfile->off; 449 } 450