Update TFTP and FTP to take the same temporary URI scheme as HTTP
authorMichael Brown <mcb30@etherboot.org>
Mon, 15 Jan 2007 09:58:26 +0000 (09:58 +0000)
committerMichael Brown <mcb30@etherboot.org>
Mon, 15 Jan 2007 09:58:26 +0000 (09:58 +0000)
src/include/gpxe/ftp.h
src/include/gpxe/tftp.h
src/net/tcp/ftp.c
src/net/udp/tftp.c
src/usr/fetch.c

index 06799d2..64e8d4e 100644 (file)
@@ -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 */
index 551a6fc..cb643d9 100644 (file)
@@ -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 */
index 8a27f8c..f3921ab 100644 (file)
@@ -6,6 +6,7 @@
 #include <errno.h>
 #include <gpxe/async.h>
 #include <gpxe/buffer.h>
+#include <gpxe/uri.h>
 #include <gpxe/ftp.h>
 
 /** @file
  *
  */
 
-/** 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 <byteswap.h>
+
 /**
  * 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;
 }
index 69650ab..992b82b 100644 (file)
@@ -26,6 +26,7 @@
 #include <vsprintf.h>
 #include <gpxe/async.h>
 #include <gpxe/tftp.h>
+#include <gpxe/uri.h>
 
 /** @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;
 }
index 71304bb..4503104 100644 (file)
@@ -35,6 +35,7 @@
 #include <gpxe/dhcp.h>
 #include <gpxe/tftp.h>
 #include <gpxe/http.h>
+#include <gpxe/ftp.h>
 
 /**
  * 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 )