|  | @@ -1,15 +1,16 @@
 | 
		
	
		
			
			| 1 |  | -#include <stddef.h>
 | 
		
	
		
			
			|  | 1 | +#include <stdint.h>
 | 
		
	
		
			
			| 2 | 2 |  #include <stdlib.h>
 | 
		
	
		
			
			| 3 | 3 |  #include <stdio.h>
 | 
		
	
		
			
			| 4 | 4 |  #include <string.h>
 | 
		
	
		
			
			| 5 | 5 |  #include <assert.h>
 | 
		
	
		
			
			| 6 | 6 |  #include <errno.h>
 | 
		
	
		
			
			| 7 | 7 |  #include <byteswap.h>
 | 
		
	
		
			
			| 8 |  | -#include <gpxe/async.h>
 | 
		
	
		
			
			| 9 |  | -#include <gpxe/buffer.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>
 | 
		
	
		
			
			| 10 | 13 |  #include <gpxe/uri.h>
 | 
		
	
		
			
			| 11 |  | -#include <gpxe/download.h>
 | 
		
	
		
			
			| 12 |  | -#include <gpxe/tcp.h>
 | 
		
	
		
			
			| 13 | 14 |  #include <gpxe/ftp.h>
 | 
		
	
		
			
			| 14 | 15 |  
 | 
		
	
		
			
			| 15 | 16 |  /** @file
 | 
		
	
	
		
			
			|  | @@ -18,13 +19,94 @@
 | 
		
	
		
			
			| 18 | 19 |   *
 | 
		
	
		
			
			| 19 | 20 |   */
 | 
		
	
		
			
			| 20 | 21 |  
 | 
		
	
		
			
			|  | 22 | +/**
 | 
		
	
		
			
			|  | 23 | + * FTP states
 | 
		
	
		
			
			|  | 24 | + *
 | 
		
	
		
			
			|  | 25 | + * These @b must be sequential, i.e. a successful FTP session must
 | 
		
	
		
			
			|  | 26 | + * pass through each of these states in order.
 | 
		
	
		
			
			|  | 27 | + */
 | 
		
	
		
			
			|  | 28 | +enum ftp_state {
 | 
		
	
		
			
			|  | 29 | +	FTP_CONNECT = 0,
 | 
		
	
		
			
			|  | 30 | +	FTP_USER,
 | 
		
	
		
			
			|  | 31 | +	FTP_PASS,
 | 
		
	
		
			
			|  | 32 | +	FTP_TYPE,
 | 
		
	
		
			
			|  | 33 | +	FTP_PASV,
 | 
		
	
		
			
			|  | 34 | +	FTP_RETR,
 | 
		
	
		
			
			|  | 35 | +	FTP_QUIT,
 | 
		
	
		
			
			|  | 36 | +	FTP_DONE,
 | 
		
	
		
			
			|  | 37 | +};
 | 
		
	
		
			
			|  | 38 | +
 | 
		
	
		
			
			|  | 39 | +/**
 | 
		
	
		
			
			|  | 40 | + * An FTP request
 | 
		
	
		
			
			|  | 41 | + *
 | 
		
	
		
			
			|  | 42 | + */
 | 
		
	
		
			
			|  | 43 | +struct ftp_request {
 | 
		
	
		
			
			|  | 44 | +	/** Reference counter */
 | 
		
	
		
			
			|  | 45 | +	struct refcnt refcnt;
 | 
		
	
		
			
			|  | 46 | +	/** Data transfer interface */
 | 
		
	
		
			
			|  | 47 | +	struct xfer_interface xfer;
 | 
		
	
		
			
			|  | 48 | +
 | 
		
	
		
			
			|  | 49 | +	/** URI being fetched */
 | 
		
	
		
			
			|  | 50 | +	struct uri *uri;
 | 
		
	
		
			
			|  | 51 | +	/** FTP control channel interface */
 | 
		
	
		
			
			|  | 52 | +	struct xfer_interface control;
 | 
		
	
		
			
			|  | 53 | +	/** FTP data channel interface */
 | 
		
	
		
			
			|  | 54 | +	struct xfer_interface data;
 | 
		
	
		
			
			|  | 55 | +
 | 
		
	
		
			
			|  | 56 | +	/** Current state */
 | 
		
	
		
			
			|  | 57 | +	enum ftp_state state;
 | 
		
	
		
			
			|  | 58 | +	/** Buffer to be filled with data received via the control channel */
 | 
		
	
		
			
			|  | 59 | +	char *recvbuf;
 | 
		
	
		
			
			|  | 60 | +	/** Remaining size of recvbuf */
 | 
		
	
		
			
			|  | 61 | +	size_t recvsize;
 | 
		
	
		
			
			|  | 62 | +	/** FTP status code, as text */
 | 
		
	
		
			
			|  | 63 | +	char status_text[5];
 | 
		
	
		
			
			|  | 64 | +	/** Passive-mode parameters, as text */
 | 
		
	
		
			
			|  | 65 | +	char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */
 | 
		
	
		
			
			|  | 66 | +};
 | 
		
	
		
			
			|  | 67 | +
 | 
		
	
		
			
			|  | 68 | +/**
 | 
		
	
		
			
			|  | 69 | + * Free FTP request
 | 
		
	
		
			
			|  | 70 | + *
 | 
		
	
		
			
			|  | 71 | + * @v refcnt		Reference counter
 | 
		
	
		
			
			|  | 72 | + */
 | 
		
	
		
			
			|  | 73 | +static void ftp_free ( struct refcnt *refcnt ) {
 | 
		
	
		
			
			|  | 74 | +	struct ftp_request *ftp =
 | 
		
	
		
			
			|  | 75 | +		container_of ( refcnt, struct ftp_request, refcnt );
 | 
		
	
		
			
			|  | 76 | +
 | 
		
	
		
			
			|  | 77 | +	DBGC ( ftp, "FTP %p freed\n", ftp );
 | 
		
	
		
			
			|  | 78 | +
 | 
		
	
		
			
			|  | 79 | +	uri_put ( ftp->uri );
 | 
		
	
		
			
			|  | 80 | +	free ( ftp );
 | 
		
	
		
			
			|  | 81 | +}
 | 
		
	
		
			
			|  | 82 | +
 | 
		
	
		
			
			|  | 83 | +/**
 | 
		
	
		
			
			|  | 84 | + * Mark FTP operation as complete
 | 
		
	
		
			
			|  | 85 | + *
 | 
		
	
		
			
			|  | 86 | + * @v ftp		FTP request
 | 
		
	
		
			
			|  | 87 | + * @v rc		Return status code
 | 
		
	
		
			
			|  | 88 | + */
 | 
		
	
		
			
			|  | 89 | +static void ftp_done ( struct ftp_request *ftp, int rc ) {
 | 
		
	
		
			
			|  | 90 | +
 | 
		
	
		
			
			|  | 91 | +	DBGC ( ftp, "FTP %p completed (%s)\n", ftp, strerror ( rc ) );
 | 
		
	
		
			
			|  | 92 | +
 | 
		
	
		
			
			|  | 93 | +	/* Close all data transfer interfaces */
 | 
		
	
		
			
			|  | 94 | +	xfer_nullify ( &ftp->xfer );
 | 
		
	
		
			
			|  | 95 | +	xfer_close ( &ftp->xfer, rc );
 | 
		
	
		
			
			|  | 96 | +	xfer_nullify ( &ftp->control );
 | 
		
	
		
			
			|  | 97 | +	xfer_close ( &ftp->control, rc );
 | 
		
	
		
			
			|  | 98 | +	xfer_nullify ( &ftp->data );
 | 
		
	
		
			
			|  | 99 | +	xfer_close ( &ftp->data, rc );
 | 
		
	
		
			
			|  | 100 | +}
 | 
		
	
		
			
			|  | 101 | +
 | 
		
	
		
			
			| 21 | 102 |  /*****************************************************************************
 | 
		
	
		
			
			| 22 | 103 |   *
 | 
		
	
		
			
			| 23 | 104 |   * FTP control channel
 | 
		
	
		
			
			| 24 | 105 |   *
 | 
		
	
		
			
			| 25 | 106 |   */
 | 
		
	
		
			
			| 26 | 107 |  
 | 
		
	
		
			
			| 27 |  | -/** FTP control channel strings
 | 
		
	
		
			
			|  | 108 | +/**
 | 
		
	
		
			
			|  | 109 | + * FTP control channel strings
 | 
		
	
		
			
			| 28 | 110 |   *
 | 
		
	
		
			
			| 29 | 111 |   * These are used as printf() format strings.  Since only one of them
 | 
		
	
		
			
			| 30 | 112 |   * (RETR) takes an argument, we always supply that argument to the
 | 
		
	
	
		
			
			|  | @@ -42,32 +124,23 @@ static const char * ftp_strings[] = {
 | 
		
	
		
			
			| 42 | 124 |  };
 | 
		
	
		
			
			| 43 | 125 |  
 | 
		
	
		
			
			| 44 | 126 |  /**
 | 
		
	
		
			
			| 45 |  | - * Get FTP request from control stream application
 | 
		
	
		
			
			|  | 127 | + * Handle control channel being closed
 | 
		
	
		
			
			| 46 | 128 |   *
 | 
		
	
		
			
			| 47 |  | - * @v app		Stream application
 | 
		
	
		
			
			| 48 |  | - * @ret ftp		FTP request
 | 
		
	
		
			
			| 49 |  | - */
 | 
		
	
		
			
			| 50 |  | -static inline struct ftp_request *
 | 
		
	
		
			
			| 51 |  | -stream_to_ftp ( struct stream_application *app ) {
 | 
		
	
		
			
			| 52 |  | -	return container_of ( app, struct ftp_request, stream );
 | 
		
	
		
			
			| 53 |  | -}
 | 
		
	
		
			
			| 54 |  | -
 | 
		
	
		
			
			| 55 |  | -/**
 | 
		
	
		
			
			| 56 |  | - * Mark FTP operation as complete
 | 
		
	
		
			
			|  | 129 | + * @v control		FTP control channel interface
 | 
		
	
		
			
			|  | 130 | + * @v rc		Reason for close
 | 
		
	
		
			
			| 57 | 131 |   *
 | 
		
	
		
			
			| 58 |  | - * @v ftp		FTP request
 | 
		
	
		
			
			| 59 |  | - * @v rc		Return status code
 | 
		
	
		
			
			|  | 132 | + * When the control channel is closed, the data channel must also be
 | 
		
	
		
			
			|  | 133 | + * closed, if it is currently open.
 | 
		
	
		
			
			| 60 | 134 |   */
 | 
		
	
		
			
			| 61 |  | -static void ftp_done ( struct ftp_request *ftp, int rc ) {
 | 
		
	
		
			
			| 62 |  | -
 | 
		
	
		
			
			| 63 |  | -	DBGC ( ftp, "FTP %p completed with status %d\n", ftp, rc );
 | 
		
	
		
			
			|  | 135 | +static void ftp_control_close ( struct xfer_interface *control, int rc ) {
 | 
		
	
		
			
			|  | 136 | +	struct ftp_request *ftp =
 | 
		
	
		
			
			|  | 137 | +		container_of ( control, struct ftp_request, control );
 | 
		
	
		
			
			| 64 | 138 |  
 | 
		
	
		
			
			| 65 |  | -	/* Close both stream connections */
 | 
		
	
		
			
			| 66 |  | -	stream_close ( &ftp->stream );
 | 
		
	
		
			
			| 67 |  | -	stream_close ( &ftp->stream_data );
 | 
		
	
		
			
			|  | 139 | +	DBGC ( ftp, "FTP %p control connection closed: %s\n",
 | 
		
	
		
			
			|  | 140 | +	       ftp, strerror ( rc ) );
 | 
		
	
		
			
			| 68 | 141 |  
 | 
		
	
		
			
			| 69 |  | -	/* Mark asynchronous operation as complete */
 | 
		
	
		
			
			| 70 |  | -	async_done ( &ftp->async, rc );
 | 
		
	
		
			
			|  | 142 | +	/* Complete FTP operation */
 | 
		
	
		
			
			|  | 143 | +	ftp_done ( ftp, rc );
 | 
		
	
		
			
			| 71 | 144 |  }
 | 
		
	
		
			
			| 72 | 145 |  
 | 
		
	
		
			
			| 73 | 146 |  /**
 | 
		
	
	
		
			
			|  | @@ -102,9 +175,14 @@ static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
 | 
		
	
		
			
			| 102 | 175 |   */
 | 
		
	
		
			
			| 103 | 176 |  static void ftp_reply ( struct ftp_request *ftp ) {
 | 
		
	
		
			
			| 104 | 177 |  	char status_major = ftp->status_text[0];
 | 
		
	
		
			
			|  | 178 | +	char separator = ftp->status_text[3];
 | 
		
	
		
			
			| 105 | 179 |  
 | 
		
	
		
			
			| 106 | 180 |  	DBGC ( ftp, "FTP %p received status %s\n", ftp, ftp->status_text );
 | 
		
	
		
			
			| 107 | 181 |  
 | 
		
	
		
			
			|  | 182 | +	/* Ignore malformed lines */
 | 
		
	
		
			
			|  | 183 | +	if ( separator != ' ' )
 | 
		
	
		
			
			|  | 184 | +		return;
 | 
		
	
		
			
			|  | 185 | +
 | 
		
	
		
			
			| 108 | 186 |  	/* Ignore "intermediate" responses (1xx codes) */
 | 
		
	
		
			
			| 109 | 187 |  	if ( status_major == '1' )
 | 
		
	
		
			
			| 110 | 188 |  		return;
 | 
		
	
	
		
			
			|  | @@ -133,47 +211,42 @@ static void ftp_reply ( struct ftp_request *ftp ) {
 | 
		
	
		
			
			| 133 | 211 |  				  sizeof ( sa.sin.sin_addr ) );
 | 
		
	
		
			
			| 134 | 212 |  		ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_port,
 | 
		
	
		
			
			| 135 | 213 |  				  sizeof ( sa.sin.sin_port ) );
 | 
		
	
		
			
			| 136 |  | -		if ( ( rc = tcp_open ( &ftp->stream_data ) ) != 0 ) {
 | 
		
	
		
			
			|  | 214 | +		if ( ( rc = xfer_open_socket ( &ftp->data, SOCK_STREAM,
 | 
		
	
		
			
			|  | 215 | +					       &sa.sa, NULL ) ) != 0 ) {
 | 
		
	
		
			
			| 137 | 216 |  			DBGC ( ftp, "FTP %p could not open data connection\n",
 | 
		
	
		
			
			| 138 | 217 |  			       ftp );
 | 
		
	
		
			
			| 139 | 218 |  			ftp_done ( ftp, rc );
 | 
		
	
		
			
			| 140 | 219 |  			return;
 | 
		
	
		
			
			| 141 | 220 |  		}
 | 
		
	
		
			
			| 142 |  | -		if ( ( rc = stream_connect ( &ftp->stream_data,
 | 
		
	
		
			
			| 143 |  | -					     &sa.sa ) ) != 0 ){
 | 
		
	
		
			
			| 144 |  | -			DBGC ( ftp, "FTP %p could not make data connection\n",
 | 
		
	
		
			
			| 145 |  | -			       ftp );
 | 
		
	
		
			
			| 146 |  | -			ftp_done ( ftp, rc );
 | 
		
	
		
			
			| 147 |  | -			return;
 | 
		
	
		
			
			| 148 |  | -		}
 | 
		
	
		
			
			| 149 | 221 |  	}
 | 
		
	
		
			
			| 150 | 222 |  
 | 
		
	
		
			
			| 151 | 223 |  	/* Move to next state */
 | 
		
	
		
			
			| 152 | 224 |  	if ( ftp->state < FTP_DONE )
 | 
		
	
		
			
			| 153 | 225 |  		ftp->state++;
 | 
		
	
		
			
			| 154 |  | -	ftp->already_sent = 0;
 | 
		
	
		
			
			| 155 | 226 |  
 | 
		
	
		
			
			|  | 227 | +	/* Send control string */
 | 
		
	
		
			
			| 156 | 228 |  	if ( ftp->state < FTP_DONE ) {
 | 
		
	
		
			
			| 157 | 229 |  		DBGC ( ftp, "FTP %p sending ", ftp );
 | 
		
	
		
			
			| 158 | 230 |  		DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
 | 
		
	
		
			
			|  | 231 | +		xfer_printf ( &ftp->control, ftp_strings[ftp->state],
 | 
		
	
		
			
			|  | 232 | +			      ftp->uri->path );
 | 
		
	
		
			
			| 159 | 233 |  	}
 | 
		
	
		
			
			| 160 |  | -
 | 
		
	
		
			
			| 161 |  | -	return;
 | 
		
	
		
			
			| 162 | 234 |  }
 | 
		
	
		
			
			| 163 | 235 |  
 | 
		
	
		
			
			| 164 | 236 |  /**
 | 
		
	
		
			
			| 165 | 237 |   * Handle new data arriving on FTP control channel
 | 
		
	
		
			
			| 166 | 238 |   *
 | 
		
	
		
			
			| 167 |  | - * @v app		Stream application
 | 
		
	
		
			
			|  | 239 | + * @v control		FTP control channel interface
 | 
		
	
		
			
			| 168 | 240 |   * @v data		New data
 | 
		
	
		
			
			| 169 | 241 |   * @v len		Length of new data
 | 
		
	
		
			
			| 170 | 242 |   *
 | 
		
	
		
			
			| 171 | 243 |   * Data is collected until a complete line is received, at which point
 | 
		
	
		
			
			| 172 | 244 |   * its information is passed to ftp_reply().
 | 
		
	
		
			
			| 173 | 245 |   */
 | 
		
	
		
			
			| 174 |  | -static void ftp_newdata ( struct stream_application *app,
 | 
		
	
		
			
			| 175 |  | -			  void *data, size_t len ) {
 | 
		
	
		
			
			| 176 |  | -	struct ftp_request *ftp = stream_to_ftp ( app );
 | 
		
	
		
			
			|  | 246 | +static int ftp_control_deliver_raw ( struct xfer_interface *control,
 | 
		
	
		
			
			|  | 247 | +				     const void *data, size_t len ) {
 | 
		
	
		
			
			|  | 248 | +	struct ftp_request *ftp =
 | 
		
	
		
			
			|  | 249 | +		container_of ( control, struct ftp_request, control );
 | 
		
	
		
			
			| 177 | 250 |  	char *recvbuf = ftp->recvbuf;
 | 
		
	
		
			
			| 178 | 251 |  	size_t recvsize = ftp->recvsize;
 | 
		
	
		
			
			| 179 | 252 |  	char c;
 | 
		
	
	
		
			
			|  | @@ -215,62 +288,19 @@ static void ftp_newdata ( struct stream_application *app,
 | 
		
	
		
			
			| 215 | 288 |  	/* Store for next invocation */
 | 
		
	
		
			
			| 216 | 289 |  	ftp->recvbuf = recvbuf;
 | 
		
	
		
			
			| 217 | 290 |  	ftp->recvsize = recvsize;
 | 
		
	
		
			
			| 218 |  | -}
 | 
		
	
		
			
			| 219 | 291 |  
 | 
		
	
		
			
			| 220 |  | -/**
 | 
		
	
		
			
			| 221 |  | - * Handle acknowledgement of data sent on FTP control channel
 | 
		
	
		
			
			| 222 |  | - *
 | 
		
	
		
			
			| 223 |  | - * @v app		Stream application
 | 
		
	
		
			
			| 224 |  | - */
 | 
		
	
		
			
			| 225 |  | -static void ftp_acked ( struct stream_application *app, size_t len ) {
 | 
		
	
		
			
			| 226 |  | -	struct ftp_request *ftp = stream_to_ftp ( app );
 | 
		
	
		
			
			| 227 |  | -	
 | 
		
	
		
			
			| 228 |  | -	/* Mark off ACKed portion of the currently-transmitted data */
 | 
		
	
		
			
			| 229 |  | -	ftp->already_sent += len;
 | 
		
	
		
			
			| 230 |  | -}
 | 
		
	
		
			
			| 231 |  | -
 | 
		
	
		
			
			| 232 |  | -/**
 | 
		
	
		
			
			| 233 |  | - * Construct data to send on FTP control channel
 | 
		
	
		
			
			| 234 |  | - *
 | 
		
	
		
			
			| 235 |  | - * @v app		Stream application
 | 
		
	
		
			
			| 236 |  | - * @v buf		Temporary data buffer
 | 
		
	
		
			
			| 237 |  | - * @v len		Length of temporary data buffer
 | 
		
	
		
			
			| 238 |  | - */
 | 
		
	
		
			
			| 239 |  | -static void ftp_senddata ( struct stream_application *app,
 | 
		
	
		
			
			| 240 |  | -			   void *buf, size_t len ) {
 | 
		
	
		
			
			| 241 |  | -	struct ftp_request *ftp = stream_to_ftp ( app );
 | 
		
	
		
			
			| 242 |  | -
 | 
		
	
		
			
			| 243 |  | -	/* Send the as-yet-unACKed portion of the string for the
 | 
		
	
		
			
			| 244 |  | -	 * current state.
 | 
		
	
		
			
			| 245 |  | -	 */
 | 
		
	
		
			
			| 246 |  | -	len = snprintf ( buf, len, ftp_strings[ftp->state], ftp->uri->path );
 | 
		
	
		
			
			| 247 |  | -	stream_send ( app, buf + ftp->already_sent, len - ftp->already_sent );
 | 
		
	
		
			
			| 248 |  | -}
 | 
		
	
		
			
			| 249 |  | -
 | 
		
	
		
			
			| 250 |  | -/**
 | 
		
	
		
			
			| 251 |  | - * Handle control channel being closed
 | 
		
	
		
			
			| 252 |  | - *
 | 
		
	
		
			
			| 253 |  | - * @v app		Stream application
 | 
		
	
		
			
			| 254 |  | - *
 | 
		
	
		
			
			| 255 |  | - * When the control channel is closed, the data channel must also be
 | 
		
	
		
			
			| 256 |  | - * closed, if it is currently open.
 | 
		
	
		
			
			| 257 |  | - */
 | 
		
	
		
			
			| 258 |  | -static void ftp_closed ( struct stream_application *app, int rc ) {
 | 
		
	
		
			
			| 259 |  | -	struct ftp_request *ftp = stream_to_ftp ( app );
 | 
		
	
		
			
			| 260 |  | -
 | 
		
	
		
			
			| 261 |  | -	DBGC ( ftp, "FTP %p control connection closed: %s\n",
 | 
		
	
		
			
			| 262 |  | -	       ftp, strerror ( rc ) );
 | 
		
	
		
			
			| 263 |  | -
 | 
		
	
		
			
			| 264 |  | -	/* Complete FTP operation */
 | 
		
	
		
			
			| 265 |  | -	ftp_done ( ftp, rc );
 | 
		
	
		
			
			|  | 292 | +	return 0;
 | 
		
	
		
			
			| 266 | 293 |  }
 | 
		
	
		
			
			| 267 | 294 |  
 | 
		
	
		
			
			| 268 | 295 |  /** FTP control channel operations */
 | 
		
	
		
			
			| 269 |  | -static struct stream_application_operations ftp_stream_operations = {
 | 
		
	
		
			
			| 270 |  | -	.closed		= ftp_closed,
 | 
		
	
		
			
			| 271 |  | -	.acked		= ftp_acked,
 | 
		
	
		
			
			| 272 |  | -	.newdata	= ftp_newdata,
 | 
		
	
		
			
			| 273 |  | -	.senddata	= ftp_senddata,
 | 
		
	
		
			
			|  | 296 | +static struct xfer_interface_operations ftp_control_operations = {
 | 
		
	
		
			
			|  | 297 | +	.close		= ftp_control_close,
 | 
		
	
		
			
			|  | 298 | +	.vredirect	= xfer_vopen,
 | 
		
	
		
			
			|  | 299 | +	.request	= ignore_xfer_request,
 | 
		
	
		
			
			|  | 300 | +	.seek		= ignore_xfer_seek,
 | 
		
	
		
			
			|  | 301 | +	.alloc_iob	= default_xfer_alloc_iob,
 | 
		
	
		
			
			|  | 302 | +	.deliver_iob	= xfer_deliver_as_raw,
 | 
		
	
		
			
			|  | 303 | +	.deliver_raw	= ftp_control_deliver_raw,
 | 
		
	
		
			
			| 274 | 304 |  };
 | 
		
	
		
			
			| 275 | 305 |  
 | 
		
	
		
			
			| 276 | 306 |  /*****************************************************************************
 | 
		
	
	
		
			
			|  | @@ -280,20 +310,10 @@ static struct stream_application_operations ftp_stream_operations = {
 | 
		
	
		
			
			| 280 | 310 |   */
 | 
		
	
		
			
			| 281 | 311 |  
 | 
		
	
		
			
			| 282 | 312 |  /**
 | 
		
	
		
			
			| 283 |  | - * Get FTP request from data stream application
 | 
		
	
		
			
			|  | 313 | + * Handle FTP data channel being closed
 | 
		
	
		
			
			| 284 | 314 |   *
 | 
		
	
		
			
			| 285 |  | - * @v app		Stream application
 | 
		
	
		
			
			| 286 |  | - * @ret ftp		FTP request
 | 
		
	
		
			
			| 287 |  | - */
 | 
		
	
		
			
			| 288 |  | -static inline struct ftp_request *
 | 
		
	
		
			
			| 289 |  | -stream_to_ftp_data ( struct stream_application *app ) {
 | 
		
	
		
			
			| 290 |  | -	return container_of ( app, struct ftp_request, stream_data );
 | 
		
	
		
			
			| 291 |  | -}
 | 
		
	
		
			
			| 292 |  | -
 | 
		
	
		
			
			| 293 |  | -/**
 | 
		
	
		
			
			| 294 |  | - * Handle data channel being closed
 | 
		
	
		
			
			| 295 |  | - *
 | 
		
	
		
			
			| 296 |  | - * @v app		Stream application
 | 
		
	
		
			
			|  | 315 | + * @v data		FTP data channel interface
 | 
		
	
		
			
			|  | 316 | + * @v rc		Reason for closure
 | 
		
	
		
			
			| 297 | 317 |   *
 | 
		
	
		
			
			| 298 | 318 |   * When the data channel is closed, the control channel should be left
 | 
		
	
		
			
			| 299 | 319 |   * alone; the server will send a completion message via the control
 | 
		
	
	
		
			
			|  | @@ -301,8 +321,9 @@ stream_to_ftp_data ( struct stream_application *app ) {
 | 
		
	
		
			
			| 301 | 321 |   *
 | 
		
	
		
			
			| 302 | 322 |   * If the data channel is closed due to an error, we abort the request.
 | 
		
	
		
			
			| 303 | 323 |   */
 | 
		
	
		
			
			| 304 |  | -static void ftp_data_closed ( struct stream_application *app, int rc ) {
 | 
		
	
		
			
			| 305 |  | -	struct ftp_request *ftp = stream_to_ftp_data ( app );
 | 
		
	
		
			
			|  | 324 | +static void ftp_data_closed ( struct xfer_interface *data, int rc ) {
 | 
		
	
		
			
			|  | 325 | +	struct ftp_request *ftp =
 | 
		
	
		
			
			|  | 326 | +		container_of ( data, struct ftp_request, data );
 | 
		
	
		
			
			| 306 | 327 |  
 | 
		
	
		
			
			| 307 | 328 |  	DBGC ( ftp, "FTP %p data connection closed: %s\n",
 | 
		
	
		
			
			| 308 | 329 |  	       ftp, strerror ( rc ) );
 | 
		
	
	
		
			
			|  | @@ -313,121 +334,132 @@ static void ftp_data_closed ( struct stream_application *app, int rc ) {
 | 
		
	
		
			
			| 313 | 334 |  }
 | 
		
	
		
			
			| 314 | 335 |  
 | 
		
	
		
			
			| 315 | 336 |  /**
 | 
		
	
		
			
			| 316 |  | - * Handle new data arriving on the FTP data channel
 | 
		
	
		
			
			|  | 337 | + * Handle data delivery via FTP data channel
 | 
		
	
		
			
			| 317 | 338 |   *
 | 
		
	
		
			
			| 318 |  | - * @v app		Stream application
 | 
		
	
		
			
			| 319 |  | - * @v data		New data
 | 
		
	
		
			
			| 320 |  | - * @v len		Length of new data
 | 
		
	
		
			
			|  | 339 | + * @v xfer		FTP data channel interface
 | 
		
	
		
			
			|  | 340 | + * @v iobuf		I/O buffer
 | 
		
	
		
			
			|  | 341 | + * @ret rc		Return status code
 | 
		
	
		
			
			| 321 | 342 |   */
 | 
		
	
		
			
			| 322 |  | -static void ftp_data_newdata ( struct stream_application *app,
 | 
		
	
		
			
			| 323 |  | -			       void *data, size_t len ) {
 | 
		
	
		
			
			| 324 |  | -	struct ftp_request *ftp = stream_to_ftp_data ( app );
 | 
		
	
		
			
			|  | 343 | +static int ftp_data_deliver_iob ( struct xfer_interface *data,
 | 
		
	
		
			
			|  | 344 | +				  struct io_buffer *iobuf ) {
 | 
		
	
		
			
			|  | 345 | +	struct ftp_request *ftp =
 | 
		
	
		
			
			|  | 346 | +		container_of ( data, struct ftp_request, data );
 | 
		
	
		
			
			| 325 | 347 |  	int rc;
 | 
		
	
		
			
			| 326 | 348 |  
 | 
		
	
		
			
			| 327 |  | -	/* Fill data buffer */
 | 
		
	
		
			
			| 328 |  | -	if ( ( rc = fill_buffer ( ftp->buffer, data,
 | 
		
	
		
			
			| 329 |  | -				  ftp->buffer->fill, len ) ) != 0 ){
 | 
		
	
		
			
			| 330 |  | -		DBGC ( ftp, "FTP %p failed to fill data buffer: %s\n",
 | 
		
	
		
			
			|  | 349 | +	if ( ( rc = xfer_deliver_iob ( &ftp->xfer, iobuf ) ) != 0 ) {
 | 
		
	
		
			
			|  | 350 | +		DBGC ( ftp, "FTP %p failed to deliver data: %s\n",
 | 
		
	
		
			
			| 331 | 351 |  		       ftp, strerror ( rc ) );
 | 
		
	
		
			
			| 332 |  | -		ftp_done ( ftp, rc );
 | 
		
	
		
			
			| 333 |  | -		return;
 | 
		
	
		
			
			|  | 352 | +		return rc;
 | 
		
	
		
			
			| 334 | 353 |  	}
 | 
		
	
		
			
			|  | 354 | +
 | 
		
	
		
			
			|  | 355 | +	return 0;
 | 
		
	
		
			
			| 335 | 356 |  }
 | 
		
	
		
			
			| 336 | 357 |  
 | 
		
	
		
			
			| 337 | 358 |  /** FTP data channel operations */
 | 
		
	
		
			
			| 338 |  | -static struct stream_application_operations ftp_data_stream_operations = {
 | 
		
	
		
			
			| 339 |  | -	.closed		= ftp_data_closed,
 | 
		
	
		
			
			| 340 |  | -	.newdata	= ftp_data_newdata,
 | 
		
	
		
			
			|  | 359 | +static struct xfer_interface_operations ftp_data_operations = {
 | 
		
	
		
			
			|  | 360 | +	.close		= ftp_data_closed,
 | 
		
	
		
			
			|  | 361 | +	.vredirect	= xfer_vopen,
 | 
		
	
		
			
			|  | 362 | +	.request	= ignore_xfer_request,
 | 
		
	
		
			
			|  | 363 | +	.seek		= ignore_xfer_seek,
 | 
		
	
		
			
			|  | 364 | +	.alloc_iob	= default_xfer_alloc_iob,
 | 
		
	
		
			
			|  | 365 | +	.deliver_iob	= ftp_data_deliver_iob,
 | 
		
	
		
			
			|  | 366 | +	.deliver_raw	= xfer_deliver_as_iob,
 | 
		
	
		
			
			| 341 | 367 |  };
 | 
		
	
		
			
			| 342 | 368 |  
 | 
		
	
		
			
			| 343 | 369 |  /*****************************************************************************
 | 
		
	
		
			
			| 344 | 370 |   *
 | 
		
	
		
			
			| 345 |  | - * API
 | 
		
	
		
			
			|  | 371 | + * Data transfer interface
 | 
		
	
		
			
			| 346 | 372 |   *
 | 
		
	
		
			
			| 347 | 373 |   */
 | 
		
	
		
			
			| 348 | 374 |  
 | 
		
	
		
			
			| 349 | 375 |  /**
 | 
		
	
		
			
			| 350 |  | - * Reap asynchronous operation
 | 
		
	
		
			
			|  | 376 | + * Close FTP data transfer interface
 | 
		
	
		
			
			| 351 | 377 |   *
 | 
		
	
		
			
			| 352 |  | - * @v async		Asynchronous operation
 | 
		
	
		
			
			|  | 378 | + * @v xfer		FTP data transfer interface
 | 
		
	
		
			
			|  | 379 | + * @v rc		Reason for close
 | 
		
	
		
			
			| 353 | 380 |   */
 | 
		
	
		
			
			| 354 |  | -static void ftp_reap ( struct async *async ) {
 | 
		
	
		
			
			|  | 381 | +static void ftp_xfer_closed ( struct xfer_interface *xfer, int rc ) {
 | 
		
	
		
			
			| 355 | 382 |  	struct ftp_request *ftp =
 | 
		
	
		
			
			| 356 |  | -		container_of ( async, struct ftp_request, async );
 | 
		
	
		
			
			|  | 383 | +		container_of ( xfer, struct ftp_request, xfer );
 | 
		
	
		
			
			| 357 | 384 |  
 | 
		
	
		
			
			| 358 |  | -	free ( ftp );
 | 
		
	
		
			
			|  | 385 | +	DBGC ( ftp, "FTP %p data transfer interface closed: %s\n",
 | 
		
	
		
			
			|  | 386 | +	       ftp, strerror ( rc ) );
 | 
		
	
		
			
			|  | 387 | +	
 | 
		
	
		
			
			|  | 388 | +	ftp_done ( ftp, rc );
 | 
		
	
		
			
			| 359 | 389 |  }
 | 
		
	
		
			
			| 360 | 390 |  
 | 
		
	
		
			
			| 361 |  | -/** FTP asynchronous operations */
 | 
		
	
		
			
			| 362 |  | -static struct async_operations ftp_async_operations = {
 | 
		
	
		
			
			| 363 |  | -	.reap = ftp_reap,
 | 
		
	
		
			
			|  | 391 | +/** FTP data transfer interface operations */
 | 
		
	
		
			
			|  | 392 | +static struct xfer_interface_operations ftp_xfer_operations = {
 | 
		
	
		
			
			|  | 393 | +	.close		= ftp_xfer_closed,
 | 
		
	
		
			
			|  | 394 | +	.vredirect	= ignore_xfer_vredirect,
 | 
		
	
		
			
			|  | 395 | +	.request	= ignore_xfer_request,
 | 
		
	
		
			
			|  | 396 | +	.seek		= ignore_xfer_seek,
 | 
		
	
		
			
			|  | 397 | +	.alloc_iob	= default_xfer_alloc_iob,
 | 
		
	
		
			
			|  | 398 | +	.deliver_iob	= xfer_deliver_as_raw,
 | 
		
	
		
			
			|  | 399 | +	.deliver_raw	= ignore_xfer_deliver_raw,
 | 
		
	
		
			
			| 364 | 400 |  };
 | 
		
	
		
			
			| 365 | 401 |  
 | 
		
	
		
			
			|  | 402 | +/*****************************************************************************
 | 
		
	
		
			
			|  | 403 | + *
 | 
		
	
		
			
			|  | 404 | + * URI opener
 | 
		
	
		
			
			|  | 405 | + *
 | 
		
	
		
			
			|  | 406 | + */
 | 
		
	
		
			
			|  | 407 | +
 | 
		
	
		
			
			| 366 | 408 |  /**
 | 
		
	
		
			
			| 367 | 409 |   * Initiate an FTP connection
 | 
		
	
		
			
			| 368 | 410 |   *
 | 
		
	
		
			
			|  | 411 | + * @v xfer		Data transfer interface
 | 
		
	
		
			
			| 369 | 412 |   * @v uri		Uniform Resource Identifier
 | 
		
	
		
			
			| 370 |  | - * @v buffer		Buffer into which to download file
 | 
		
	
		
			
			| 371 |  | - * @v parent		Parent asynchronous operation
 | 
		
	
		
			
			| 372 | 413 |   * @ret rc		Return status code
 | 
		
	
		
			
			| 373 | 414 |   */
 | 
		
	
		
			
			| 374 |  | -int ftp_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
 | 
		
	
		
			
			| 375 |  | -	struct ftp_request *ftp = NULL;
 | 
		
	
		
			
			|  | 415 | +static int ftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
 | 
		
	
		
			
			|  | 416 | +	struct ftp_request *ftp;
 | 
		
	
		
			
			|  | 417 | +	struct sockaddr_tcpip server;
 | 
		
	
		
			
			| 376 | 418 |  	int rc;
 | 
		
	
		
			
			| 377 | 419 |  
 | 
		
	
		
			
			| 378 | 420 |  	/* Sanity checks */
 | 
		
	
		
			
			| 379 |  | -	if ( ! uri->path ) {
 | 
		
	
		
			
			| 380 |  | -		rc = -EINVAL;
 | 
		
	
		
			
			| 381 |  | -		goto err;
 | 
		
	
		
			
			| 382 |  | -	}
 | 
		
	
		
			
			|  | 421 | +	if ( ! uri->path )
 | 
		
	
		
			
			|  | 422 | +		return -EINVAL;
 | 
		
	
		
			
			|  | 423 | +	if ( ! uri->host )
 | 
		
	
		
			
			|  | 424 | +		return -EINVAL;
 | 
		
	
		
			
			| 383 | 425 |  
 | 
		
	
		
			
			| 384 |  | -	/* Allocate and populate FTP structure */
 | 
		
	
		
			
			|  | 426 | +	/* Allocate and populate structure */
 | 
		
	
		
			
			| 385 | 427 |  	ftp = malloc ( sizeof ( *ftp ) );
 | 
		
	
		
			
			| 386 |  | -	if ( ! ftp ) {
 | 
		
	
		
			
			| 387 |  | -		rc = -ENOMEM;
 | 
		
	
		
			
			| 388 |  | -		goto err;
 | 
		
	
		
			
			| 389 |  | -	}
 | 
		
	
		
			
			|  | 428 | +	if ( ! ftp )
 | 
		
	
		
			
			|  | 429 | +		return -ENOMEM;
 | 
		
	
		
			
			| 390 | 430 |  	memset ( ftp, 0, sizeof ( *ftp ) );
 | 
		
	
		
			
			| 391 |  | -	ftp->uri = uri;
 | 
		
	
		
			
			| 392 |  | -	ftp->buffer = buffer;
 | 
		
	
		
			
			| 393 |  | -	ftp->state = FTP_CONNECT;
 | 
		
	
		
			
			| 394 |  | -	ftp->already_sent = 0;
 | 
		
	
		
			
			|  | 431 | +	ftp->refcnt.free = ftp_free;
 | 
		
	
		
			
			|  | 432 | +	xfer_init ( &ftp->xfer, &ftp_xfer_operations, &ftp->refcnt );
 | 
		
	
		
			
			|  | 433 | +	ftp->uri = uri_get ( uri );
 | 
		
	
		
			
			|  | 434 | +	xfer_init ( &ftp->control, &ftp_control_operations, &ftp->refcnt );
 | 
		
	
		
			
			|  | 435 | +	xfer_init ( &ftp->data, &ftp_data_operations, &ftp->refcnt );
 | 
		
	
		
			
			| 395 | 436 |  	ftp->recvbuf = ftp->status_text;
 | 
		
	
		
			
			| 396 | 437 |  	ftp->recvsize = sizeof ( ftp->status_text ) - 1;
 | 
		
	
		
			
			| 397 |  | -	ftp->stream.op = &ftp_stream_operations;
 | 
		
	
		
			
			| 398 |  | -	ftp->stream_data.op = &ftp_data_stream_operations;
 | 
		
	
		
			
			| 399 |  | -
 | 
		
	
		
			
			| 400 |  | -#warning "Quick name resolution hack"
 | 
		
	
		
			
			| 401 |  | -	union {
 | 
		
	
		
			
			| 402 |  | -		struct sockaddr sa;
 | 
		
	
		
			
			| 403 |  | -		struct sockaddr_in sin;
 | 
		
	
		
			
			| 404 |  | -	} server;
 | 
		
	
		
			
			| 405 |  | -	server.sin.sin_port = htons ( FTP_PORT );
 | 
		
	
		
			
			| 406 |  | -	server.sin.sin_family = AF_INET;
 | 
		
	
		
			
			| 407 |  | -	if ( inet_aton ( uri->host, &server.sin.sin_addr ) == 0 ) {
 | 
		
	
		
			
			| 408 |  | -		rc = -EINVAL;
 | 
		
	
		
			
			| 409 |  | -		goto err;
 | 
		
	
		
			
			| 410 |  | -	}
 | 
		
	
		
			
			| 411 | 438 |  
 | 
		
	
		
			
			| 412 | 439 |  	DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path );
 | 
		
	
		
			
			| 413 | 440 |  
 | 
		
	
		
			
			| 414 |  | -	if ( ( rc = tcp_open ( &ftp->stream ) ) != 0 )
 | 
		
	
		
			
			| 415 |  | -		goto err;
 | 
		
	
		
			
			| 416 |  | -	if ( ( rc = stream_connect ( &ftp->stream, &server.sa ) ) != 0 )
 | 
		
	
		
			
			|  | 441 | +	/* Open control connection */
 | 
		
	
		
			
			|  | 442 | +	memset ( &server, 0, sizeof ( server ) );
 | 
		
	
		
			
			|  | 443 | +	server.st_port = htons ( uri_port ( uri, FTP_PORT ) );
 | 
		
	
		
			
			|  | 444 | +	if ( ( rc = xfer_open_named_socket ( &ftp->control, SOCK_STREAM,
 | 
		
	
		
			
			|  | 445 | +					     ( struct sockaddr * ) &server,
 | 
		
	
		
			
			|  | 446 | +					     uri->host, NULL ) ) != 0 )
 | 
		
	
		
			
			| 417 | 447 |  		goto err;
 | 
		
	
		
			
			| 418 | 448 |  
 | 
		
	
		
			
			| 419 |  | -	async_init ( &ftp->async, &ftp_async_operations, parent );
 | 
		
	
		
			
			|  | 449 | +	/* Attach to parent interface, mortalise self, and return */
 | 
		
	
		
			
			|  | 450 | +	xfer_plug_plug ( &ftp->xfer, xfer );
 | 
		
	
		
			
			|  | 451 | +	ref_put ( &ftp->refcnt );
 | 
		
	
		
			
			| 420 | 452 |  	return 0;
 | 
		
	
		
			
			| 421 | 453 |  
 | 
		
	
		
			
			| 422 | 454 |   err:
 | 
		
	
		
			
			| 423 | 455 |  	DBGC ( ftp, "FTP %p could not create request: %s\n", 
 | 
		
	
		
			
			| 424 | 456 |  	       ftp, strerror ( rc ) );
 | 
		
	
		
			
			| 425 |  | -	free ( ftp );
 | 
		
	
		
			
			|  | 457 | +	ref_put ( &ftp->refcnt );
 | 
		
	
		
			
			| 426 | 458 |  	return rc;
 | 
		
	
		
			
			| 427 | 459 |  }
 | 
		
	
		
			
			| 428 | 460 |  
 | 
		
	
		
			
			| 429 |  | -/** HTTP download protocol */
 | 
		
	
		
			
			| 430 |  | -struct download_protocol ftp_download_protocol __download_protocol = {
 | 
		
	
		
			
			| 431 |  | -	.name = "ftp",
 | 
		
	
		
			
			| 432 |  | -	.start_download = ftp_get,
 | 
		
	
		
			
			|  | 461 | +/** FTP URI opener */
 | 
		
	
		
			
			|  | 462 | +struct uri_opener ftp_uri_opener __uri_opener = {
 | 
		
	
		
			
			|  | 463 | +	.scheme	= "ftp",
 | 
		
	
		
			
			|  | 464 | +	.open	= ftp_open,
 | 
		
	
		
			
			| 433 | 465 |  };
 |