1 /* $NetBSD: tftp.c,v 1.24 2007/12/02 04:59:26 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 __P((struct iodesc *, void *, size_t, time_t)); 94 static int tftp_makereq __P((struct tftp_handle *)); 95 static int tftp_getnextblock __P((struct tftp_handle *)); 96 #ifndef TFTP_NOTERMINATE 97 static void tftp_terminate __P((struct tftp_handle *)); 98 #endif 99 100 static 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 int)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 static int 152 tftp_makereq(struct tftp_handle *h) 153 { 154 struct { 155 u_char header[HEADER_SIZE]; 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 wbuf.t.th_opcode = htons((u_short)RRQ); 165 wtail = wbuf.t.th_stuff; 166 l = strlen(h->path); 167 bcopy(h->path, wtail, l + 1); 168 wtail += l + 1; 169 bcopy("octet", wtail, 6); 170 wtail += 6; 171 172 t = &h->lastdata.t; 173 174 /* h->iodesc->myport = htons(--tftpport); */ 175 h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff)); 176 h->iodesc->destport = htons(IPPORT_TFTP); 177 h->iodesc->xid = 1; /* expected block */ 178 179 res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t, 180 recvtftp, t, sizeof(*t) + RSPACE); 181 182 if (res == -1) 183 return errno; 184 185 h->currblock = 1; 186 h->validsize = res; 187 h->islastblock = 0; 188 if (res < SEGSIZE) 189 h->islastblock = 1; /* very short file */ 190 return 0; 191 } 192 193 /* ack block, expect next */ 194 static int 195 tftp_getnextblock(struct tftp_handle *h) 196 { 197 struct { 198 u_char header[HEADER_SIZE]; 199 struct tftphdr t; 200 } wbuf; 201 char *wtail; 202 int res; 203 struct tftphdr *t; 204 205 wbuf.t.th_opcode = htons((u_short)ACK); 206 wbuf.t.th_block = htons((u_short)h->currblock); 207 wtail = (char *)&wbuf.t.th_data; 208 209 t = &h->lastdata.t; 210 211 h->iodesc->xid = h->currblock + 1; /* expected block */ 212 213 res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t, 214 recvtftp, t, sizeof(*t) + RSPACE); 215 216 if (res == -1) /* 0 is OK! */ 217 return errno; 218 219 h->currblock++; 220 h->validsize = res; 221 if (res < SEGSIZE) 222 h->islastblock = 1; /* EOF */ 223 return 0; 224 } 225 226 #ifndef TFTP_NOTERMINATE 227 static void 228 tftp_terminate(struct tftp_handle *h) 229 { 230 struct { 231 u_char header[HEADER_SIZE]; 232 struct tftphdr t; 233 } wbuf; 234 char *wtail; 235 236 if (h->islastblock) { 237 wbuf.t.th_opcode = htons((u_short)ACK); 238 wbuf.t.th_block = htons((u_short)h->currblock); 239 } else { 240 wbuf.t.th_opcode = htons((u_short)ERROR); 241 wbuf.t.th_code = htons((u_short)ENOSPACE); /* ??? */ 242 } 243 wtail = (char *)&wbuf.t.th_data; 244 245 (void)sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); 246 } 247 #endif 248 249 int 250 tftp_open(const char *path, struct open_file *f) 251 { 252 struct tftp_handle *tftpfile; 253 struct iodesc *io; 254 int res; 255 256 tftpfile = (struct tftp_handle *)alloc(sizeof(*tftpfile)); 257 if (!tftpfile) 258 return ENOMEM; 259 260 tftpfile->iodesc = io = socktodesc(*(int *)(f->f_devdata)); 261 io->destip = servip; 262 tftpfile->off = 0; 263 tftpfile->path = path; /* XXXXXXX we hope it's static */ 264 265 res = tftp_makereq(tftpfile); 266 267 if (res) { 268 dealloc(tftpfile, sizeof(*tftpfile)); 269 return res; 270 } 271 f->f_fsdata = (void *)tftpfile; 272 return 0; 273 } 274 275 int 276 tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid) 277 { 278 struct tftp_handle *tftpfile; 279 #if !defined(LIBSA_NO_TWIDDLE) 280 static int tc = 0; 281 #endif 282 tftpfile = (struct tftp_handle *)f->f_fsdata; 283 284 while (size > 0) { 285 int needblock; 286 size_t count; 287 288 #if !defined(LIBSA_NO_TWIDDLE) 289 if (!(tc++ % 16)) 290 twiddle(); 291 #endif 292 293 needblock = tftpfile->off / SEGSIZE + 1; 294 295 if (tftpfile->currblock > needblock) { /* seek backwards */ 296 #ifndef TFTP_NOTERMINATE 297 tftp_terminate(tftpfile); 298 #endif 299 tftp_makereq(tftpfile); /* no error check, it worked 300 * for open */ 301 } 302 303 while (tftpfile->currblock < needblock) { 304 int res; 305 306 res = tftp_getnextblock(tftpfile); 307 if (res) { /* no answer */ 308 #ifdef DEBUG 309 printf("tftp: read error (block %d->%d)\n", 310 tftpfile->currblock, needblock); 311 #endif 312 return res; 313 } 314 if (tftpfile->islastblock) 315 break; 316 } 317 318 if (tftpfile->currblock == needblock) { 319 size_t offinblock, inbuffer; 320 321 offinblock = tftpfile->off % SEGSIZE; 322 323 inbuffer = tftpfile->validsize - offinblock; 324 if (inbuffer < 0) { 325 #ifdef DEBUG 326 printf("tftp: invalid offset %d\n", 327 tftpfile->off); 328 #endif 329 return EINVAL; 330 } 331 count = (size < inbuffer ? size : inbuffer); 332 bcopy(tftpfile->lastdata.t.th_data + offinblock, 333 addr, count); 334 335 addr = (char *)addr + count; 336 tftpfile->off += count; 337 size -= count; 338 339 if ((tftpfile->islastblock) && (count == inbuffer)) 340 break; /* EOF */ 341 } else { 342 #ifdef DEBUG 343 printf("tftp: block %d not found\n", needblock); 344 #endif 345 return EINVAL; 346 } 347 348 } 349 350 if (resid) 351 *resid = size; 352 return 0; 353 } 354 355 int 356 tftp_close(struct open_file *f) 357 { 358 struct tftp_handle *tftpfile; 359 tftpfile = (struct tftp_handle *)f->f_fsdata; 360 361 #ifdef TFTP_NOTERMINATE 362 /* let it time out ... */ 363 #else 364 tftp_terminate(tftpfile); 365 #endif 366 367 dealloc(tftpfile, sizeof(*tftpfile)); 368 return 0; 369 } 370 371 int 372 tftp_write(struct open_file *f, void *start, size_t size, size_t *resid) 373 { 374 375 return EROFS; 376 } 377 378 int 379 tftp_stat(struct open_file *f, struct stat *sb) 380 { 381 struct tftp_handle *tftpfile; 382 tftpfile = (struct tftp_handle *)f->f_fsdata; 383 384 sb->st_mode = 0444; 385 sb->st_nlink = 1; 386 sb->st_uid = 0; 387 sb->st_gid = 0; 388 sb->st_size = -1; 389 return 0; 390 } 391 392 off_t 393 tftp_seek(struct open_file *f, off_t offset, int where) 394 { 395 struct tftp_handle *tftpfile; 396 tftpfile = (struct tftp_handle *)f->f_fsdata; 397 398 switch (where) { 399 case SEEK_SET: 400 tftpfile->off = offset; 401 break; 402 case SEEK_CUR: 403 tftpfile->off += offset; 404 break; 405 default: 406 errno = EOFFSET; 407 return -1; 408 } 409 return tftpfile->off; 410 } 411