• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
ftp_free(struct refcnt * refcnt)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  */
ftp_done(struct ftp_request * ftp,int rc)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  */
ftp_uri_path(struct ftp_request * ftp)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  */
ftp_user(struct ftp_request * ftp)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  */
ftp_password(struct ftp_request * ftp)151 static const char * ftp_password ( struct ftp_request *ftp ) {
152 	static char *ftp_default_password = "etherboot@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  */
ftp_control_close(struct xfer_interface * control,int rc)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  */
ftp_parse_value(char ** text,uint8_t * value,size_t len)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  */
ftp_next_state(struct ftp_request * ftp)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  */
ftp_reply(struct ftp_request * ftp)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  */
ftp_control_deliver_raw(struct xfer_interface * control,const void * data,size_t len)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  */
ftp_data_closed(struct xfer_interface * data,int rc)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  */
ftp_data_deliver_iob(struct xfer_interface * data,struct io_buffer * iobuf,struct xfer_metadata * meta __unused)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  */
ftp_xfer_closed(struct xfer_interface * xfer,int rc)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  */
ftp_open(struct xfer_interface * xfer,struct uri * uri)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