iPXE
Data Structures | Enumerations | Functions | Variables
ftp.c File Reference

File transfer protocol. More...

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <byteswap.h>
#include <ipxe/socket.h>
#include <ipxe/tcpip.h>
#include <ipxe/in.h>
#include <ipxe/iobuf.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/uri.h>
#include <ipxe/features.h>
#include <ipxe/ftp.h>

Go to the source code of this file.

Data Structures

struct  ftp_request
 An FTP request. More...
struct  ftp_control_string
 An FTP control channel string. More...

Enumerations

enum  ftp_state {
  FTP_CONNECT = 0, FTP_USER, FTP_PASS, FTP_TYPE,
  FTP_SIZE, FTP_PASV, FTP_RETR, FTP_WAIT,
  FTP_QUIT, FTP_DONE
}
 FTP states. More...

Functions

 FEATURE (FEATURE_PROTOCOL,"FTP", DHCP_EB_FEATURE_FTP, 1)
static void ftp_free (struct refcnt *refcnt)
 Free FTP request.
static void ftp_done (struct ftp_request *ftp, int rc)
 Mark FTP operation as complete.
static const char * ftp_uri_path (struct ftp_request *ftp)
 Retrieve FTP pathname.
static const char * ftp_user (struct ftp_request *ftp)
 Retrieve FTP user.
static const char * ftp_password (struct ftp_request *ftp)
 Retrieve FTP password.
static void ftp_parse_value (char **text, uint8_t *value, size_t len)
 Parse FTP byte sequence value.
static void ftp_next_state (struct ftp_request *ftp)
 Move to next state and send the appropriate FTP control string.
static void ftp_reply (struct ftp_request *ftp)
 Handle an FTP control channel response.
static int ftp_control_deliver (struct ftp_request *ftp, struct io_buffer *iobuf, struct xfer_metadata *meta __unused)
 Handle new data arriving on FTP control channel.
static void ftp_data_closed (struct ftp_request *ftp, int rc)
 Handle FTP data channel being closed.
static int ftp_check_string (const char *string)
 Check validity of FTP control channel string.
static int ftp_open (struct interface *xfer, struct uri *uri)
 Initiate an FTP connection.

Variables

static struct ftp_control_string ftp_strings []
 FTP control channel strings.
static struct interface_operation ftp_control_operations []
 FTP control channel interface operations.
static struct interface_descriptor ftp_control_desc
 FTP control channel interface descriptor.
static struct interface_operation ftp_data_operations []
 FTP data channel interface operations.
static struct interface_descriptor ftp_data_desc
 FTP data channel interface descriptor.
static struct interface_operation ftp_xfer_operations []
 FTP data transfer interface operations.
static struct interface_descriptor ftp_xfer_desc
 FTP data transfer interface descriptor.
struct uri_opener ftp_uri_opener __uri_opener
 FTP URI opener.

Detailed Description

File transfer protocol.

Definition in file ftp.c.


Enumeration Type Documentation

enum ftp_state

FTP states.

These must be sequential, i.e. a successful FTP session must pass through each of these states in order.

Enumerator:
FTP_CONNECT 
FTP_USER 
FTP_PASS 
FTP_TYPE 
FTP_SIZE 
FTP_PASV 
FTP_RETR 
FTP_WAIT 
FTP_QUIT 
FTP_DONE 

Definition at line 52 of file ftp.c.


Function Documentation

FEATURE ( FEATURE_PROTOCOL  ,
"FTP"  ,
DHCP_EB_FEATURE_FTP  ,
 
)
static void ftp_free ( struct refcnt refcnt) [static]

Free FTP request.

Parameters:
refcntReference counter

Definition at line 101 of file ftp.c.

References container_of, DBGC, free, ftp_request::uri, and uri_put().

Referenced by ftp_open().

                                               {
        struct ftp_request *ftp =
                container_of ( refcnt, struct ftp_request, refcnt );

        DBGC ( ftp, "FTP %p freed\n", ftp );

        uri_put ( ftp->uri );
        free ( ftp );
}
static void ftp_done ( struct ftp_request ftp,
int  rc 
) [static]

Mark FTP operation as complete.

Parameters:
ftpFTP request
rcReturn status code

Definition at line 117 of file ftp.c.

References ftp_request::control, ftp_request::data, DBGC, intf_shutdown(), strerror(), and ftp_request::xfer.

Referenced by ftp_data_closed(), ftp_open(), and ftp_reply().

                                                         {

        DBGC ( ftp, "FTP %p completed (%s)\n", ftp, strerror ( rc ) );

        /* Close all data transfer interfaces */
        intf_shutdown ( &ftp->data, rc );
        intf_shutdown ( &ftp->control, rc );
        intf_shutdown ( &ftp->xfer, rc );
}
static const char* ftp_uri_path ( struct ftp_request ftp) [static]

Retrieve FTP pathname.

Parameters:
ftpFTP request
Return values:
pathFTP pathname

Definition at line 151 of file ftp.c.

References uri::path, and ftp_request::uri.

                                                             {
        return ftp->uri->path;
}
static const char* ftp_user ( struct ftp_request ftp) [static]

Retrieve FTP user.

Parameters:
ftpFTP request
Return values:
userFTP user

Definition at line 161 of file ftp.c.

References ftp_request::uri, and uri::user.

                                                         {
        static char *ftp_default_user = "anonymous";
        return ftp->uri->user ? ftp->uri->user : ftp_default_user;
}
static const char* ftp_password ( struct ftp_request ftp) [static]

Retrieve FTP password.

Parameters:
ftpFTP request
Return values:
passwordFTP password

Definition at line 172 of file ftp.c.

References uri::password, and ftp_request::uri.

                                                             {
        static char *ftp_default_password = "ipxe@ipxe.org";
        return ftp->uri->password ? ftp->uri->password : ftp_default_password;
}
static void ftp_parse_value ( char **  text,
uint8_t value,
size_t  len 
) [static]

Parse FTP byte sequence value.

Parameters:
textText string
valueValue buffer
lenLength of value buffer

This parses an FTP byte sequence value (e.g. the "aaa,bbb,ccc,ddd" form for IP addresses in PORT commands) into a byte sequence. *text will be updated to point beyond the end of the parsed byte sequence.

This function is safe in the presence of malformed data, though the output is undefined.

Definition at line 206 of file ftp.c.

References strtoul().

Referenced by ftp_reply().

                                                                        {
        do {
                *(value++) = strtoul ( *text, text, 10 );
                if ( **text )
                        (*text)++;
        } while ( --len );
}
static void ftp_next_state ( struct ftp_request ftp) [static]

Move to next state and send the appropriate FTP control string.

Parameters:
ftpFTP request

Definition at line 220 of file ftp.c.

References ftp_request::control, DBGC, FTP_DONE, ftp_control_string::literal, ftp_request::state, ftp_control_string::variable, and xfer_printf().

Referenced by ftp_data_closed(), and ftp_reply().

                                                       {
        struct ftp_control_string *ftp_string;
        const char *literal;
        const char *variable;

        /* Move to next state */
        if ( ftp->state < FTP_DONE )
                ftp->state++;

        /* Send control string if needed */
        ftp_string = &ftp_strings[ftp->state];
        literal = ftp_string->literal;
        variable = ( ftp_string->variable ?
                     ftp_string->variable ( ftp ) : "" );
        if ( literal ) {
                DBGC ( ftp, "FTP %p sending %s%s\n", ftp, literal, variable );
                xfer_printf ( &ftp->control, "%s%s\r\n", literal, variable );
        }
}
static void ftp_reply ( struct ftp_request ftp) [static]

Handle an FTP control channel response.

Parameters:
ftpFTP request

This is called once we have received a complete response line.

Definition at line 247 of file ftp.c.

References AF_INET, ftp_request::data, DBGC, EPROTO, ftp_request::filesize, ftp_done(), ftp_next_state(), ftp_parse_value(), FTP_PASV, FTP_SIZE, FTP_USER, NULL, ftp_request::passive_text, rc, sa, SOCK_STREAM, ftp_request::state, ftp_request::status_text, strtoul(), ftp_request::xfer, xfer_open_socket(), and xfer_seek().

Referenced by ftp_control_deliver().

                                                  {
        char status_major = ftp->status_text[0];
        char separator = ftp->status_text[3];

        DBGC ( ftp, "FTP %p received status %s\n", ftp, ftp->status_text );

        /* Ignore malformed lines */
        if ( separator != ' ' )
                return;

        /* Ignore "intermediate" responses (1xx codes) */
        if ( status_major == '1' )
                return;

        /* If the SIZE command is not supported by the server, we go to
         * the next step.
         */
        if ( ( status_major == '5' ) && ( ftp->state == FTP_SIZE ) ) {
                ftp_next_state ( ftp );
                return;
        }

        /* Anything other than success (2xx) or, in the case of a
         * repsonse to a "USER" command, a password prompt (3xx), is a
         * fatal error.
         */
        if ( ! ( ( status_major == '2' ) ||
                 ( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) ){
                /* Flag protocol error and close connections */
                ftp_done ( ftp, -EPROTO );
                return;
        }

        /* Parse file size */
        if ( ftp->state == FTP_SIZE ) {
                size_t filesize;
                char *endptr;

                /* Parse size */
                filesize = strtoul ( ftp->filesize, &endptr, 10 );
                if ( *endptr != '\0' ) {
                        DBGC ( ftp, "FTP %p invalid SIZE \"%s\"\n",
                               ftp, ftp->filesize );
                        ftp_done ( ftp, -EPROTO );
                        return;
                }

                /* Use seek() to notify recipient of filesize */
                DBGC ( ftp, "FTP %p file size is %zd bytes\n", ftp, filesize );
                xfer_seek ( &ftp->xfer, filesize );
                xfer_seek ( &ftp->xfer, 0 );
        }

        /* Open passive connection when we get "PASV" response */
        if ( ftp->state == FTP_PASV ) {
                char *ptr = ftp->passive_text;
                union {
                        struct sockaddr_in sin;
                        struct sockaddr sa;
                } sa;
                int rc;

                sa.sin.sin_family = AF_INET;
                ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_addr,
                                  sizeof ( sa.sin.sin_addr ) );
                ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_port,
                                  sizeof ( sa.sin.sin_port ) );
                if ( ( rc = xfer_open_socket ( &ftp->data, SOCK_STREAM,
                                               &sa.sa, NULL ) ) != 0 ) {
                        DBGC ( ftp, "FTP %p could not open data connection\n",
                               ftp );
                        ftp_done ( ftp, rc );
                        return;
                }
        }

        /* Move to next state and send control string */
        ftp_next_state ( ftp );
        
}
static int ftp_control_deliver ( struct ftp_request ftp,
struct io_buffer iobuf,
struct xfer_metadata *meta  __unused 
) [static]

Handle new data arriving on FTP control channel.

Parameters:
ftpFTP request
iobI/O buffer
metaData transfer metadata
Return values:
rcReturn status code

Data is collected until a complete line is received, at which point its information is passed to ftp_reply().

Definition at line 339 of file ftp.c.

References io_buffer::data, data, ftp_request::filesize, free_iob(), FTP_PASV, ftp_reply(), FTP_SIZE, iob_len(), len, ftp_request::passive_text, ftp_request::recvbuf, ftp_request::recvsize, ftp_request::state, and ftp_request::status_text.

                                                                       {
        char *data = iobuf->data;
        size_t len = iob_len ( iobuf );
        char *recvbuf = ftp->recvbuf;
        size_t recvsize = ftp->recvsize;
        char c;
        
        while ( len-- ) {
                c = *(data++);
                if ( ( c == '\r' ) || ( c == '\n' ) ) {
                        /* End of line: call ftp_reply() to handle
                         * completed reply.  Avoid calling ftp_reply()
                         * twice if we receive both \r and \n.
                         */
                        if ( recvbuf != ftp->status_text )
                                ftp_reply ( ftp );
                        /* Start filling up the status code buffer */
                        recvbuf = ftp->status_text;
                        recvsize = sizeof ( ftp->status_text ) - 1;
                } else if ( ( ftp->state == FTP_PASV ) && ( c == '(' ) ) {
                        /* Start filling up the passive parameter buffer */
                        recvbuf = ftp->passive_text;
                        recvsize = sizeof ( ftp->passive_text ) - 1;
                } else if ( ( ftp->state == FTP_PASV ) && ( c == ')' ) ) {
                        /* Stop filling the passive parameter buffer */
                        recvsize = 0;
                } else if ( ( ftp->state == FTP_SIZE ) && ( c == ' ' ) ) {
                        /* Start filling up the file size buffer */
                        recvbuf = ftp->filesize;
                        recvsize = sizeof ( ftp->filesize ) - 1;
                } else {
                        /* Fill up buffer if applicable */
                        if ( recvsize > 0 ) {
                                *(recvbuf++) = c;
                                recvsize--;
                        }
                }
        }

        /* Store for next invocation */
        ftp->recvbuf = recvbuf;
        ftp->recvsize = recvsize;

        /* Free I/O buffer */
        free_iob ( iobuf );

        return 0;
}
static void ftp_data_closed ( struct ftp_request ftp,
int  rc 
) [static]

Handle FTP data channel being closed.

Parameters:
ftpFTP request
rcReason for closure

When the data channel is closed, the control channel should be left alone; the server will send a completion message via the control channel which we'll pick up.

If the data channel is closed due to an error, we abort the request.

Definition at line 418 of file ftp.c.

References DBGC, ftp_done(), ftp_next_state(), and strerror().

                                                                {

        DBGC ( ftp, "FTP %p data connection closed: %s\n",
               ftp, strerror ( rc ) );
        
        /* If there was an error, close control channel and record status */
        if ( rc ) {
                ftp_done ( ftp, rc );
        } else {
                ftp_next_state ( ftp );
        }
}
static int ftp_check_string ( const char *  string) [static]

Check validity of FTP control channel string.

Parameters:
stringString
Return values:
rcReturn status code

Definition at line 469 of file ftp.c.

References EINVAL, and isprint().

Referenced by ftp_open().

                                                   {
        char c;

        /* The FTP control channel is line-based.  Check for invalid
         * non-printable characters (e.g. newlines).
         */
        while ( ( c = *(string++) ) ) {
                if ( ! isprint ( c ) )
                        return -EINVAL;
        }
        return 0;
}
static int ftp_open ( struct interface xfer,
struct uri uri 
) [static]

Initiate an FTP connection.

Parameters:
xferData transfer interface
uriUniform Resource Identifier
Return values:
rcReturn status code

Definition at line 489 of file ftp.c.

References ftp_request::control, ftp_request::data, DBGC, EINVAL, ENOMEM, ftp_check_string(), ftp_done(), ftp_free(), FTP_PORT, uri::host, htons, intf_init(), intf_plug_plug(), memset(), NULL, uri::password, uri::path, rc, ftp_request::recvbuf, ftp_request::recvsize, ref_init, ref_put, ftp_request::refcnt, SOCK_STREAM, sockaddr_tcpip::st_port, ftp_request::status_text, strerror(), ftp_request::uri, uri_get(), uri_port(), uri::user, ftp_request::xfer, xfer_open_named_socket(), and zalloc().

                                                                {
        struct ftp_request *ftp;
        struct sockaddr_tcpip server;
        int rc;

        /* Sanity checks */
        if ( ! uri->host )
                return -EINVAL;
        if ( ! uri->path )
                return -EINVAL;
        if ( ( rc = ftp_check_string ( uri->path ) ) != 0 )
                return rc;
        if ( uri->user && ( ( rc = ftp_check_string ( uri->user ) ) != 0 ) )
                return rc;
        if ( uri->password &&
             ( ( rc = ftp_check_string ( uri->password ) ) != 0 ) )
                return rc;

        /* Allocate and populate structure */
        ftp = zalloc ( sizeof ( *ftp ) );
        if ( ! ftp )
                return -ENOMEM;
        ref_init ( &ftp->refcnt, ftp_free );
        intf_init ( &ftp->xfer, &ftp_xfer_desc, &ftp->refcnt );
        intf_init ( &ftp->control, &ftp_control_desc, &ftp->refcnt );
        intf_init ( &ftp->data, &ftp_data_desc, &ftp->refcnt );
        ftp->uri = uri_get ( uri );
        ftp->recvbuf = ftp->status_text;
        ftp->recvsize = sizeof ( ftp->status_text ) - 1;

        DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path );

        /* Open control connection */
        memset ( &server, 0, sizeof ( server ) );
        server.st_port = htons ( uri_port ( uri, FTP_PORT ) );
        if ( ( rc = xfer_open_named_socket ( &ftp->control, SOCK_STREAM,
                                             ( struct sockaddr * ) &server,
                                             uri->host, NULL ) ) != 0 )
                goto err;

        /* Attach to parent interface, mortalise self, and return */
        intf_plug_plug ( &ftp->xfer, xfer );
        ref_put ( &ftp->refcnt );
        return 0;

 err:
        DBGC ( ftp, "FTP %p could not create request: %s\n", 
               ftp, strerror ( rc ) );
        ftp_done ( ftp, rc );
        ref_put ( &ftp->refcnt );
        return rc;
}

Variable Documentation

struct ftp_control_string ftp_strings[] [static]
Initial value:
 {
        [FTP_CONNECT]   = { NULL, NULL },
        [FTP_USER]      = { "USER ", ftp_user },
        [FTP_PASS]      = { "PASS ", ftp_password },
        [FTP_TYPE]      = { "TYPE I", NULL },
        [FTP_SIZE]      = { "SIZE ", ftp_uri_path },
        [FTP_PASV]      = { "PASV", NULL },
        [FTP_RETR]      = { "RETR ", ftp_uri_path },
        [FTP_WAIT]      = { NULL, NULL },
        [FTP_QUIT]      = { "QUIT", NULL },
        [FTP_DONE]      = { NULL, NULL },
}

FTP control channel strings.

Definition at line 178 of file ftp.c.

Initial value:

FTP control channel interface operations.

Definition at line 391 of file ftp.c.

Initial value:

FTP control channel interface descriptor.

Definition at line 397 of file ftp.c.

Initial value:

FTP data channel interface operations.

Definition at line 432 of file ftp.c.

Initial value:

FTP data channel interface descriptor.

Definition at line 437 of file ftp.c.

Initial value:
 {
        INTF_OP ( intf_close, struct ftp_request *, ftp_done ),
}

FTP data transfer interface operations.

Definition at line 448 of file ftp.c.

Initial value:

FTP data transfer interface descriptor.

Definition at line 453 of file ftp.c.

struct uri_opener ftp_uri_opener __uri_opener
Initial value:
 {
        .scheme = "ftp",
        .open   = ftp_open,
}

FTP URI opener.

Definition at line 543 of file ftp.c.