Home | History | Annotate | Download | only in tcp
      1 #include <stdint.h>
      2 #include <stdlib.h>
      3 #include <stdio.h>
      4 #include <string.h>
      5 #include <assert.h>
      6 #include <errno.h>
      7 #include <byteswap.h>
      8 #include <gpxe/socket.h>
      9 #include <gpxe/tcpip.h>
     10 #include <gpxe/in.h>
     11 #include <gpxe/xfer.h>
     12 #include <gpxe/open.h>
     13 #include <gpxe/uri.h>
     14 #include <gpxe/features.h>
     15 #include <gpxe/ftp.h>
     16 
     17 /** @file
     18  *
     19  * File transfer protocol
     20  *
     21  */
     22 
     23 FEATURE ( FEATURE_PROTOCOL, "FTP", DHCP_EB_FEATURE_FTP, 1 );
     24 
     25 /**
     26  * FTP states
     27  *
     28  * These @b must be sequential, i.e. a successful FTP session must
     29  * pass through each of these states in order.
     30  */
     31 enum ftp_state {
     32 	FTP_CONNECT = 0,
     33 	FTP_USER,
     34 	FTP_PASS,
     35 	FTP_TYPE,
     36 	FTP_PASV,
     37 	FTP_RETR,
     38 	FTP_WAIT,
     39 	FTP_QUIT,
     40 	FTP_DONE,
     41 };
     42 
     43 /**
     44  * An FTP request
     45  *
     46  */
     47 struct ftp_request {
     48 	/** Reference counter */
     49 	struct refcnt refcnt;
     50 	/** Data transfer interface */
     51 	struct xfer_interface xfer;
     52 
     53 	/** URI being fetched */
     54 	struct uri *uri;
     55 	/** FTP control channel interface */
     56 	struct xfer_interface control;
     57 	/** FTP data channel interface */
     58 	struct xfer_interface data;
     59 
     60 	/** Current state */
     61 	enum ftp_state state;
     62 	/** Buffer to be filled with data received via the control channel */
     63 	char *recvbuf;
     64 	/** Remaining size of recvbuf */
     65 	size_t recvsize;
     66 	/** FTP status code, as text */
     67 	char status_text[5];
     68 	/** Passive-mode parameters, as text */
     69 	char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */
     70 };
     71 
     72 /**
     73  * Free FTP request
     74  *
     75  * @v refcnt		Reference counter
     76  */
     77 static void ftp_free ( struct refcnt *refcnt ) {
     78 	struct ftp_request *ftp =
     79 		container_of ( refcnt, struct ftp_request, refcnt );
     80 
     81 	DBGC ( ftp, "FTP %p freed\n", ftp );
     82 
     83 	uri_put ( ftp->uri );
     84 	free ( ftp );
     85 }
     86 
     87 /**
     88  * Mark FTP operation as complete
     89  *
     90  * @v ftp		FTP request
     91  * @v rc		Return status code
     92  */
     93 static void ftp_done ( struct ftp_request *ftp, int rc ) {
     94 
     95 	DBGC ( ftp, "FTP %p completed (%s)\n", ftp, strerror ( rc ) );
     96 
     97 	/* Close all data transfer interfaces */
     98 	xfer_nullify ( &ftp->xfer );
     99 	xfer_close ( &ftp->xfer, rc );
    100 	xfer_nullify ( &ftp->control );
    101 	xfer_close ( &ftp->control, rc );
    102 	xfer_nullify ( &ftp->data );
    103 	xfer_close ( &ftp->data, rc );
    104 }
    105 
    106 /*****************************************************************************
    107  *
    108  * FTP control channel
    109  *
    110  */
    111 
    112 /** An FTP control channel string */
    113 struct ftp_control_string {
    114 	/** Literal portion */
    115 	const char *literal;
    116 	/** Variable portion
    117 	 *
    118 	 * @v ftp	FTP request
    119 	 * @ret string	Variable portion of string
    120 	 */
    121 	const char * ( *variable ) ( struct ftp_request *ftp );
    122 };
    123 
    124 /**
    125  * Retrieve FTP pathname
    126  *
    127  * @v ftp		FTP request
    128  * @ret path		FTP pathname
    129  */
    130 static const char * ftp_uri_path ( struct ftp_request *ftp ) {
    131 	return ftp->uri->path;
    132 }
    133 
    134 /**
    135  * Retrieve FTP user
    136  *
    137  * @v ftp		FTP request
    138  * @ret user		FTP user
    139  */
    140 static const char * ftp_user ( struct ftp_request *ftp ) {
    141 	static char *ftp_default_user = "anonymous";
    142 	return ftp->uri->user ? ftp->uri->user : ftp_default_user;
    143 }
    144 
    145 /**
    146  * Retrieve FTP password
    147  *
    148  * @v ftp		FTP request
    149  * @ret password	FTP password
    150  */
    151 static const char * ftp_password ( struct ftp_request *ftp ) {
    152 	static char *ftp_default_password = "etherboot (at) etherboot.org";
    153 	return ftp->uri->password ? ftp->uri->password : ftp_default_password;
    154 }
    155 
    156 /** FTP control channel strings */
    157 static struct ftp_control_string ftp_strings[] = {
    158 	[FTP_CONNECT]	= { NULL, NULL },
    159 	[FTP_USER]	= { "USER ", ftp_user },
    160 	[FTP_PASS]	= { "PASS ", ftp_password },
    161 	[FTP_TYPE]	= { "TYPE I", NULL },
    162 	[FTP_PASV]	= { "PASV", NULL },
    163 	[FTP_RETR]	= { "RETR ", ftp_uri_path },
    164 	[FTP_WAIT]	= { NULL, NULL },
    165 	[FTP_QUIT]	= { "QUIT", NULL },
    166 	[FTP_DONE]	= { NULL, NULL },
    167 };
    168 
    169 /**
    170  * Handle control channel being closed
    171  *
    172  * @v control		FTP control channel interface
    173  * @v rc		Reason for close
    174  *
    175  * When the control channel is closed, the data channel must also be
    176  * closed, if it is currently open.
    177  */
    178 static void ftp_control_close ( struct xfer_interface *control, int rc ) {
    179 	struct ftp_request *ftp =
    180 		container_of ( control, struct ftp_request, control );
    181 
    182 	DBGC ( ftp, "FTP %p control connection closed: %s\n",
    183 	       ftp, strerror ( rc ) );
    184 
    185 	/* Complete FTP operation */
    186 	ftp_done ( ftp, rc );
    187 }
    188 
    189 /**
    190  * Parse FTP byte sequence value
    191  *
    192  * @v text		Text string
    193  * @v value		Value buffer
    194  * @v len		Length of value buffer
    195  *
    196  * This parses an FTP byte sequence value (e.g. the "aaa,bbb,ccc,ddd"
    197  * form for IP addresses in PORT commands) into a byte sequence.  @c
    198  * *text will be updated to point beyond the end of the parsed byte
    199  * sequence.
    200  *
    201  * This function is safe in the presence of malformed data, though the
    202  * output is undefined.
    203  */
    204 static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
    205 	do {
    206 		*(value++) = strtoul ( *text, text, 10 );
    207 		if ( **text )
    208 			(*text)++;
    209 	} while ( --len );
    210 }
    211 
    212 /**
    213  * Move to next state and send the appropriate FTP control string
    214  *
    215  * @v ftp		FTP request
    216  *
    217  */
    218 static void ftp_next_state ( struct ftp_request *ftp ) {
    219 	struct ftp_control_string *ftp_string;
    220 	const char *literal;
    221 	const char *variable;
    222 
    223 	/* Move to next state */
    224 	if ( ftp->state < FTP_DONE )
    225 		ftp->state++;
    226 
    227 	/* Send control string if needed */
    228 	ftp_string = &ftp_strings[ftp->state];
    229 	literal = ftp_string->literal;
    230 	variable = ( ftp_string->variable ?
    231 		     ftp_string->variable ( ftp ) : "" );
    232 	if ( literal ) {
    233 		DBGC ( ftp, "FTP %p sending %s%s\n", ftp, literal, variable );
    234 		xfer_printf ( &ftp->control, "%s%s\r\n", literal, variable );
    235 	}
    236 }
    237 
    238 /**
    239  * Handle an FTP control channel response
    240  *
    241  * @v ftp		FTP request
    242  *
    243  * This is called once we have received a complete response line.
    244  */
    245 static void ftp_reply ( struct ftp_request *ftp ) {
    246 	char status_major = ftp->status_text[0];
    247 	char separator = ftp->status_text[3];
    248 
    249 	DBGC ( ftp, "FTP %p received status %s\n", ftp, ftp->status_text );
    250 
    251 	/* Ignore malformed lines */
    252 	if ( separator != ' ' )
    253 		return;
    254 
    255 	/* Ignore "intermediate" responses (1xx codes) */
    256 	if ( status_major == '1' )
    257 		return;
    258 
    259 	/* Anything other than success (2xx) or, in the case of a
    260 	 * repsonse to a "USER" command, a password prompt (3xx), is a
    261 	 * fatal error.
    262 	 */
    263 	if ( ! ( ( status_major == '2' ) ||
    264 		 ( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) ){
    265 		/* Flag protocol error and close connections */
    266 		ftp_done ( ftp, -EPROTO );
    267 		return;
    268 	}
    269 
    270 	/* Open passive connection when we get "PASV" response */
    271 	if ( ftp->state == FTP_PASV ) {
    272 		char *ptr = ftp->passive_text;
    273 		union {
    274 			struct sockaddr_in sin;
    275 			struct sockaddr sa;
    276 		} sa;
    277 		int rc;
    278 
    279 		sa.sin.sin_family = AF_INET;
    280 		ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_addr,
    281 				  sizeof ( sa.sin.sin_addr ) );
    282 		ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_port,
    283 				  sizeof ( sa.sin.sin_port ) );
    284 		if ( ( rc = xfer_open_socket ( &ftp->data, SOCK_STREAM,
    285 					       &sa.sa, NULL ) ) != 0 ) {
    286 			DBGC ( ftp, "FTP %p could not open data connection\n",
    287 			       ftp );
    288 			ftp_done ( ftp, rc );
    289 			return;
    290 		}
    291 	}
    292 
    293 	/* Move to next state and send control string */
    294 	ftp_next_state ( ftp );
    295 
    296 }
    297 
    298 /**
    299  * Handle new data arriving on FTP control channel
    300  *
    301  * @v control		FTP control channel interface
    302  * @v data		New data
    303  * @v len		Length of new data
    304  *
    305  * Data is collected until a complete line is received, at which point
    306  * its information is passed to ftp_reply().
    307  */
    308 static int ftp_control_deliver_raw ( struct xfer_interface *control,
    309 				     const void *data, size_t len ) {
    310 	struct ftp_request *ftp =
    311 		container_of ( control, struct ftp_request, control );
    312 	char *recvbuf = ftp->recvbuf;
    313 	size_t recvsize = ftp->recvsize;
    314 	char c;
    315 
    316 	while ( len-- ) {
    317 		c = * ( ( char * ) data++ );
    318 		switch ( c ) {
    319 		case '\r' :
    320 		case '\n' :
    321 			/* End of line: call ftp_reply() to handle
    322 			 * completed reply.  Avoid calling ftp_reply()
    323 			 * twice if we receive both \r and \n.
    324 			 */
    325 			if ( recvsize == 0 )
    326 				ftp_reply ( ftp );
    327 			/* Start filling up the status code buffer */
    328 			recvbuf = ftp->status_text;
    329 			recvsize = sizeof ( ftp->status_text ) - 1;
    330 			break;
    331 		case '(' :
    332 			/* Start filling up the passive parameter buffer */
    333 			recvbuf = ftp->passive_text;
    334 			recvsize = sizeof ( ftp->passive_text ) - 1;
    335 			break;
    336 		case ')' :
    337 			/* Stop filling the passive parameter buffer */
    338 			recvsize = 0;
    339 			break;
    340 		default :
    341 			/* Fill up buffer if applicable */
    342 			if ( recvsize > 0 ) {
    343 				*(recvbuf++) = c;
    344 				recvsize--;
    345 			}
    346 			break;
    347 		}
    348 	}
    349 
    350 	/* Store for next invocation */
    351 	ftp->recvbuf = recvbuf;
    352 	ftp->recvsize = recvsize;
    353 
    354 	return 0;
    355 }
    356 
    357 /** FTP control channel operations */
    358 static struct xfer_interface_operations ftp_control_operations = {
    359 	.close		= ftp_control_close,
    360 	.vredirect	= xfer_vreopen,
    361 	.window		= unlimited_xfer_window,
    362 	.alloc_iob	= default_xfer_alloc_iob,
    363 	.deliver_iob	= xfer_deliver_as_raw,
    364 	.deliver_raw	= ftp_control_deliver_raw,
    365 };
    366 
    367 /*****************************************************************************
    368  *
    369  * FTP data channel
    370  *
    371  */
    372 
    373 /**
    374  * Handle FTP data channel being closed
    375  *
    376  * @v data		FTP data channel interface
    377  * @v rc		Reason for closure
    378  *
    379  * When the data channel is closed, the control channel should be left
    380  * alone; the server will send a completion message via the control
    381  * channel which we'll pick up.
    382  *
    383  * If the data channel is closed due to an error, we abort the request.
    384  */
    385 static void ftp_data_closed ( struct xfer_interface *data, int rc ) {
    386 	struct ftp_request *ftp =
    387 		container_of ( data, struct ftp_request, data );
    388 
    389 	DBGC ( ftp, "FTP %p data connection closed: %s\n",
    390 	       ftp, strerror ( rc ) );
    391 
    392 	/* If there was an error, close control channel and record status */
    393 	if ( rc ) {
    394 		ftp_done ( ftp, rc );
    395 	} else {
    396 		ftp_next_state ( ftp );
    397 	}
    398 }
    399 
    400 /**
    401  * Handle data delivery via FTP data channel
    402  *
    403  * @v xfer		FTP data channel interface
    404  * @v iobuf		I/O buffer
    405  * @v meta		Data transfer metadata
    406  * @ret rc		Return status code
    407  */
    408 static int ftp_data_deliver_iob ( struct xfer_interface *data,
    409 				  struct io_buffer *iobuf,
    410 				  struct xfer_metadata *meta __unused ) {
    411 	struct ftp_request *ftp =
    412 		container_of ( data, struct ftp_request, data );
    413 	int rc;
    414 
    415 	if ( ( rc = xfer_deliver_iob ( &ftp->xfer, iobuf ) ) != 0 ) {
    416 		DBGC ( ftp, "FTP %p failed to deliver data: %s\n",
    417 		       ftp, strerror ( rc ) );
    418 		return rc;
    419 	}
    420 
    421 	return 0;
    422 }
    423 
    424 /** FTP data channel operations */
    425 static struct xfer_interface_operations ftp_data_operations = {
    426 	.close		= ftp_data_closed,
    427 	.vredirect	= xfer_vreopen,
    428 	.window		= unlimited_xfer_window,
    429 	.alloc_iob	= default_xfer_alloc_iob,
    430 	.deliver_iob	= ftp_data_deliver_iob,
    431 	.deliver_raw	= xfer_deliver_as_iob,
    432 };
    433 
    434 /*****************************************************************************
    435  *
    436  * Data transfer interface
    437  *
    438  */
    439 
    440 /**
    441  * Close FTP data transfer interface
    442  *
    443  * @v xfer		FTP data transfer interface
    444  * @v rc		Reason for close
    445  */
    446 static void ftp_xfer_closed ( struct xfer_interface *xfer, int rc ) {
    447 	struct ftp_request *ftp =
    448 		container_of ( xfer, struct ftp_request, xfer );
    449 
    450 	DBGC ( ftp, "FTP %p data transfer interface closed: %s\n",
    451 	       ftp, strerror ( rc ) );
    452 
    453 	ftp_done ( ftp, rc );
    454 }
    455 
    456 /** FTP data transfer interface operations */
    457 static struct xfer_interface_operations ftp_xfer_operations = {
    458 	.close		= ftp_xfer_closed,
    459 	.vredirect	= ignore_xfer_vredirect,
    460 	.window		= unlimited_xfer_window,
    461 	.alloc_iob	= default_xfer_alloc_iob,
    462 	.deliver_iob	= xfer_deliver_as_raw,
    463 	.deliver_raw	= ignore_xfer_deliver_raw,
    464 };
    465 
    466 /*****************************************************************************
    467  *
    468  * URI opener
    469  *
    470  */
    471 
    472 /**
    473  * Initiate an FTP connection
    474  *
    475  * @v xfer		Data transfer interface
    476  * @v uri		Uniform Resource Identifier
    477  * @ret rc		Return status code
    478  */
    479 static int ftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
    480 	struct ftp_request *ftp;
    481 	struct sockaddr_tcpip server;
    482 	int rc;
    483 
    484 	/* Sanity checks */
    485 	if ( ! uri->path )
    486 		return -EINVAL;
    487 	if ( ! uri->host )
    488 		return -EINVAL;
    489 
    490 	/* Allocate and populate structure */
    491 	ftp = zalloc ( sizeof ( *ftp ) );
    492 	if ( ! ftp )
    493 		return -ENOMEM;
    494 	ftp->refcnt.free = ftp_free;
    495 	xfer_init ( &ftp->xfer, &ftp_xfer_operations, &ftp->refcnt );
    496 	ftp->uri = uri_get ( uri );
    497 	xfer_init ( &ftp->control, &ftp_control_operations, &ftp->refcnt );
    498 	xfer_init ( &ftp->data, &ftp_data_operations, &ftp->refcnt );
    499 	ftp->recvbuf = ftp->status_text;
    500 	ftp->recvsize = sizeof ( ftp->status_text ) - 1;
    501 
    502 	DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path );
    503 
    504 	/* Open control connection */
    505 	memset ( &server, 0, sizeof ( server ) );
    506 	server.st_port = htons ( uri_port ( uri, FTP_PORT ) );
    507 	if ( ( rc = xfer_open_named_socket ( &ftp->control, SOCK_STREAM,
    508 					     ( struct sockaddr * ) &server,
    509 					     uri->host, NULL ) ) != 0 )
    510 		goto err;
    511 
    512 	/* Attach to parent interface, mortalise self, and return */
    513 	xfer_plug_plug ( &ftp->xfer, xfer );
    514 	ref_put ( &ftp->refcnt );
    515 	return 0;
    516 
    517  err:
    518 	DBGC ( ftp, "FTP %p could not create request: %s\n",
    519 	       ftp, strerror ( rc ) );
    520 	ftp_done ( ftp, rc );
    521 	ref_put ( &ftp->refcnt );
    522 	return rc;
    523 }
    524 
    525 /** FTP URI opener */
    526 struct uri_opener ftp_uri_opener __uri_opener = {
    527 	.scheme	= "ftp",
    528 	.open	= ftp_open,
    529 };
    530