From df0397f33469f9e3ed1d54cedd5fe6282881e3fc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jan 2007 09:58:26 +0000 Subject: [PATCH] Update TFTP and FTP to take the same temporary URI scheme as HTTP --- src/include/gpxe/ftp.h | 15 ++--- src/include/gpxe/tftp.h | 17 +++--- src/net/tcp/ftp.c | 130 +++++++++++++++++++++++++--------------- src/net/udp/tftp.c | 82 +++++++++++++++++++------ src/usr/fetch.c | 21 ++++--- 5 files changed, 174 insertions(+), 91 deletions(-) diff --git a/src/include/gpxe/ftp.h b/src/include/gpxe/ftp.h index 06799d24..64e8d4e4 100644 --- a/src/include/gpxe/ftp.h +++ b/src/include/gpxe/ftp.h @@ -38,12 +38,12 @@ enum ftp_state { * */ struct ftp_request { - /** Server address */ - struct sockaddr_tcpip server; - /** File to download */ - const char *filename; + /** URI being fetched */ + struct uri *uri; /** Data buffer to fill */ struct buffer *buffer; + /** Asynchronous operation */ + struct async async; /** Current state */ enum ftp_state state; @@ -57,16 +57,13 @@ struct ftp_request { char status_text[4]; /** Passive-mode parameters, as text */ char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */ - /** TCP application for the control channel */ struct tcp_application tcp; /** TCP application for the data channel */ struct tcp_application tcp_data; - - /** Asynchronous operation for this FTP operation */ - struct async async; }; -struct async_operation * ftp_get ( struct ftp_request *ftp ); +extern int ftp_get ( struct uri *uri, struct buffer *buffer, + struct async *parent ); #endif /* _GPXE_FTP_H */ diff --git a/src/include/gpxe/tftp.h b/src/include/gpxe/tftp.h index 551a6fc9..cb643d9c 100644 --- a/src/include/gpxe/tftp.h +++ b/src/include/gpxe/tftp.h @@ -86,12 +86,13 @@ union tftp_any { * This data structure holds the state for an ongoing TFTP transfer. */ struct tftp_session { - /** UDP connection */ - struct udp_connection udp; - /** Filename */ - const char *filename; + /** URI being fetched */ + struct uri *uri; /** Data buffer to fill */ struct buffer *buffer; + /** Asynchronous operation */ + struct async async; + /** Requested data block size * * This is the "blksize" option requested from the TFTP @@ -133,15 +134,15 @@ struct tftp_session { * (i.e. that no blocks have yet been received). */ int state; - - /** Asynchronous operation for this session */ - struct async async; + /** UDP connection */ + struct udp_connection udp; /** Retransmission timer */ struct retry_timer timer; }; /* Function prototypes */ -extern struct async_operation * tftp_get ( struct tftp_session *tftp ); +extern int tftp_get ( struct uri *uri, struct buffer *buffer, + struct async *parent ); #endif /* _GPXE_TFTP_H */ diff --git a/src/net/tcp/ftp.c b/src/net/tcp/ftp.c index 8a27f8cb..f3921ab7 100644 --- a/src/net/tcp/ftp.c +++ b/src/net/tcp/ftp.c @@ -6,6 +6,7 @@ #include #include #include +#include #include /** @file @@ -20,43 +21,22 @@ * */ -/** An FTP control channel string */ -struct ftp_string { - /** String format */ - const char *format; - /** Offset to string data - * - * This is the offset within the struct ftp_request to the - * pointer to the string data. Use ftp_string_data() to get a - * pointer to the actual data. - */ - off_t data_offset; -}; - -/** FTP control channel strings */ -static const struct ftp_string ftp_strings[] = { - [FTP_CONNECT] = { "", 0 }, - [FTP_USER] = { "USER anonymous\r\n", 0 }, - [FTP_PASS] = { "PASS etherboot@etherboot.org\r\n", 0 }, - [FTP_TYPE] = { "TYPE I\r\n", 0 }, - [FTP_PASV] = { "PASV\r\n", 0 }, - [FTP_RETR] = { "RETR %s\r\n", - offsetof ( struct ftp_request, filename ) }, - [FTP_QUIT] = { "QUIT\r\n", 0 }, - [FTP_DONE] = { "", 0 }, -}; - -/** - * Get data associated with an FTP control channel string +/** FTP control channel strings * - * @v ftp FTP request - * @v data_offset Data offset field from ftp_string structure - * @ret data Pointer to data + * These are used as printf() format strings. Since only one of them + * (RETR) takes an argument, we always supply that argument to the + * snprintf() call. */ -static inline const void * ftp_string_data ( struct ftp_request *ftp, - off_t data_offset ) { - return * ( ( void ** ) ( ( ( void * ) ftp ) + data_offset ) ); -} +static const char * ftp_strings[] = { + [FTP_CONNECT] = "", + [FTP_USER] = "USER anonymous\r\n", + [FTP_PASS] = "PASS etherboot@etherboot.org\r\n", + [FTP_TYPE] = "TYPE I\r\n", + [FTP_PASV] = "PASV\r\n", + [FTP_RETR] = "RETR %s\r\n", + [FTP_QUIT] = "QUIT\r\n", + [FTP_DONE] = "", +}; /** * Get FTP request from control TCP application @@ -163,10 +143,8 @@ static void ftp_reply ( struct ftp_request *ftp ) { ftp->already_sent = 0; if ( ftp->state < FTP_DONE ) { - const struct ftp_string *string = &ftp_strings[ftp->state]; DBGC ( ftp, "FTP %p sending ", ftp ); - DBGC ( ftp, string->format, - ftp_string_data ( ftp, string->data_offset ) ); + DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path ); } return; @@ -250,14 +228,11 @@ static void ftp_acked ( struct tcp_application *app, size_t len ) { static void ftp_senddata ( struct tcp_application *app, void *buf, size_t len ) { struct ftp_request *ftp = tcp_to_ftp ( app ); - const struct ftp_string *string; /* Send the as-yet-unACKed portion of the string for the * current state. */ - string = &ftp_strings[ftp->state]; - len = snprintf ( buf, len, string->format, - ftp_string_data ( ftp, string->data_offset ) ); + len = snprintf ( buf, len, ftp_strings[ftp->state], ftp->uri->path ); tcp_send ( app, buf + ftp->already_sent, len - ftp->already_sent ); } @@ -360,24 +335,83 @@ static struct tcp_operations ftp_data_tcp_operations = { * */ +/** + * Reap asynchronous operation + * + * @v async Asynchronous operation + */ +static void ftp_reap ( struct async *async ) { + struct ftp_request *ftp = + container_of ( async, struct ftp_request, async ); + + free ( ftp ); +} + +/** FTP asynchronous operations */ +static struct async_operations ftp_async_operations = { + .reap = ftp_reap, +}; + +#warning "Quick name resolution hack" +#include + /** * Initiate an FTP connection * - * @v ftp FTP request + * @v uri Uniform Resource Identifier + * @v buffer Buffer into which to download file + * @v parent Parent asynchronous operation + * @ret rc Return status code */ -struct async_operation * ftp_get ( struct ftp_request *ftp ) { +int ftp_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) { + struct ftp_request *ftp = NULL; int rc; - DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->filename ); + /* Sanity checks */ + if ( ! uri->path ) { + rc = -EINVAL; + goto err; + } + /* Allocate and populate FTP structure */ + ftp = malloc ( sizeof ( *ftp ) ); + if ( ! ftp ) { + rc = -ENOMEM; + goto err; + } + memset ( ftp, 0, sizeof ( *ftp ) ); + ftp->uri = uri; + ftp->buffer = buffer; ftp->state = FTP_CONNECT; ftp->already_sent = 0; ftp->recvbuf = ftp->status_text; ftp->recvsize = sizeof ( ftp->status_text ) - 1; ftp->tcp.tcp_op = &ftp_tcp_operations; ftp->tcp_data.tcp_op = &ftp_data_tcp_operations; - if ( ( rc = tcp_connect ( &ftp->tcp, &ftp->server, 0 ) ) != 0 ) - ftp_done ( ftp, rc ); - return &ftp->async; +#warning "Quick name resolution hack" + union { + struct sockaddr_tcpip st; + struct sockaddr_in sin; + } server; + server.sin.sin_port = htons ( FTP_PORT ); + server.sin.sin_family = AF_INET; + if ( inet_aton ( uri->host, &server.sin.sin_addr ) == 0 ) { + rc = -EINVAL; + goto err; + } + + DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path ); + + if ( ( rc = tcp_connect ( &ftp->tcp, &server.st, 0 ) ) != 0 ) + goto err; + + async_init ( &ftp->async, &ftp_async_operations, parent ); + return 0; + + err: + DBGC ( ftp, "FTP %p could not create request: %s\n", + ftp, strerror ( rc ) ); + free ( ftp ); + return rc; } diff --git a/src/net/udp/tftp.c b/src/net/udp/tftp.c index 69650ab0..992b82ba 100644 --- a/src/net/udp/tftp.c +++ b/src/net/udp/tftp.c @@ -26,6 +26,7 @@ #include #include #include +#include /** @file * @@ -203,7 +204,7 @@ static int tftp_send_rrq ( struct tftp_session *tftp, void *buf, size_t len ) { void *data; void *end; - DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, tftp->filename ); + DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, tftp->uri->path ); data = rrq->data; end = ( buf + len ); @@ -211,7 +212,7 @@ static int tftp_send_rrq ( struct tftp_session *tftp, void *buf, size_t len ) { goto overflow; data += ( snprintf ( data, ( end - data ), "%s%coctet%cblksize%c%d%ctsize%c0", - tftp->filename, 0, 0, 0, + tftp->uri->path, 0, 0, 0, tftp->request_blksize, 0, 0 ) + 1 ); if ( data > end ) goto overflow; @@ -452,38 +453,85 @@ static struct udp_operations tftp_udp_operations = { .newdata = tftp_newdata, }; +/** + * Reap asynchronous operation + * + * @v async Asynchronous operation + */ +static void tftp_reap ( struct async *async ) { + struct tftp_session *tftp = + container_of ( async, struct tftp_session, async ); + + free ( tftp ); +} + +/** TFTP asynchronous operations */ +static struct async_operations tftp_async_operations = { + .reap = tftp_reap, +}; + /** * Initiate TFTP download * - * @v tftp TFTP session - * @ret aop Asynchronous operation + * @v uri Uniform Resource Identifier + * @v buffer Buffer into which to download file + * @v parent Parent asynchronous operation + * @ret rc Return status code */ -struct async_operation * tftp_get ( struct tftp_session *tftp ) { +int tftp_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) { + struct tftp_session *tftp = NULL; int rc; - assert ( tftp->filename != NULL ); - assert ( tftp->buffer != NULL ); - assert ( tftp->udp.peer.st_family != 0 ); + /* Sanity checks */ + if ( ! uri->path ) { + rc = -EINVAL; + goto err; + } - /* Initialise TFTP session */ + /* Allocate and populate TFTP structure */ + tftp = malloc ( sizeof ( *tftp ) ); + if ( ! tftp ) { + rc = -ENOMEM; + goto err; + } + 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->tsize = 0; - tftp->tid = 0; tftp->state = -1; tftp->udp.udp_op = &tftp_udp_operations; tftp->timer.expired = tftp_timer_expired; - /* Open UDP connection */ - if ( ( rc = udp_open ( &tftp->udp, 0 ) ) != 0 ) { - async_done ( &tftp->async, rc ); - goto out; + +#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 ) + goto err; /* Transmit initial RRQ */ tftp_send_packet ( tftp ); - out: - return &tftp->async; + async_init ( &tftp->async, &tftp_async_operations, parent ); + return 0; + + err: + DBGC ( tftp, "TFTP %p could not create session: %s\n", + tftp, strerror ( rc ) ); + free ( tftp ); + return rc; } diff --git a/src/usr/fetch.c b/src/usr/fetch.c index 71304bb2..45031049 100644 --- a/src/usr/fetch.c +++ b/src/usr/fetch.c @@ -35,6 +35,7 @@ #include #include #include +#include /** * Fetch file @@ -73,15 +74,17 @@ int fetch ( const char *uri_string, userptr_t *data, size_t *len ) { int ( * download ) ( struct uri *uri, struct buffer *buffer, struct async *parent ); -#if 0 - server.sin.sin_port = htons ( TFTP_PORT ); - udp_connect ( &tftp.udp, &server.st ); - tftp.filename = filename; - tftp.buffer = &buffer; - aop = tftp_get ( &tftp ); -#else - download = http_get; -#endif + if ( ! uri->scheme ) { + download = tftp_get; + } else { + if ( strcmp ( uri->scheme, "http" ) == 0 ) { + download = http_get; + } else if ( strcmp ( uri->scheme, "ftp" ) == 0 ) { + download = ftp_get; + } else { + download = tftp_get; + } + } async_init_orphan ( &async ); if ( ( rc = download ( uri, &buffer, &async ) ) != 0 )