diff options
author | Michael Brown <mcb30@etherboot.org> | 2007-06-11 18:11:29 +0100 |
---|---|---|
committer | Michael Brown <mcb30@etherboot.org> | 2007-06-11 18:11:29 +0100 |
commit | 01b4bde8a061773d563dfe3086ca04ad7ec7ca30 (patch) | |
tree | 043e3d6f6ecdc4a392da5ed38c21d02e3936dfad /src/net/udp/tftp.c | |
parent | 95adce0ce15b7e7b226f3d1162a7330e36ad76c8 (diff) | |
download | ipxe-01b4bde8a061773d563dfe3086ca04ad7ec7ca30.tar.gz |
Updated TFTP and PXE UDP API code to use not-yet-implemented data-xfer
UDP API.
Diffstat (limited to 'src/net/udp/tftp.c')
-rw-r--r-- | src/net/udp/tftp.c | 624 |
1 files changed, 378 insertions, 246 deletions
diff --git a/src/net/udp/tftp.c b/src/net/udp/tftp.c index b07c150a..027ada1c 100644 --- a/src/net/udp/tftp.c +++ b/src/net/udp/tftp.c @@ -24,9 +24,11 @@ #include <byteswap.h> #include <errno.h> #include <assert.h> -#include <gpxe/async.h> -#include <gpxe/tftp.h> +#include <gpxe/refcnt.h> +#include <gpxe/xfer.h> +#include <gpxe/open.h> #include <gpxe/uri.h> +#include <gpxe/tftp.h> /** @file * @@ -34,126 +36,187 @@ * */ -/** A TFTP option */ -struct tftp_option { - /** Option name */ - const char *name; - /** Option processor +/** + * A TFTP request + * + * This data structure holds the state for an ongoing TFTP transfer. + */ +struct tftp_request { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct xfer_interface xfer; + + /** URI being fetched */ + struct uri *uri; + /** Transport layer interface */ + struct xfer_interface socket; + + /** Data block size * - * @v tftp TFTP connection - * @v value Option value - * @ret rc Return status code + * This is the "blksize" option negotiated with the TFTP + * server. (If the TFTP server does not support TFTP options, + * this will default to 512). + */ + unsigned int blksize; + /** File size + * + * This is the value returned in the "tsize" option from the + * TFTP server. If the TFTP server does not support the + * "tsize" option, this value will be zero. + */ + unsigned long tsize; + + /** Request state + * + * This is the block number to be used in the next ACK sent + * back to the server, i.e. the number of the last received + * data block. The value zero indicates that the last + * received block was an OACK (i.e. that the next ACK will + * contain a block number of zero), and any value less than + * zero indicates that the connection has not yet been opened + * (i.e. that no blocks have yet been received). + */ + int state; + /** Peer address + * + * The peer address is determined by the first response + * received to the TFTP RRQ. */ - int ( * process ) ( struct tftp_session *tftp, const char *value ); + struct sockaddr_tcpip peer; + /** Retransmission timer */ + struct retry_timer timer; }; /** - * Process TFTP "blksize" option + * Free TFTP request * - * @v tftp TFTP connection - * @v value Option value - * @ret rc Return status code + * @v refcnt Reference counter */ -static int tftp_process_blksize ( struct tftp_session *tftp, - const char *value ) { - char *end; - - tftp->blksize = strtoul ( value, &end, 10 ); - if ( *end ) { - DBGC ( tftp, "TFTP %p got invalid blksize \"%s\"\n", - tftp, value ); - return -EINVAL; - } - DBGC ( tftp, "TFTP %p blksize=%d\n", tftp, tftp->blksize ); +static void tftp_free ( struct refcnt *refcnt ) { + struct tftp_request *tftp = + container_of ( refcnt, struct tftp_request, refcnt ); - return 0; + uri_put ( tftp->uri ); + free ( tftp ); } /** - * Process TFTP "tsize" option + * Mark TFTP request as complete * * @v tftp TFTP connection - * @v value Option value - * @ret rc Return status code + * @v rc Return status code */ -static int tftp_process_tsize ( struct tftp_session *tftp, - const char *value ) { - char *end; +static void tftp_done ( struct tftp_request *tftp, int rc ) { - tftp->tsize = strtoul ( value, &end, 10 ); - if ( *end ) { - DBGC ( tftp, "TFTP %p got invalid tsize \"%s\"\n", - tftp, value ); - return -EINVAL; - } - DBGC ( tftp, "TFTP %p tsize=%ld\n", tftp, tftp->tsize ); + /* Stop the retry timer */ + stop_timer ( &tftp->timer ); - return 0; + /* Close all data transfer interfaces */ + xfer_nullify ( &tftp->socket ); + xfer_close ( &tftp->socket, rc ); + xfer_nullify ( &tftp->xfer ); + xfer_close ( &tftp->xfer, rc ); } -/** Recognised TFTP options */ -static struct tftp_option tftp_options[] = { - { "blksize", tftp_process_blksize }, - { "tsize", tftp_process_tsize }, - { NULL, NULL } -}; +/** + * TFTP requested blocksize + * + * This is treated as a global configuration parameter. + */ +static unsigned int tftp_request_blksize = TFTP_MAX_BLKSIZE; /** - * Process TFTP option + * Set TFTP request blocksize + * + * @v blksize Requested block size + */ +void tftp_set_request_blksize ( unsigned int blksize ) { + if ( blksize < TFTP_DEFAULT_BLKSIZE ) + blksize = TFTP_DEFAULT_BLKSIZE; + tftp_request_blksize = blksize; +} + +/** + * Transmit RRQ * * @v tftp TFTP connection - * @v name Option name - * @v value Option value * @ret rc Return status code */ -static int tftp_process_option ( struct tftp_session *tftp, - const char *name, const char *value ) { - struct tftp_option *option; - - for ( option = tftp_options ; option->name ; option++ ) { - if ( strcasecmp ( name, option->name ) == 0 ) - return option->process ( tftp, value ); - } - - DBGC ( tftp, "TFTP %p received unknown option \"%s\" = \"%s\"\n", - tftp, name, value ); +static int tftp_send_rrq ( struct tftp_request *tftp ) { + struct tftp_rrq *rrq; + const char *path = tftp->uri->path; + size_t len = ( sizeof ( *rrq ) + strlen ( path ) + 1 /* NUL */ + + 5 + 1 /* "octet" + NUL */ + + 7 + 1 + 5 + 1 /* "blksize" + NUL + ddddd + NUL */ + + 5 + 1 + 1 + 1 /* "tsize" + NUL + "0" + NUL */ ); + struct io_buffer *iobuf; + + DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, path ); + + /* Allocate buffer */ + iobuf = xfer_alloc_iob ( &tftp->socket, len ); + if ( ! iobuf ) + return -ENOMEM; + + /* Build request */ + rrq = iob_put ( iobuf, sizeof ( *rrq ) ); + rrq->opcode = htons ( TFTP_RRQ ); + iob_put ( iobuf, + snprintf ( iobuf->data, iob_tailroom ( iobuf ), + "%s%coctet%cblksize%c%d%ctsize%c0", path, 0, + 0, 0, tftp_request_blksize, 0, 0 ) + 1 ); - return -EINVAL; + /* RRQ always goes to the address specified in the initial + * xfer_open() call + */ + return xfer_deliver_iob ( &tftp->socket, iobuf ); } -/** Translation between TFTP errors and internal error numbers */ -static const uint8_t tftp_errors[] = { - [TFTP_ERR_FILE_NOT_FOUND] = PXENV_STATUS_TFTP_FILE_NOT_FOUND, - [TFTP_ERR_ACCESS_DENIED] = PXENV_STATUS_TFTP_ACCESS_VIOLATION, - [TFTP_ERR_ILLEGAL_OP] = PXENV_STATUS_TFTP_UNKNOWN_OPCODE, -}; - /** - * Mark TFTP session as complete + * Transmit ACK * * @v tftp TFTP connection - * @v rc Return status code + * @ret rc Return status code */ -static void tftp_done ( struct tftp_session *tftp, int rc ) { +static int tftp_send_ack ( struct tftp_request *tftp ) { + struct tftp_ack *ack; + struct io_buffer *iobuf; + struct xfer_metadata meta = { + .dest = ( struct sockaddr * ) &tftp->peer, + }; - /* Stop the retry timer */ - stop_timer ( &tftp->timer ); + /* Allocate buffer */ + iobuf = xfer_alloc_iob ( &tftp->socket, sizeof ( *ack ) ); + if ( ! iobuf ) + return -ENOMEM; - /* Close UDP connection */ - udp_close ( &tftp->udp ); + /* Build ACK */ + ack = iob_put ( iobuf, sizeof ( *ack ) ); + ack->opcode = htons ( TFTP_ACK ); + ack->block = htons ( tftp->state ); - /* Mark async operation as complete */ - async_done ( &tftp->async, rc ); + /* ACK always goes to the peer recorded from the RRQ response */ + return xfer_deliver_iob_meta ( &tftp->socket, iobuf, &meta ); } /** - * Send next packet in TFTP session + * Transmit data * * @v tftp TFTP connection + * @ret rc Return status code */ -static void tftp_send_packet ( struct tftp_session *tftp ) { +static int tftp_send_packet ( struct tftp_request *tftp ) { + + /* Start retransmission timer */ start_timer ( &tftp->timer ); - udp_senddata ( &tftp->udp ); + + /* Send RRQ or ACK as appropriate */ + if ( tftp->state < 0 ) { + return tftp_send_rrq ( tftp ); + } else { + return tftp_send_ack ( tftp ); + } } /** @@ -163,8 +226,8 @@ static void tftp_send_packet ( struct tftp_session *tftp ) { * @v fail Failure indicator */ static void tftp_timer_expired ( struct retry_timer *timer, int fail ) { - struct tftp_session *tftp = - container_of ( timer, struct tftp_session, timer ); + struct tftp_request *tftp = + container_of ( timer, struct tftp_request, timer ); if ( fail ) { tftp_done ( tftp, -ETIMEDOUT ); @@ -179,7 +242,7 @@ static void tftp_timer_expired ( struct retry_timer *timer, int fail ) { * @v tftp TFTP connection * @v block Block number */ -static void tftp_received ( struct tftp_session *tftp, unsigned int block ) { +static void tftp_received ( struct tftp_request *tftp, unsigned int block ) { /* Stop the retry timer */ stop_timer ( &tftp->timer ); @@ -192,37 +255,94 @@ static void tftp_received ( struct tftp_session *tftp, unsigned int block ) { } /** - * Transmit RRQ + * Process TFTP "blksize" option * * @v tftp TFTP connection - * @v buf Temporary data buffer - * @v len Length of temporary data buffer + * @v value Option value * @ret rc Return status code */ -static int tftp_send_rrq ( struct tftp_session *tftp, void *buf, size_t len ) { - struct tftp_rrq *rrq = buf; - void *data; - void *end; - - DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, tftp->uri->path ); - - data = rrq->data; - end = ( buf + len ); - if ( data > end ) - goto overflow; - data += ( snprintf ( data, ( end - data ), - "%s%coctet%cblksize%c%d%ctsize%c0", - tftp->uri->path, 0, 0, 0, - tftp->request_blksize, 0, 0 ) + 1 ); - if ( data > end ) - goto overflow; - rrq->opcode = htons ( TFTP_RRQ ); +static int tftp_process_blksize ( struct tftp_request *tftp, + const char *value ) { + char *end; + + tftp->blksize = strtoul ( value, &end, 10 ); + if ( *end ) { + DBGC ( tftp, "TFTP %p got invalid blksize \"%s\"\n", + tftp, value ); + return -EINVAL; + } + DBGC ( tftp, "TFTP %p blksize=%d\n", tftp, tftp->blksize ); + + return 0; +} + +/** + * Process TFTP "tsize" option + * + * @v tftp TFTP connection + * @v value Option value + * @ret rc Return status code + */ +static int tftp_process_tsize ( struct tftp_request *tftp, + const char *value ) { + char *end; - return udp_send ( &tftp->udp, buf, ( data - buf ) ); + tftp->tsize = strtoul ( value, &end, 10 ); + if ( *end ) { + DBGC ( tftp, "TFTP %p got invalid tsize \"%s\"\n", + tftp, value ); + return -EINVAL; + } + DBGC ( tftp, "TFTP %p tsize=%ld\n", tftp, tftp->tsize ); - overflow: - DBGC ( tftp, "TFTP %p RRQ out of space\n", tftp ); - return -ENOBUFS; + /* Notify recipient of file size */ + xfer_seek ( &tftp->xfer, tftp->tsize, SEEK_SET ); + xfer_seek ( &tftp->xfer, 0, SEEK_SET ); + + return 0; +} + +/** A TFTP option */ +struct tftp_option { + /** Option name */ + const char *name; + /** Option processor + * + * @v tftp TFTP connection + * @v value Option value + * @ret rc Return status code + */ + int ( * process ) ( struct tftp_request *tftp, const char *value ); +}; + +/** Recognised TFTP options */ +static struct tftp_option tftp_options[] = { + { "blksize", tftp_process_blksize }, + { "tsize", tftp_process_tsize }, + { NULL, NULL } +}; + +/** + * Process TFTP option + * + * @v tftp TFTP connection + * @v name Option name + * @v value Option value + * @ret rc Return status code + */ +static int tftp_process_option ( struct tftp_request *tftp, + const char *name, const char *value ) { + struct tftp_option *option; + + for ( option = tftp_options ; option->name ; option++ ) { + if ( strcasecmp ( name, option->name ) == 0 ) + return option->process ( tftp, value ); + } + + DBGC ( tftp, "TFTP %p received unknown option \"%s\" = \"%s\"\n", + tftp, name, value ); + + return -EINVAL; } /** @@ -233,7 +353,7 @@ static int tftp_send_rrq ( struct tftp_session *tftp, void *buf, size_t len ) { * @v len Length of temporary data buffer * @ret rc Return status code */ -static int tftp_rx_oack ( struct tftp_session *tftp, void *buf, size_t len ) { +static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) { struct tftp_oack *oack = buf; char *end = buf + len; char *name; @@ -276,31 +396,34 @@ static int tftp_rx_oack ( struct tftp_session *tftp, void *buf, size_t len ) { * Receive DATA * * @v tftp TFTP connection - * @v buf Temporary data buffer - * @v len Length of temporary data buffer + * @v iobuf I/O buffer * @ret rc Return status code + * + * Takes ownership of I/O buffer. */ -static int tftp_rx_data ( struct tftp_session *tftp, void *buf, size_t len ) { - struct tftp_data *data = buf; +static int tftp_rx_data ( struct tftp_request *tftp, + struct io_buffer *iobuf ) { + struct tftp_data *data = iobuf->data; unsigned int block; - size_t data_offset; size_t data_len; int rc; /* Sanity check */ - if ( len < sizeof ( *data ) ) { + if ( iob_len ( iobuf ) < sizeof ( *data ) ) { DBGC ( tftp, "TFTP %p received underlength DATA packet " - "length %d\n", tftp, len ); + "length %d\n", tftp, iob_len ( iobuf ) ); + free_iob ( iobuf ); return -EINVAL; } - /* Fill data buffer */ + /* Extract data */ block = ntohs ( data->block ); - data_offset = ( ( block - 1 ) * tftp->blksize ); - data_len = ( len - offsetof ( typeof ( *data ), data ) ); - if ( ( rc = fill_buffer ( tftp->buffer, data->data, data_offset, - data_len ) ) != 0 ) { - DBGC ( tftp, "TFTP %p could not fill data buffer: %s\n", + iob_pull ( iobuf, sizeof ( *data ) ); + data_len = iob_len ( iobuf ); + + /* Deliver data */ + if ( ( rc = xfer_deliver_iob ( &tftp->xfer, iobuf ) ) != 0 ) { + DBGC ( tftp, "TFTP %p could not deliver data: %s\n", tftp, strerror ( rc ) ); tftp_done ( tftp, rc ); return rc; @@ -316,21 +439,12 @@ static int tftp_rx_data ( struct tftp_session *tftp, void *buf, size_t len ) { return 0; } -/** - * Transmit ACK - * - * @v tftp TFTP connection - * @v buf Temporary data buffer - * @v len Length of temporary data buffer - * @ret rc Return status code - */ -static int tftp_send_ack ( struct tftp_session *tftp ) { - struct tftp_ack ack; - - ack.opcode = htons ( TFTP_ACK ); - ack.block = htons ( tftp->state ); - return udp_send ( &tftp->udp, &ack, sizeof ( ack ) ); -} +/** Translation between TFTP errors and internal error numbers */ +static const uint8_t tftp_errors[] = { + [TFTP_ERR_FILE_NOT_FOUND] = PXENV_STATUS_TFTP_FILE_NOT_FOUND, + [TFTP_ERR_ACCESS_DENIED] = PXENV_STATUS_TFTP_ACCESS_VIOLATION, + [TFTP_ERR_ILLEGAL_OP] = PXENV_STATUS_TFTP_UNKNOWN_OPCODE, +}; /** * Receive ERROR @@ -340,7 +454,7 @@ static int tftp_send_ack ( struct tftp_session *tftp ) { * @v len Length of temporary data buffer * @ret rc Return status code */ -static int tftp_rx_error ( struct tftp_session *tftp, void *buf, size_t len ) { +static int tftp_rx_error ( struct tftp_request *tftp, void *buf, size_t len ) { struct tftp_error *error = buf; unsigned int err; int rc = 0; @@ -362,33 +476,13 @@ static int tftp_rx_error ( struct tftp_session *tftp, void *buf, size_t len ) { if ( ! rc ) rc = -PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION; - /* Close TFTP session */ + /* Close TFTP request */ tftp_done ( tftp, rc ); return 0; } /** - * Transmit data - * - * @v conn UDP connection - * @v buf Temporary data buffer - * @v len Length of temporary data buffer - * @ret rc Return status code - */ -static int tftp_senddata ( struct udp_connection *conn, - void *buf, size_t len ) { - struct tftp_session *tftp = - container_of ( conn, struct tftp_session, udp ); - - if ( tftp->state < 0 ) { - return tftp_send_rrq ( tftp, buf, len ); - } else { - return tftp_send_ack ( tftp ); - } -} - -/** * Receive new data * * @v udp UDP connection @@ -397,141 +491,179 @@ static int tftp_senddata ( struct udp_connection *conn, * @v st_src Partially-filled source address * @v st_dest Partially-filled destination address */ -static int tftp_newdata ( struct udp_connection *conn, void *data, size_t len, - struct sockaddr_tcpip *st_src __unused, - struct sockaddr_tcpip *st_dest __unused ) { - struct tftp_session *tftp = - container_of ( conn, struct tftp_session, udp ); - struct tftp_common *common = data; +static int tftp_socket_deliver_iob ( struct xfer_interface *socket, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct tftp_request *tftp = + container_of ( socket, struct tftp_request, socket ); + struct sockaddr_tcpip *st_src; + struct tftp_common *common = iobuf->data; + size_t len = iob_len ( iobuf ); + int rc = -EINVAL; + /* Sanity checks */ if ( len < sizeof ( *common ) ) { DBGC ( tftp, "TFTP %p received underlength packet length %d\n", tftp, len ); - return -EINVAL; + goto done; + } + if ( ! meta ) { + DBGC ( tftp, "TFTP %p received packet without metadata\n", + tftp ); + goto done; + } + if ( ! meta->src ) { + DBGC ( tftp, "TFTP %p received packet without source port\n", + tftp ); + goto done; } /* Filter by TID. Set TID on first response received */ - if ( tftp->tid ) { - if ( tftp->tid != st_src->st_port ) { - DBGC ( tftp, "TFTP %p received packet from wrong port " - "(got %d, wanted %d)\n", tftp, - ntohs ( st_src->st_port ), ntohs ( tftp->tid )); - return -EINVAL; - } - } else { - tftp->tid = st_src->st_port; + st_src = ( struct sockaddr_tcpip * ) meta->src; + if ( tftp->state < 0 ) { + memcpy ( &tftp->peer, st_src, sizeof ( tftp->peer ) ); DBGC ( tftp, "TFTP %p using remote port %d\n", tftp, - ntohs ( tftp->tid ) ); - udp_connect_port ( &tftp->udp, tftp->tid ); - } - - /* Filter by source address */ - if ( memcmp ( st_src, udp_peer ( &tftp->udp ), - sizeof ( *st_src ) ) != 0 ) { - DBGC ( tftp, "TFTP %p received packet from foreign source\n", - tftp ); - return -EINVAL; + ntohs ( tftp->peer.st_port ) ); + } else if ( memcmp ( &tftp->peer, st_src, + sizeof ( tftp->peer ) ) != 0 ) { + DBGC ( tftp, "TFTP %p received packet from wrong source (got " + "%d, wanted %d)\n", tftp, ntohs ( st_src->st_port ), + ntohs ( tftp->peer.st_port ) ); + goto done; } switch ( common->opcode ) { case htons ( TFTP_OACK ): - return tftp_rx_oack ( tftp, data, len ); + rc = tftp_rx_oack ( tftp, iobuf->data, len ); + break; case htons ( TFTP_DATA ): - return tftp_rx_data ( tftp, data, len ); + rc = tftp_rx_data ( tftp, iobuf ); + iobuf = NULL; + break; case htons ( TFTP_ERROR ): - return tftp_rx_error ( tftp, data, len ); + rc = tftp_rx_error ( tftp, iobuf->data, len ); + break; default: - DBGC ( tftp, "TFTP %p received strange packet type %d\n", tftp, - ntohs ( common->opcode ) ); - return -EINVAL; + DBGC ( tftp, "TFTP %p received strange packet type %d\n", + tftp, ntohs ( common->opcode ) ); + break; }; + + done: + free_iob ( iobuf ); + return rc; } -/** TFTP UDP operations */ -static struct udp_operations tftp_udp_operations = { - .senddata = tftp_senddata, - .newdata = tftp_newdata, -}; +/** + * TFTP connection closed by network stack + * + * @v socket Transport layer interface + * @v rc Reason for close + */ +static void tftp_socket_close ( struct xfer_interface *socket, int rc ) { + struct tftp_request *tftp = + container_of ( socket, struct tftp_request, socket ); + + DBGC ( tftp, "TFTP %p socket closed: %s\n", + tftp, strerror ( rc ) ); + tftp_done ( tftp, rc ); +} + +/** TFTP socket operations */ +static struct xfer_interface_operations tftp_socket_operations = { + .close = tftp_socket_close, + .vredirect = xfer_vopen, + .request = ignore_xfer_request, + .seek = ignore_xfer_seek, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = tftp_socket_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + /** - * Reap asynchronous operation + * Close TFTP data transfer interface * - * @v async Asynchronous operation + * @v xfer Data transfer interface + * @v rc Reason for close */ -static void tftp_reap ( struct async *async ) { - struct tftp_session *tftp = - container_of ( async, struct tftp_session, async ); +static void tftp_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct tftp_request *tftp = + container_of ( xfer, struct tftp_request, xfer ); - free ( tftp ); + DBGC ( tftp, "TFTP %p interface closed: %s\n", + tftp, strerror ( rc ) ); + + tftp_done ( tftp, rc ); } -/** TFTP asynchronous operations */ -static struct async_operations tftp_async_operations = { - .reap = tftp_reap, +/** TFTP data transfer interface operations */ +static struct xfer_interface_operations tftp_xfer_operations = { + .close = tftp_xfer_close, + .vredirect = ignore_xfer_vredirect, + .request = ignore_xfer_request, + .seek = ignore_xfer_seek, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, }; /** * Initiate TFTP download * + * @v xfer Data transfer interface * @v uri Uniform Resource Identifier - * @v buffer Buffer into which to download file - * @v parent Parent asynchronous operation * @ret rc Return status code */ -int tftp_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) { - struct tftp_session *tftp = NULL; +int tftp_open ( struct xfer_interface *xfer, struct uri *uri ) { + struct tftp_request *tftp; + struct sockaddr_tcpip server; int rc; /* Sanity checks */ - if ( ! uri->path ) { - rc = -EINVAL; - goto err; - } + if ( ! uri->host ) + return -EINVAL; + if ( ! uri->path ) + return -EINVAL; /* Allocate and populate TFTP structure */ tftp = malloc ( sizeof ( *tftp ) ); - if ( ! tftp ) { - rc = -ENOMEM; - goto err; - } + if ( ! tftp ) + return -ENOMEM; memset ( tftp, 0, sizeof ( *tftp ) ); - tftp->uri = uri; - tftp->buffer = buffer; - if ( ! tftp->request_blksize ) - tftp->request_blksize = TFTP_MAX_BLKSIZE; - tftp->blksize = TFTP_DEFAULT_BLKSIZE; + tftp->refcnt.free = tftp_free; + xfer_init ( &tftp->xfer, &tftp_xfer_operations, &tftp->refcnt ); + tftp->uri = uri_get ( uri ); + xfer_init ( &tftp->socket, &tftp_socket_operations, &tftp->refcnt ); tftp->state = -1; - tftp->udp.udp_op = &tftp_udp_operations; tftp->timer.expired = tftp_timer_expired; - -#warning "Quick name resolution hack" - union { - struct sockaddr_tcpip st; - struct sockaddr_in sin; - } server; - server.sin.sin_port = htons ( TFTP_PORT ); - server.sin.sin_family = AF_INET; - if ( inet_aton ( uri->host, &server.sin.sin_addr ) == 0 ) { - rc = -EINVAL; - goto err; - } - udp_connect ( &tftp->udp, &server.st ); - - - /* Open UDP connection */ - if ( ( rc = udp_open ( &tftp->udp, 0 ) ) != 0 ) + /* Open socket */ + memset ( &server, 0, sizeof ( server ) ); + server.st_port = htons ( uri_port ( tftp->uri, TFTP_PORT ) ); + if ( ( rc = xfer_open_named_socket ( &tftp->socket, SOCK_DGRAM, + ( struct sockaddr * ) &server, + uri->host, NULL ) ) != 0 ) goto err; - /* Transmit initial RRQ */ - tftp_send_packet ( tftp ); + /* Start timer to initiate RRQ */ + start_timer ( &tftp->timer ); - async_init ( &tftp->async, &tftp_async_operations, parent ); + /* Attach to parent interface, mortalise self, and return */ + xfer_plug_plug ( &tftp->xfer, xfer ); + ref_put ( &tftp->refcnt ); return 0; err: - DBGC ( tftp, "TFTP %p could not create session: %s\n", + DBGC ( tftp, "TFTP %p could not create request: %s\n", tftp, strerror ( rc ) ); - free ( tftp ); + tftp_done ( tftp, rc ); + ref_put ( &tftp->refcnt ); return rc; } + +/** TFTP URI opener */ +struct uri_opener tftp_uri_opener __uri_opener = { + .scheme = "tftp", + .open = tftp_open, +}; |