|
|
1.1 ! root 1: /* ! 2: * Copyright (C) 2006 Michael Brown <[email protected]>. ! 3: * ! 4: * This program is free software; you can redistribute it and/or ! 5: * modify it under the terms of the GNU General Public License as ! 6: * published by the Free Software Foundation; either version 2 of the ! 7: * License, or any later version. ! 8: * ! 9: * This program is distributed in the hope that it will be useful, but ! 10: * WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 12: * General Public License for more details. ! 13: * ! 14: * You should have received a copy of the GNU General Public License ! 15: * along with this program; if not, write to the Free Software ! 16: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ! 17: */ ! 18: ! 19: FILE_LICENCE ( GPL2_OR_LATER ); ! 20: ! 21: #include <stdint.h> ! 22: #include <stdlib.h> ! 23: #include <stdio.h> ! 24: #include <string.h> ! 25: #include <strings.h> ! 26: #include <byteswap.h> ! 27: #include <errno.h> ! 28: #include <assert.h> ! 29: #include <ipxe/refcnt.h> ! 30: #include <ipxe/iobuf.h> ! 31: #include <ipxe/xfer.h> ! 32: #include <ipxe/open.h> ! 33: #include <ipxe/uri.h> ! 34: #include <ipxe/tcpip.h> ! 35: #include <ipxe/retry.h> ! 36: #include <ipxe/features.h> ! 37: #include <ipxe/bitmap.h> ! 38: #include <ipxe/settings.h> ! 39: #include <ipxe/dhcp.h> ! 40: #include <ipxe/uri.h> ! 41: #include <ipxe/tftp.h> ! 42: ! 43: /** @file ! 44: * ! 45: * TFTP protocol ! 46: * ! 47: */ ! 48: ! 49: FEATURE ( FEATURE_PROTOCOL, "TFTP", DHCP_EB_FEATURE_TFTP, 1 ); ! 50: ! 51: /* TFTP-specific error codes */ ! 52: #define EINVAL_BLKSIZE __einfo_error ( EINFO_EINVAL_BLKSIZE ) ! 53: #define EINFO_EINVAL_BLKSIZE __einfo_uniqify \ ! 54: ( EINFO_EINVAL, 0x01, "Invalid blksize" ) ! 55: #define EINVAL_TSIZE __einfo_error ( EINFO_EINVAL_TSIZE ) ! 56: #define EINFO_EINVAL_TSIZE __einfo_uniqify \ ! 57: ( EINFO_EINVAL, 0x02, "Invalid tsize" ) ! 58: #define EINVAL_MC_NO_PORT __einfo_error ( EINFO_EINVAL_MC_NO_PORT ) ! 59: #define EINFO_EINVAL_MC_NO_PORT __einfo_uniqify \ ! 60: ( EINFO_EINVAL, 0x03, "Missing multicast port" ) ! 61: #define EINVAL_MC_NO_MC __einfo_error ( EINFO_EINVAL_MC_NO_MC ) ! 62: #define EINFO_EINVAL_MC_NO_MC __einfo_uniqify \ ! 63: ( EINFO_EINVAL, 0x04, "Missing multicast mc" ) ! 64: #define EINVAL_MC_INVALID_MC __einfo_error ( EINFO_EINVAL_MC_INVALID_MC ) ! 65: #define EINFO_EINVAL_MC_INVALID_MC __einfo_uniqify \ ! 66: ( EINFO_EINVAL, 0x05, "Missing multicast IP" ) ! 67: #define EINVAL_MC_INVALID_IP __einfo_error ( EINFO_EINVAL_MC_INVALID_IP ) ! 68: #define EINFO_EINVAL_MC_INVALID_IP __einfo_uniqify \ ! 69: ( EINFO_EINVAL, 0x06, "Invalid multicast IP" ) ! 70: #define EINVAL_MC_INVALID_PORT __einfo_error ( EINFO_EINVAL_MC_INVALID_PORT ) ! 71: #define EINFO_EINVAL_MC_INVALID_PORT __einfo_uniqify \ ! 72: ( EINFO_EINVAL, 0x07, "Invalid multicast port" ) ! 73: ! 74: /** ! 75: * A TFTP request ! 76: * ! 77: * This data structure holds the state for an ongoing TFTP transfer. ! 78: */ ! 79: struct tftp_request { ! 80: /** Reference count */ ! 81: struct refcnt refcnt; ! 82: /** Data transfer interface */ ! 83: struct interface xfer; ! 84: ! 85: /** URI being fetched */ ! 86: struct uri *uri; ! 87: /** Transport layer interface */ ! 88: struct interface socket; ! 89: /** Multicast transport layer interface */ ! 90: struct interface mc_socket; ! 91: ! 92: /** Data block size ! 93: * ! 94: * This is the "blksize" option negotiated with the TFTP ! 95: * server. (If the TFTP server does not support TFTP options, ! 96: * this will default to 512). ! 97: */ ! 98: unsigned int blksize; ! 99: /** File size ! 100: * ! 101: * This is the value returned in the "tsize" option from the ! 102: * TFTP server. If the TFTP server does not support the ! 103: * "tsize" option, this value will be zero. ! 104: */ ! 105: unsigned long tsize; ! 106: ! 107: /** Server port ! 108: * ! 109: * This is the port to which RRQ packets are sent. ! 110: */ ! 111: unsigned int port; ! 112: /** Peer address ! 113: * ! 114: * The peer address is determined by the first response ! 115: * received to the TFTP RRQ. ! 116: */ ! 117: struct sockaddr_tcpip peer; ! 118: /** Request flags */ ! 119: unsigned int flags; ! 120: /** MTFTP timeout count */ ! 121: unsigned int mtftp_timeouts; ! 122: ! 123: /** Block bitmap */ ! 124: struct bitmap bitmap; ! 125: /** Maximum known length ! 126: * ! 127: * We don't always know the file length in advance. In ! 128: * particular, if the TFTP server doesn't support the tsize ! 129: * option, or we are using MTFTP, then we don't know the file ! 130: * length until we see the end-of-file block (which, in the ! 131: * case of MTFTP, may not be the last block we see). ! 132: * ! 133: * This value is updated whenever we obtain information about ! 134: * the file length. ! 135: */ ! 136: size_t filesize; ! 137: /** Retransmission timer */ ! 138: struct retry_timer timer; ! 139: }; ! 140: ! 141: /** TFTP request flags */ ! 142: enum { ! 143: /** Send ACK packets */ ! 144: TFTP_FL_SEND_ACK = 0x0001, ! 145: /** Request blksize and tsize options */ ! 146: TFTP_FL_RRQ_SIZES = 0x0002, ! 147: /** Request multicast option */ ! 148: TFTP_FL_RRQ_MULTICAST = 0x0004, ! 149: /** Perform MTFTP recovery on timeout */ ! 150: TFTP_FL_MTFTP_RECOVERY = 0x0008, ! 151: /** Only get filesize and then abort the transfer */ ! 152: TFTP_FL_SIZEONLY = 0x0010, ! 153: }; ! 154: ! 155: /** Maximum number of MTFTP open requests before falling back to TFTP */ ! 156: #define MTFTP_MAX_TIMEOUTS 3 ! 157: ! 158: /** ! 159: * Free TFTP request ! 160: * ! 161: * @v refcnt Reference counter ! 162: */ ! 163: static void tftp_free ( struct refcnt *refcnt ) { ! 164: struct tftp_request *tftp = ! 165: container_of ( refcnt, struct tftp_request, refcnt ); ! 166: ! 167: uri_put ( tftp->uri ); ! 168: bitmap_free ( &tftp->bitmap ); ! 169: free ( tftp ); ! 170: } ! 171: ! 172: /** ! 173: * Mark TFTP request as complete ! 174: * ! 175: * @v tftp TFTP connection ! 176: * @v rc Return status code ! 177: */ ! 178: static void tftp_done ( struct tftp_request *tftp, int rc ) { ! 179: ! 180: DBGC ( tftp, "TFTP %p finished with status %d (%s)\n", ! 181: tftp, rc, strerror ( rc ) ); ! 182: ! 183: /* Stop the retry timer */ ! 184: stop_timer ( &tftp->timer ); ! 185: ! 186: /* Close all data transfer interfaces */ ! 187: intf_shutdown ( &tftp->socket, rc ); ! 188: intf_shutdown ( &tftp->mc_socket, rc ); ! 189: intf_shutdown ( &tftp->xfer, rc ); ! 190: } ! 191: ! 192: /** ! 193: * Reopen TFTP socket ! 194: * ! 195: * @v tftp TFTP connection ! 196: * @ret rc Return status code ! 197: */ ! 198: static int tftp_reopen ( struct tftp_request *tftp ) { ! 199: struct sockaddr_tcpip server; ! 200: int rc; ! 201: ! 202: /* Close socket */ ! 203: intf_restart ( &tftp->socket, 0 ); ! 204: ! 205: /* Disable ACK sending. */ ! 206: tftp->flags &= ~TFTP_FL_SEND_ACK; ! 207: ! 208: /* Reset peer address */ ! 209: memset ( &tftp->peer, 0, sizeof ( tftp->peer ) ); ! 210: ! 211: /* Open socket */ ! 212: memset ( &server, 0, sizeof ( server ) ); ! 213: server.st_port = htons ( tftp->port ); ! 214: if ( ( rc = xfer_open_named_socket ( &tftp->socket, SOCK_DGRAM, ! 215: ( struct sockaddr * ) &server, ! 216: tftp->uri->host, NULL ) ) != 0 ) { ! 217: DBGC ( tftp, "TFTP %p could not open socket: %s\n", ! 218: tftp, strerror ( rc ) ); ! 219: return rc; ! 220: } ! 221: ! 222: return 0; ! 223: } ! 224: ! 225: /** ! 226: * Reopen TFTP multicast socket ! 227: * ! 228: * @v tftp TFTP connection ! 229: * @v local Local socket address ! 230: * @ret rc Return status code ! 231: */ ! 232: static int tftp_reopen_mc ( struct tftp_request *tftp, ! 233: struct sockaddr *local ) { ! 234: int rc; ! 235: ! 236: /* Close multicast socket */ ! 237: intf_restart ( &tftp->mc_socket, 0 ); ! 238: ! 239: /* Open multicast socket. We never send via this socket, so ! 240: * use the local address as the peer address (since the peer ! 241: * address cannot be NULL). ! 242: */ ! 243: if ( ( rc = xfer_open_socket ( &tftp->mc_socket, SOCK_DGRAM, ! 244: local, local ) ) != 0 ) { ! 245: DBGC ( tftp, "TFTP %p could not open multicast " ! 246: "socket: %s\n", tftp, strerror ( rc ) ); ! 247: return rc; ! 248: } ! 249: ! 250: return 0; ! 251: } ! 252: ! 253: /** ! 254: * Presize TFTP receive buffers and block bitmap ! 255: * ! 256: * @v tftp TFTP connection ! 257: * @v filesize Known minimum file size ! 258: * @ret rc Return status code ! 259: */ ! 260: static int tftp_presize ( struct tftp_request *tftp, size_t filesize ) { ! 261: unsigned int num_blocks; ! 262: int rc; ! 263: ! 264: /* Do nothing if we are already large enough */ ! 265: if ( filesize <= tftp->filesize ) ! 266: return 0; ! 267: ! 268: /* Record filesize */ ! 269: tftp->filesize = filesize; ! 270: ! 271: /* Notify recipient of file size */ ! 272: xfer_seek ( &tftp->xfer, filesize ); ! 273: xfer_seek ( &tftp->xfer, 0 ); ! 274: ! 275: /* Calculate expected number of blocks. Note that files whose ! 276: * length is an exact multiple of the blocksize will have a ! 277: * trailing zero-length block, which must be included. ! 278: */ ! 279: num_blocks = ( ( filesize / tftp->blksize ) + 1 ); ! 280: if ( ( rc = bitmap_resize ( &tftp->bitmap, num_blocks ) ) != 0 ) { ! 281: DBGC ( tftp, "TFTP %p could not resize bitmap to %d blocks: " ! 282: "%s\n", tftp, num_blocks, strerror ( rc ) ); ! 283: return rc; ! 284: } ! 285: ! 286: return 0; ! 287: } ! 288: ! 289: /** ! 290: * TFTP requested blocksize ! 291: * ! 292: * This is treated as a global configuration parameter. ! 293: */ ! 294: static unsigned int tftp_request_blksize = TFTP_MAX_BLKSIZE; ! 295: ! 296: /** ! 297: * Set TFTP request blocksize ! 298: * ! 299: * @v blksize Requested block size ! 300: */ ! 301: void tftp_set_request_blksize ( unsigned int blksize ) { ! 302: if ( blksize < TFTP_DEFAULT_BLKSIZE ) ! 303: blksize = TFTP_DEFAULT_BLKSIZE; ! 304: tftp_request_blksize = blksize; ! 305: } ! 306: ! 307: /** ! 308: * MTFTP multicast receive address ! 309: * ! 310: * This is treated as a global configuration parameter. ! 311: */ ! 312: static struct sockaddr_in tftp_mtftp_socket = { ! 313: .sin_family = AF_INET, ! 314: .sin_addr.s_addr = htonl ( 0xefff0101 ), ! 315: .sin_port = htons ( 3001 ), ! 316: }; ! 317: ! 318: /** ! 319: * Set MTFTP multicast address ! 320: * ! 321: * @v address Multicast IPv4 address ! 322: */ ! 323: void tftp_set_mtftp_address ( struct in_addr address ) { ! 324: tftp_mtftp_socket.sin_addr = address; ! 325: } ! 326: ! 327: /** ! 328: * Set MTFTP multicast port ! 329: * ! 330: * @v port Multicast port ! 331: */ ! 332: void tftp_set_mtftp_port ( unsigned int port ) { ! 333: tftp_mtftp_socket.sin_port = htons ( port ); ! 334: } ! 335: ! 336: /** ! 337: * Transmit RRQ ! 338: * ! 339: * @v tftp TFTP connection ! 340: * @ret rc Return status code ! 341: */ ! 342: static int tftp_send_rrq ( struct tftp_request *tftp ) { ! 343: struct tftp_rrq *rrq; ! 344: const char *path; ! 345: size_t len; ! 346: struct io_buffer *iobuf; ! 347: ! 348: /* Strip initial '/' if present. If we were opened via the ! 349: * URI interface, then there will be an initial '/', since a ! 350: * full tftp:// URI provides no way to specify a non-absolute ! 351: * path. However, many TFTP servers (particularly Windows ! 352: * TFTP servers) complain about having an initial '/', and it ! 353: * violates user expectations to have a '/' silently added to ! 354: * the DHCP-specified filename. ! 355: */ ! 356: path = tftp->uri->path; ! 357: if ( *path == '/' ) ! 358: path++; ! 359: ! 360: DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, path ); ! 361: ! 362: /* Allocate buffer */ ! 363: len = ( sizeof ( *rrq ) + strlen ( path ) + 1 /* NUL */ ! 364: + 5 + 1 /* "octet" + NUL */ ! 365: + 7 + 1 + 5 + 1 /* "blksize" + NUL + ddddd + NUL */ ! 366: + 5 + 1 + 1 + 1 /* "tsize" + NUL + "0" + NUL */ ! 367: + 9 + 1 + 1 /* "multicast" + NUL + NUL */ ); ! 368: iobuf = xfer_alloc_iob ( &tftp->socket, len ); ! 369: if ( ! iobuf ) ! 370: return -ENOMEM; ! 371: ! 372: /* Build request */ ! 373: rrq = iob_put ( iobuf, sizeof ( *rrq ) ); ! 374: rrq->opcode = htons ( TFTP_RRQ ); ! 375: iob_put ( iobuf, snprintf ( iobuf->tail, iob_tailroom ( iobuf ), ! 376: "%s%coctet", path, 0 ) + 1 ); ! 377: if ( tftp->flags & TFTP_FL_RRQ_SIZES ) { ! 378: iob_put ( iobuf, snprintf ( iobuf->tail, ! 379: iob_tailroom ( iobuf ), ! 380: "blksize%c%d%ctsize%c0", 0, ! 381: tftp_request_blksize, 0, 0 ) + 1 ); ! 382: } ! 383: if ( tftp->flags & TFTP_FL_RRQ_MULTICAST ) { ! 384: iob_put ( iobuf, snprintf ( iobuf->tail, ! 385: iob_tailroom ( iobuf ), ! 386: "multicast%c", 0 ) + 1 ); ! 387: } ! 388: ! 389: /* RRQ always goes to the address specified in the initial ! 390: * xfer_open() call ! 391: */ ! 392: return xfer_deliver_iob ( &tftp->socket, iobuf ); ! 393: } ! 394: ! 395: /** ! 396: * Transmit ACK ! 397: * ! 398: * @v tftp TFTP connection ! 399: * @ret rc Return status code ! 400: */ ! 401: static int tftp_send_ack ( struct tftp_request *tftp ) { ! 402: struct tftp_ack *ack; ! 403: struct io_buffer *iobuf; ! 404: struct xfer_metadata meta = { ! 405: .dest = ( struct sockaddr * ) &tftp->peer, ! 406: }; ! 407: unsigned int block; ! 408: ! 409: /* Determine next required block number */ ! 410: block = bitmap_first_gap ( &tftp->bitmap ); ! 411: DBGC2 ( tftp, "TFTP %p sending ACK for block %d\n", tftp, block ); ! 412: ! 413: /* Allocate buffer */ ! 414: iobuf = xfer_alloc_iob ( &tftp->socket, sizeof ( *ack ) ); ! 415: if ( ! iobuf ) ! 416: return -ENOMEM; ! 417: ! 418: /* Build ACK */ ! 419: ack = iob_put ( iobuf, sizeof ( *ack ) ); ! 420: ack->opcode = htons ( TFTP_ACK ); ! 421: ack->block = htons ( block ); ! 422: ! 423: /* ACK always goes to the peer recorded from the RRQ response */ ! 424: return xfer_deliver ( &tftp->socket, iobuf, &meta ); ! 425: } ! 426: ! 427: /** ! 428: * Transmit ERROR (Abort) ! 429: * ! 430: * @v tftp TFTP connection ! 431: * @v errcode TFTP error code ! 432: * @v errmsg Error message string ! 433: * @ret rc Return status code ! 434: */ ! 435: static int tftp_send_error ( struct tftp_request *tftp, int errcode, ! 436: const char *errmsg ) { ! 437: struct tftp_error *err; ! 438: struct io_buffer *iobuf; ! 439: struct xfer_metadata meta = { ! 440: .dest = ( struct sockaddr * ) &tftp->peer, ! 441: }; ! 442: size_t msglen; ! 443: ! 444: DBGC2 ( tftp, "TFTP %p sending ERROR %d: %s\n", tftp, errcode, ! 445: errmsg ); ! 446: ! 447: /* Allocate buffer */ ! 448: msglen = sizeof ( *err ) + strlen ( errmsg ) + 1 /* NUL */; ! 449: iobuf = xfer_alloc_iob ( &tftp->socket, msglen ); ! 450: if ( ! iobuf ) ! 451: return -ENOMEM; ! 452: ! 453: /* Build ERROR */ ! 454: err = iob_put ( iobuf, msglen ); ! 455: err->opcode = htons ( TFTP_ERROR ); ! 456: err->errcode = htons ( errcode ); ! 457: strcpy ( err->errmsg, errmsg ); ! 458: ! 459: /* ERR always goes to the peer recorded from the RRQ response */ ! 460: return xfer_deliver ( &tftp->socket, iobuf, &meta ); ! 461: } ! 462: ! 463: /** ! 464: * Transmit next relevant packet ! 465: * ! 466: * @v tftp TFTP connection ! 467: * @ret rc Return status code ! 468: */ ! 469: static int tftp_send_packet ( struct tftp_request *tftp ) { ! 470: ! 471: /* Update retransmission timer. While name resolution takes place the ! 472: * window is zero. Avoid unnecessary delay after name resolution ! 473: * completes by retrying immediately. ! 474: */ ! 475: stop_timer ( &tftp->timer ); ! 476: if ( xfer_window ( &tftp->socket ) ) { ! 477: start_timer ( &tftp->timer ); ! 478: } else { ! 479: start_timer_nodelay ( &tftp->timer ); ! 480: } ! 481: ! 482: /* Send RRQ or ACK as appropriate */ ! 483: if ( ! tftp->peer.st_family ) { ! 484: return tftp_send_rrq ( tftp ); ! 485: } else { ! 486: if ( tftp->flags & TFTP_FL_SEND_ACK ) { ! 487: return tftp_send_ack ( tftp ); ! 488: } else { ! 489: return 0; ! 490: } ! 491: } ! 492: } ! 493: ! 494: /** ! 495: * Handle TFTP retransmission timer expiry ! 496: * ! 497: * @v timer Retry timer ! 498: * @v fail Failure indicator ! 499: */ ! 500: static void tftp_timer_expired ( struct retry_timer *timer, int fail ) { ! 501: struct tftp_request *tftp = ! 502: container_of ( timer, struct tftp_request, timer ); ! 503: int rc; ! 504: ! 505: /* If we are doing MTFTP, attempt the various recovery strategies */ ! 506: if ( tftp->flags & TFTP_FL_MTFTP_RECOVERY ) { ! 507: if ( tftp->peer.st_family ) { ! 508: /* If we have received any response from the server, ! 509: * try resending the RRQ to restart the download. ! 510: */ ! 511: DBGC ( tftp, "TFTP %p attempting reopen\n", tftp ); ! 512: if ( ( rc = tftp_reopen ( tftp ) ) != 0 ) ! 513: goto err; ! 514: } else { ! 515: /* Fall back to plain TFTP after several attempts */ ! 516: tftp->mtftp_timeouts++; ! 517: DBGC ( tftp, "TFTP %p timeout %d waiting for MTFTP " ! 518: "open\n", tftp, tftp->mtftp_timeouts ); ! 519: ! 520: if ( tftp->mtftp_timeouts > MTFTP_MAX_TIMEOUTS ) { ! 521: DBGC ( tftp, "TFTP %p falling back to plain " ! 522: "TFTP\n", tftp ); ! 523: tftp->flags = TFTP_FL_RRQ_SIZES; ! 524: ! 525: /* Close multicast socket */ ! 526: intf_restart ( &tftp->mc_socket, 0 ); ! 527: ! 528: /* Reset retry timer */ ! 529: start_timer_nodelay ( &tftp->timer ); ! 530: ! 531: /* The blocksize may change: discard ! 532: * the block bitmap ! 533: */ ! 534: bitmap_free ( &tftp->bitmap ); ! 535: memset ( &tftp->bitmap, 0, ! 536: sizeof ( tftp->bitmap ) ); ! 537: ! 538: /* Reopen on standard TFTP port */ ! 539: tftp->port = TFTP_PORT; ! 540: if ( ( rc = tftp_reopen ( tftp ) ) != 0 ) ! 541: goto err; ! 542: } ! 543: } ! 544: } else { ! 545: /* Not doing MTFTP (or have fallen back to plain ! 546: * TFTP); fail as per normal. ! 547: */ ! 548: if ( fail ) { ! 549: rc = -ETIMEDOUT; ! 550: goto err; ! 551: } ! 552: } ! 553: tftp_send_packet ( tftp ); ! 554: return; ! 555: ! 556: err: ! 557: tftp_done ( tftp, rc ); ! 558: } ! 559: ! 560: /** ! 561: * Process TFTP "blksize" option ! 562: * ! 563: * @v tftp TFTP connection ! 564: * @v value Option value ! 565: * @ret rc Return status code ! 566: */ ! 567: static int tftp_process_blksize ( struct tftp_request *tftp, ! 568: const char *value ) { ! 569: char *end; ! 570: ! 571: tftp->blksize = strtoul ( value, &end, 10 ); ! 572: if ( *end ) { ! 573: DBGC ( tftp, "TFTP %p got invalid blksize \"%s\"\n", ! 574: tftp, value ); ! 575: return -EINVAL_BLKSIZE; ! 576: } ! 577: DBGC ( tftp, "TFTP %p blksize=%d\n", tftp, tftp->blksize ); ! 578: ! 579: return 0; ! 580: } ! 581: ! 582: /** ! 583: * Process TFTP "tsize" option ! 584: * ! 585: * @v tftp TFTP connection ! 586: * @v value Option value ! 587: * @ret rc Return status code ! 588: */ ! 589: static int tftp_process_tsize ( struct tftp_request *tftp, ! 590: const char *value ) { ! 591: char *end; ! 592: ! 593: tftp->tsize = strtoul ( value, &end, 10 ); ! 594: if ( *end ) { ! 595: DBGC ( tftp, "TFTP %p got invalid tsize \"%s\"\n", ! 596: tftp, value ); ! 597: return -EINVAL_TSIZE; ! 598: } ! 599: DBGC ( tftp, "TFTP %p tsize=%ld\n", tftp, tftp->tsize ); ! 600: ! 601: return 0; ! 602: } ! 603: ! 604: /** ! 605: * Process TFTP "multicast" option ! 606: * ! 607: * @v tftp TFTP connection ! 608: * @v value Option value ! 609: * @ret rc Return status code ! 610: */ ! 611: static int tftp_process_multicast ( struct tftp_request *tftp, ! 612: const char *value ) { ! 613: union { ! 614: struct sockaddr sa; ! 615: struct sockaddr_in sin; ! 616: } socket; ! 617: char buf[ strlen ( value ) + 1 ]; ! 618: char *addr; ! 619: char *port; ! 620: char *port_end; ! 621: char *mc; ! 622: char *mc_end; ! 623: int rc; ! 624: ! 625: /* Split value into "addr,port,mc" fields */ ! 626: memcpy ( buf, value, sizeof ( buf ) ); ! 627: addr = buf; ! 628: port = strchr ( addr, ',' ); ! 629: if ( ! port ) { ! 630: DBGC ( tftp, "TFTP %p multicast missing port,mc\n", tftp ); ! 631: return -EINVAL_MC_NO_PORT; ! 632: } ! 633: *(port++) = '\0'; ! 634: mc = strchr ( port, ',' ); ! 635: if ( ! mc ) { ! 636: DBGC ( tftp, "TFTP %p multicast missing mc\n", tftp ); ! 637: return -EINVAL_MC_NO_MC; ! 638: } ! 639: *(mc++) = '\0'; ! 640: ! 641: /* Parse parameters */ ! 642: if ( strtoul ( mc, &mc_end, 0 ) == 0 ) ! 643: tftp->flags &= ~TFTP_FL_SEND_ACK; ! 644: if ( *mc_end ) { ! 645: DBGC ( tftp, "TFTP %p multicast invalid mc %s\n", tftp, mc ); ! 646: return -EINVAL_MC_INVALID_MC; ! 647: } ! 648: DBGC ( tftp, "TFTP %p is%s the master client\n", ! 649: tftp, ( ( tftp->flags & TFTP_FL_SEND_ACK ) ? "" : " not" ) ); ! 650: if ( *addr && *port ) { ! 651: socket.sin.sin_family = AF_INET; ! 652: if ( inet_aton ( addr, &socket.sin.sin_addr ) == 0 ) { ! 653: DBGC ( tftp, "TFTP %p multicast invalid IP address " ! 654: "%s\n", tftp, addr ); ! 655: return -EINVAL_MC_INVALID_IP; ! 656: } ! 657: DBGC ( tftp, "TFTP %p multicast IP address %s\n", ! 658: tftp, inet_ntoa ( socket.sin.sin_addr ) ); ! 659: socket.sin.sin_port = htons ( strtoul ( port, &port_end, 0 ) ); ! 660: if ( *port_end ) { ! 661: DBGC ( tftp, "TFTP %p multicast invalid port %s\n", ! 662: tftp, port ); ! 663: return -EINVAL_MC_INVALID_PORT; ! 664: } ! 665: DBGC ( tftp, "TFTP %p multicast port %d\n", ! 666: tftp, ntohs ( socket.sin.sin_port ) ); ! 667: if ( ( rc = tftp_reopen_mc ( tftp, &socket.sa ) ) != 0 ) ! 668: return rc; ! 669: } ! 670: ! 671: return 0; ! 672: } ! 673: ! 674: /** A TFTP option */ ! 675: struct tftp_option { ! 676: /** Option name */ ! 677: const char *name; ! 678: /** Option processor ! 679: * ! 680: * @v tftp TFTP connection ! 681: * @v value Option value ! 682: * @ret rc Return status code ! 683: */ ! 684: int ( * process ) ( struct tftp_request *tftp, const char *value ); ! 685: }; ! 686: ! 687: /** Recognised TFTP options */ ! 688: static struct tftp_option tftp_options[] = { ! 689: { "blksize", tftp_process_blksize }, ! 690: { "tsize", tftp_process_tsize }, ! 691: { "multicast", tftp_process_multicast }, ! 692: { NULL, NULL } ! 693: }; ! 694: ! 695: /** ! 696: * Process TFTP option ! 697: * ! 698: * @v tftp TFTP connection ! 699: * @v name Option name ! 700: * @v value Option value ! 701: * @ret rc Return status code ! 702: */ ! 703: static int tftp_process_option ( struct tftp_request *tftp, ! 704: const char *name, const char *value ) { ! 705: struct tftp_option *option; ! 706: ! 707: for ( option = tftp_options ; option->name ; option++ ) { ! 708: if ( strcasecmp ( name, option->name ) == 0 ) ! 709: return option->process ( tftp, value ); ! 710: } ! 711: ! 712: DBGC ( tftp, "TFTP %p received unknown option \"%s\" = \"%s\"\n", ! 713: tftp, name, value ); ! 714: ! 715: /* Unknown options should be silently ignored */ ! 716: return 0; ! 717: } ! 718: ! 719: /** ! 720: * Receive OACK ! 721: * ! 722: * @v tftp TFTP connection ! 723: * @v buf Temporary data buffer ! 724: * @v len Length of temporary data buffer ! 725: * @ret rc Return status code ! 726: */ ! 727: static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) { ! 728: struct tftp_oack *oack = buf; ! 729: char *end = buf + len; ! 730: char *name; ! 731: char *value; ! 732: char *next; ! 733: int rc = 0; ! 734: ! 735: /* Sanity check */ ! 736: if ( len < sizeof ( *oack ) ) { ! 737: DBGC ( tftp, "TFTP %p received underlength OACK packet " ! 738: "length %zd\n", tftp, len ); ! 739: rc = -EINVAL; ! 740: goto done; ! 741: } ! 742: ! 743: /* Process each option in turn */ ! 744: for ( name = oack->data ; name < end ; name = next ) { ! 745: ! 746: /* Parse option name and value ! 747: * ! 748: * We treat parsing errors as non-fatal, because there ! 749: * exists at least one TFTP server (IBM Tivoli PXE ! 750: * Server 5.1.0.3) that has been observed to send ! 751: * malformed OACKs containing trailing garbage bytes. ! 752: */ ! 753: value = ( name + strnlen ( name, ( end - name ) ) + 1 ); ! 754: if ( value > end ) { ! 755: DBGC ( tftp, "TFTP %p received OACK with malformed " ! 756: "option name:\n", tftp ); ! 757: DBGC_HD ( tftp, oack, len ); ! 758: break; ! 759: } ! 760: if ( value == end ) { ! 761: DBGC ( tftp, "TFTP %p received OACK missing value " ! 762: "for option \"%s\"\n", tftp, name ); ! 763: DBGC_HD ( tftp, oack, len ); ! 764: break; ! 765: } ! 766: next = ( value + strnlen ( value, ( end - value ) ) + 1 ); ! 767: if ( next > end ) { ! 768: DBGC ( tftp, "TFTP %p received OACK with malformed " ! 769: "value for option \"%s\":\n", tftp, name ); ! 770: DBGC_HD ( tftp, oack, len ); ! 771: break; ! 772: } ! 773: ! 774: /* Process option */ ! 775: if ( ( rc = tftp_process_option ( tftp, name, value ) ) != 0 ) ! 776: goto done; ! 777: } ! 778: ! 779: /* Process tsize information, if available */ ! 780: if ( tftp->tsize ) { ! 781: if ( ( rc = tftp_presize ( tftp, tftp->tsize ) ) != 0 ) ! 782: goto done; ! 783: } ! 784: ! 785: /* Abort request if only trying to determine file size */ ! 786: if ( tftp->flags & TFTP_FL_SIZEONLY ) { ! 787: rc = 0; ! 788: tftp_send_error ( tftp, 0, "TFTP Aborted" ); ! 789: tftp_done ( tftp, rc ); ! 790: return rc; ! 791: } ! 792: ! 793: /* Request next data block */ ! 794: tftp_send_packet ( tftp ); ! 795: ! 796: done: ! 797: if ( rc ) ! 798: tftp_done ( tftp, rc ); ! 799: return rc; ! 800: } ! 801: ! 802: /** ! 803: * Receive DATA ! 804: * ! 805: * @v tftp TFTP connection ! 806: * @v iobuf I/O buffer ! 807: * @ret rc Return status code ! 808: * ! 809: * Takes ownership of I/O buffer. ! 810: */ ! 811: static int tftp_rx_data ( struct tftp_request *tftp, ! 812: struct io_buffer *iobuf ) { ! 813: struct tftp_data *data = iobuf->data; ! 814: struct xfer_metadata meta; ! 815: unsigned int block; ! 816: off_t offset; ! 817: size_t data_len; ! 818: int rc; ! 819: ! 820: if ( tftp->flags & TFTP_FL_SIZEONLY ) { ! 821: /* If we get here then server doesn't support SIZE option */ ! 822: rc = -ENOTSUP; ! 823: tftp_send_error ( tftp, 0, "TFTP Aborted" ); ! 824: goto done; ! 825: } ! 826: ! 827: /* Sanity check */ ! 828: if ( iob_len ( iobuf ) < sizeof ( *data ) ) { ! 829: DBGC ( tftp, "TFTP %p received underlength DATA packet " ! 830: "length %zd\n", tftp, iob_len ( iobuf ) ); ! 831: rc = -EINVAL; ! 832: goto done; ! 833: } ! 834: ! 835: /* Calculate block number */ ! 836: block = ( ( bitmap_first_gap ( &tftp->bitmap ) + 1 ) & ~0xffff ); ! 837: if ( data->block == 0 && block == 0 ) { ! 838: DBGC ( tftp, "TFTP %p received data block 0\n", tftp ); ! 839: rc = -EINVAL; ! 840: goto done; ! 841: } ! 842: block += ( ntohs ( data->block ) - 1 ); ! 843: ! 844: /* Extract data */ ! 845: offset = ( block * tftp->blksize ); ! 846: iob_pull ( iobuf, sizeof ( *data ) ); ! 847: data_len = iob_len ( iobuf ); ! 848: if ( data_len > tftp->blksize ) { ! 849: DBGC ( tftp, "TFTP %p received overlength DATA packet " ! 850: "length %zd\n", tftp, data_len ); ! 851: rc = -EINVAL; ! 852: goto done; ! 853: } ! 854: ! 855: /* Deliver data */ ! 856: memset ( &meta, 0, sizeof ( meta ) ); ! 857: meta.flags = XFER_FL_ABS_OFFSET; ! 858: meta.offset = offset; ! 859: if ( ( rc = xfer_deliver ( &tftp->xfer, iob_disown ( iobuf ), ! 860: &meta ) ) != 0 ) { ! 861: DBGC ( tftp, "TFTP %p could not deliver data: %s\n", ! 862: tftp, strerror ( rc ) ); ! 863: goto done; ! 864: } ! 865: ! 866: /* Ensure block bitmap is ready */ ! 867: if ( ( rc = tftp_presize ( tftp, ( offset + data_len ) ) ) != 0 ) ! 868: goto done; ! 869: ! 870: /* Mark block as received */ ! 871: bitmap_set ( &tftp->bitmap, block ); ! 872: ! 873: /* Acknowledge block */ ! 874: tftp_send_packet ( tftp ); ! 875: ! 876: /* If all blocks have been received, finish. */ ! 877: if ( bitmap_full ( &tftp->bitmap ) ) ! 878: tftp_done ( tftp, 0 ); ! 879: ! 880: done: ! 881: free_iob ( iobuf ); ! 882: if ( rc ) ! 883: tftp_done ( tftp, rc ); ! 884: return rc; ! 885: } ! 886: ! 887: /** ! 888: * Convert TFTP error code to return status code ! 889: * ! 890: * @v errcode TFTP error code ! 891: * @ret rc Return status code ! 892: */ ! 893: static int tftp_errcode_to_rc ( unsigned int errcode ) { ! 894: switch ( errcode ) { ! 895: case TFTP_ERR_FILE_NOT_FOUND: return -ENOENT; ! 896: case TFTP_ERR_ACCESS_DENIED: return -EACCES; ! 897: case TFTP_ERR_ILLEGAL_OP: return -ENOTTY; ! 898: default: return -ENOTSUP; ! 899: } ! 900: } ! 901: ! 902: /** ! 903: * Receive ERROR ! 904: * ! 905: * @v tftp TFTP connection ! 906: * @v buf Temporary data buffer ! 907: * @v len Length of temporary data buffer ! 908: * @ret rc Return status code ! 909: */ ! 910: static int tftp_rx_error ( struct tftp_request *tftp, void *buf, size_t len ) { ! 911: struct tftp_error *error = buf; ! 912: int rc; ! 913: ! 914: /* Sanity check */ ! 915: if ( len < sizeof ( *error ) ) { ! 916: DBGC ( tftp, "TFTP %p received underlength ERROR packet " ! 917: "length %zd\n", tftp, len ); ! 918: return -EINVAL; ! 919: } ! 920: ! 921: DBGC ( tftp, "TFTP %p received ERROR packet with code %d, message " ! 922: "\"%s\"\n", tftp, ntohs ( error->errcode ), error->errmsg ); ! 923: ! 924: /* Determine final operation result */ ! 925: rc = tftp_errcode_to_rc ( ntohs ( error->errcode ) ); ! 926: ! 927: /* Close TFTP request */ ! 928: tftp_done ( tftp, rc ); ! 929: ! 930: return 0; ! 931: } ! 932: ! 933: /** ! 934: * Receive new data ! 935: * ! 936: * @v tftp TFTP connection ! 937: * @v iobuf I/O buffer ! 938: * @v meta Transfer metadata ! 939: * @ret rc Return status code ! 940: */ ! 941: static int tftp_rx ( struct tftp_request *tftp, ! 942: struct io_buffer *iobuf, ! 943: struct xfer_metadata *meta ) { ! 944: struct sockaddr_tcpip *st_src; ! 945: struct tftp_common *common = iobuf->data; ! 946: size_t len = iob_len ( iobuf ); ! 947: int rc = -EINVAL; ! 948: ! 949: /* Sanity checks */ ! 950: if ( len < sizeof ( *common ) ) { ! 951: DBGC ( tftp, "TFTP %p received underlength packet length " ! 952: "%zd\n", tftp, len ); ! 953: goto done; ! 954: } ! 955: if ( ! meta->src ) { ! 956: DBGC ( tftp, "TFTP %p received packet without source port\n", ! 957: tftp ); ! 958: goto done; ! 959: } ! 960: ! 961: /* Filter by TID. Set TID on first response received */ ! 962: st_src = ( struct sockaddr_tcpip * ) meta->src; ! 963: if ( ! tftp->peer.st_family ) { ! 964: memcpy ( &tftp->peer, st_src, sizeof ( tftp->peer ) ); ! 965: DBGC ( tftp, "TFTP %p using remote port %d\n", tftp, ! 966: ntohs ( tftp->peer.st_port ) ); ! 967: } else if ( memcmp ( &tftp->peer, st_src, ! 968: sizeof ( tftp->peer ) ) != 0 ) { ! 969: DBGC ( tftp, "TFTP %p received packet from wrong source (got " ! 970: "%d, wanted %d)\n", tftp, ntohs ( st_src->st_port ), ! 971: ntohs ( tftp->peer.st_port ) ); ! 972: goto done; ! 973: } ! 974: ! 975: switch ( common->opcode ) { ! 976: case htons ( TFTP_OACK ): ! 977: rc = tftp_rx_oack ( tftp, iobuf->data, len ); ! 978: break; ! 979: case htons ( TFTP_DATA ): ! 980: rc = tftp_rx_data ( tftp, iob_disown ( iobuf ) ); ! 981: break; ! 982: case htons ( TFTP_ERROR ): ! 983: rc = tftp_rx_error ( tftp, iobuf->data, len ); ! 984: break; ! 985: default: ! 986: DBGC ( tftp, "TFTP %p received strange packet type %d\n", ! 987: tftp, ntohs ( common->opcode ) ); ! 988: break; ! 989: }; ! 990: ! 991: done: ! 992: free_iob ( iobuf ); ! 993: return rc; ! 994: } ! 995: ! 996: /** ! 997: * Receive new data via socket ! 998: * ! 999: * @v tftp TFTP connection ! 1000: * @v iobuf I/O buffer ! 1001: * @v meta Transfer metadata ! 1002: * @ret rc Return status code ! 1003: */ ! 1004: static int tftp_socket_deliver ( struct tftp_request *tftp, ! 1005: struct io_buffer *iobuf, ! 1006: struct xfer_metadata *meta ) { ! 1007: ! 1008: /* Enable sending ACKs when we receive a unicast packet. This ! 1009: * covers three cases: ! 1010: * ! 1011: * 1. Standard TFTP; we should always send ACKs, and will ! 1012: * always receive a unicast packet before we need to send the ! 1013: * first ACK. ! 1014: * ! 1015: * 2. RFC2090 multicast TFTP; the only unicast packets we will ! 1016: * receive are the OACKs; enable sending ACKs here (before ! 1017: * processing the OACK) and disable it when processing the ! 1018: * multicast option if we are not the master client. ! 1019: * ! 1020: * 3. MTFTP; receiving a unicast datagram indicates that we ! 1021: * are the "master client" and should send ACKs. ! 1022: */ ! 1023: tftp->flags |= TFTP_FL_SEND_ACK; ! 1024: ! 1025: return tftp_rx ( tftp, iobuf, meta ); ! 1026: } ! 1027: ! 1028: /** TFTP socket operations */ ! 1029: static struct interface_operation tftp_socket_operations[] = { ! 1030: INTF_OP ( xfer_deliver, struct tftp_request *, tftp_socket_deliver ), ! 1031: }; ! 1032: ! 1033: /** TFTP socket interface descriptor */ ! 1034: static struct interface_descriptor tftp_socket_desc = ! 1035: INTF_DESC ( struct tftp_request, socket, tftp_socket_operations ); ! 1036: ! 1037: /** TFTP multicast socket operations */ ! 1038: static struct interface_operation tftp_mc_socket_operations[] = { ! 1039: INTF_OP ( xfer_deliver, struct tftp_request *, tftp_rx ), ! 1040: }; ! 1041: ! 1042: /** TFTP multicast socket interface descriptor */ ! 1043: static struct interface_descriptor tftp_mc_socket_desc = ! 1044: INTF_DESC ( struct tftp_request, mc_socket, tftp_mc_socket_operations ); ! 1045: ! 1046: /** ! 1047: * Check flow control window ! 1048: * ! 1049: * @v tftp TFTP connection ! 1050: * @ret len Length of window ! 1051: */ ! 1052: static size_t tftp_xfer_window ( struct tftp_request *tftp ) { ! 1053: ! 1054: /* We abuse this data-xfer method to convey the blocksize to ! 1055: * the caller. This really should be done using some kind of ! 1056: * stat() method, but we don't yet have the facility to do ! 1057: * that. ! 1058: */ ! 1059: return tftp->blksize; ! 1060: } ! 1061: ! 1062: /** TFTP data transfer interface operations */ ! 1063: static struct interface_operation tftp_xfer_operations[] = { ! 1064: INTF_OP ( xfer_window, struct tftp_request *, tftp_xfer_window ), ! 1065: INTF_OP ( intf_close, struct tftp_request *, tftp_done ), ! 1066: }; ! 1067: ! 1068: /** TFTP data transfer interface descriptor */ ! 1069: static struct interface_descriptor tftp_xfer_desc = ! 1070: INTF_DESC ( struct tftp_request, xfer, tftp_xfer_operations ); ! 1071: ! 1072: /** ! 1073: * Initiate TFTP/TFTM/MTFTP download ! 1074: * ! 1075: * @v xfer Data transfer interface ! 1076: * @v uri Uniform Resource Identifier ! 1077: * @ret rc Return status code ! 1078: */ ! 1079: static int tftp_core_open ( struct interface *xfer, struct uri *uri, ! 1080: unsigned int default_port, ! 1081: struct sockaddr *multicast, ! 1082: unsigned int flags ) { ! 1083: struct tftp_request *tftp; ! 1084: int rc; ! 1085: ! 1086: /* Sanity checks */ ! 1087: if ( ! uri->host ) ! 1088: return -EINVAL; ! 1089: if ( ! uri->path ) ! 1090: return -EINVAL; ! 1091: ! 1092: /* Allocate and populate TFTP structure */ ! 1093: tftp = zalloc ( sizeof ( *tftp ) ); ! 1094: if ( ! tftp ) ! 1095: return -ENOMEM; ! 1096: ref_init ( &tftp->refcnt, tftp_free ); ! 1097: intf_init ( &tftp->xfer, &tftp_xfer_desc, &tftp->refcnt ); ! 1098: intf_init ( &tftp->socket, &tftp_socket_desc, &tftp->refcnt ); ! 1099: intf_init ( &tftp->mc_socket, &tftp_mc_socket_desc, &tftp->refcnt ); ! 1100: timer_init ( &tftp->timer, tftp_timer_expired, &tftp->refcnt ); ! 1101: tftp->uri = uri_get ( uri ); ! 1102: tftp->blksize = TFTP_DEFAULT_BLKSIZE; ! 1103: tftp->flags = flags; ! 1104: ! 1105: /* Open socket */ ! 1106: tftp->port = uri_port ( tftp->uri, default_port ); ! 1107: if ( ( rc = tftp_reopen ( tftp ) ) != 0 ) ! 1108: goto err; ! 1109: ! 1110: /* Open multicast socket */ ! 1111: if ( multicast ) { ! 1112: if ( ( rc = tftp_reopen_mc ( tftp, multicast ) ) != 0 ) ! 1113: goto err; ! 1114: } ! 1115: ! 1116: /* Start timer to initiate RRQ */ ! 1117: start_timer_nodelay ( &tftp->timer ); ! 1118: ! 1119: /* Attach to parent interface, mortalise self, and return */ ! 1120: intf_plug_plug ( &tftp->xfer, xfer ); ! 1121: ref_put ( &tftp->refcnt ); ! 1122: return 0; ! 1123: ! 1124: err: ! 1125: DBGC ( tftp, "TFTP %p could not create request: %s\n", ! 1126: tftp, strerror ( rc ) ); ! 1127: tftp_done ( tftp, rc ); ! 1128: ref_put ( &tftp->refcnt ); ! 1129: return rc; ! 1130: } ! 1131: ! 1132: /** ! 1133: * Initiate TFTP download ! 1134: * ! 1135: * @v xfer Data transfer interface ! 1136: * @v uri Uniform Resource Identifier ! 1137: * @ret rc Return status code ! 1138: */ ! 1139: static int tftp_open ( struct interface *xfer, struct uri *uri ) { ! 1140: return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, ! 1141: TFTP_FL_RRQ_SIZES ); ! 1142: ! 1143: } ! 1144: ! 1145: /** TFTP URI opener */ ! 1146: struct uri_opener tftp_uri_opener __uri_opener = { ! 1147: .scheme = "tftp", ! 1148: .open = tftp_open, ! 1149: }; ! 1150: ! 1151: /** ! 1152: * Initiate TFTP-size request ! 1153: * ! 1154: * @v xfer Data transfer interface ! 1155: * @v uri Uniform Resource Identifier ! 1156: * @ret rc Return status code ! 1157: */ ! 1158: static int tftpsize_open ( struct interface *xfer, struct uri *uri ) { ! 1159: return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, ! 1160: ( TFTP_FL_RRQ_SIZES | ! 1161: TFTP_FL_SIZEONLY ) ); ! 1162: ! 1163: } ! 1164: ! 1165: /** TFTP URI opener */ ! 1166: struct uri_opener tftpsize_uri_opener __uri_opener = { ! 1167: .scheme = "tftpsize", ! 1168: .open = tftpsize_open, ! 1169: }; ! 1170: ! 1171: /** ! 1172: * Initiate TFTM download ! 1173: * ! 1174: * @v xfer Data transfer interface ! 1175: * @v uri Uniform Resource Identifier ! 1176: * @ret rc Return status code ! 1177: */ ! 1178: static int tftm_open ( struct interface *xfer, struct uri *uri ) { ! 1179: return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, ! 1180: ( TFTP_FL_RRQ_SIZES | ! 1181: TFTP_FL_RRQ_MULTICAST ) ); ! 1182: ! 1183: } ! 1184: ! 1185: /** TFTM URI opener */ ! 1186: struct uri_opener tftm_uri_opener __uri_opener = { ! 1187: .scheme = "tftm", ! 1188: .open = tftm_open, ! 1189: }; ! 1190: ! 1191: /** ! 1192: * Initiate MTFTP download ! 1193: * ! 1194: * @v xfer Data transfer interface ! 1195: * @v uri Uniform Resource Identifier ! 1196: * @ret rc Return status code ! 1197: */ ! 1198: static int mtftp_open ( struct interface *xfer, struct uri *uri ) { ! 1199: return tftp_core_open ( xfer, uri, MTFTP_PORT, ! 1200: ( struct sockaddr * ) &tftp_mtftp_socket, ! 1201: TFTP_FL_MTFTP_RECOVERY ); ! 1202: } ! 1203: ! 1204: /** MTFTP URI opener */ ! 1205: struct uri_opener mtftp_uri_opener __uri_opener = { ! 1206: .scheme = "mtftp", ! 1207: .open = mtftp_open, ! 1208: }; ! 1209: ! 1210: /****************************************************************************** ! 1211: * ! 1212: * Settings ! 1213: * ! 1214: ****************************************************************************** ! 1215: */ ! 1216: ! 1217: /** TFTP server setting */ ! 1218: struct setting next_server_setting __setting ( SETTING_BOOT ) = { ! 1219: .name = "next-server", ! 1220: .description = "TFTP server", ! 1221: .tag = DHCP_EB_SIADDR, ! 1222: .type = &setting_type_ipv4, ! 1223: }; ! 1224: ! 1225: /** ! 1226: * Apply TFTP configuration settings ! 1227: * ! 1228: * @ret rc Return status code ! 1229: */ ! 1230: static int tftp_apply_settings ( void ) { ! 1231: static struct in_addr tftp_server = { 0 }; ! 1232: struct in_addr last_tftp_server; ! 1233: char uri_string[32]; ! 1234: struct uri *uri; ! 1235: ! 1236: /* Retrieve TFTP server setting */ ! 1237: last_tftp_server = tftp_server; ! 1238: fetch_ipv4_setting ( NULL, &next_server_setting, &tftp_server ); ! 1239: ! 1240: /* If TFTP server setting has changed, set the current working ! 1241: * URI to match. Do it only when the TFTP server has changed ! 1242: * to try to minimise surprises to the user, who probably ! 1243: * won't expect the CWURI to change just because they updated ! 1244: * an unrelated setting and triggered all the settings ! 1245: * applicators. ! 1246: */ ! 1247: if ( tftp_server.s_addr != last_tftp_server.s_addr ) { ! 1248: if ( tftp_server.s_addr ) { ! 1249: snprintf ( uri_string, sizeof ( uri_string ), ! 1250: "tftp://%s/", inet_ntoa ( tftp_server ) ); ! 1251: uri = parse_uri ( uri_string ); ! 1252: if ( ! uri ) ! 1253: return -ENOMEM; ! 1254: } else { ! 1255: uri = NULL; ! 1256: } ! 1257: churi ( uri ); ! 1258: uri_put ( uri ); ! 1259: } ! 1260: ! 1261: return 0; ! 1262: } ! 1263: ! 1264: /** TFTP settings applicator */ ! 1265: struct settings_applicator tftp_settings_applicator __settings_applicator = { ! 1266: .apply = tftp_apply_settings, ! 1267: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.