47b6ae3c912a3bcbe62c3a4b149bb5f9e504f8eb
[people/dverkamp/gpxe.git] / src / net / tcp / ftp.c
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/ftp.h>
15
16 /** @file
17  *
18  * File transfer protocol
19  *
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
102 /*****************************************************************************
103  *
104  * FTP control channel
105  *
106  */
107
108 /**
109  * FTP control channel strings
110  *
111  * These are used as printf() format strings.  Since only one of them
112  * (RETR) takes an argument, we always supply that argument to the
113  * snprintf() call.
114  */
115 static const char * ftp_strings[] = {
116         [FTP_CONNECT]   = "",
117         [FTP_USER]      = "USER anonymous\r\n",
118         [FTP_PASS]      = "PASS etherboot@etherboot.org\r\n",
119         [FTP_TYPE]      = "TYPE I\r\n",
120         [FTP_PASV]      = "PASV\r\n",
121         [FTP_RETR]      = "RETR %s\r\n", 
122         [FTP_QUIT]      = "QUIT\r\n",
123         [FTP_DONE]      = "",
124 };
125
126 /**
127  * Handle control channel being closed
128  *
129  * @v control           FTP control channel interface
130  * @v rc                Reason for close
131  *
132  * When the control channel is closed, the data channel must also be
133  * closed, if it is currently open.
134  */
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 );
138
139         DBGC ( ftp, "FTP %p control connection closed: %s\n",
140                ftp, strerror ( rc ) );
141
142         /* Complete FTP operation */
143         ftp_done ( ftp, rc );
144 }
145
146 /**
147  * Parse FTP byte sequence value
148  *
149  * @v text              Text string
150  * @v value             Value buffer
151  * @v len               Length of value buffer
152  *
153  * This parses an FTP byte sequence value (e.g. the "aaa,bbb,ccc,ddd"
154  * form for IP addresses in PORT commands) into a byte sequence.  @c
155  * *text will be updated to point beyond the end of the parsed byte
156  * sequence.
157  *
158  * This function is safe in the presence of malformed data, though the
159  * output is undefined.
160  */
161 static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
162         do {
163                 *(value++) = strtoul ( *text, text, 10 );
164                 if ( **text )
165                         (*text)++;
166         } while ( --len );
167 }
168
169 /**
170  * Handle an FTP control channel response
171  *
172  * @v ftp               FTP request
173  *
174  * This is called once we have received a complete response line.
175  */
176 static void ftp_reply ( struct ftp_request *ftp ) {
177         char status_major = ftp->status_text[0];
178         char separator = ftp->status_text[3];
179
180         DBGC ( ftp, "FTP %p received status %s\n", ftp, ftp->status_text );
181
182         /* Ignore malformed lines */
183         if ( separator != ' ' )
184                 return;
185
186         /* Ignore "intermediate" responses (1xx codes) */
187         if ( status_major == '1' )
188                 return;
189
190         /* Anything other than success (2xx) or, in the case of a
191          * repsonse to a "USER" command, a password prompt (3xx), is a
192          * fatal error.
193          */
194         if ( ! ( ( status_major == '2' ) ||
195                  ( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) ){
196                 /* Flag protocol error and close connections */
197                 ftp_done ( ftp, -EPROTO );
198         }
199
200         /* Open passive connection when we get "PASV" response */
201         if ( ftp->state == FTP_PASV ) {
202                 char *ptr = ftp->passive_text;
203                 union {
204                         struct sockaddr_in sin;
205                         struct sockaddr sa;
206                 } sa;
207                 int rc;
208
209                 sa.sin.sin_family = AF_INET;
210                 ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_addr,
211                                   sizeof ( sa.sin.sin_addr ) );
212                 ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_port,
213                                   sizeof ( sa.sin.sin_port ) );
214                 if ( ( rc = xfer_open_socket ( &ftp->data, SOCK_STREAM,
215                                                &sa.sa, NULL ) ) != 0 ) {
216                         DBGC ( ftp, "FTP %p could not open data connection\n",
217                                ftp );
218                         ftp_done ( ftp, rc );
219                         return;
220                 }
221         }
222
223         /* Move to next state */
224         if ( ftp->state < FTP_DONE )
225                 ftp->state++;
226
227         /* Send control string */
228         if ( ftp->state < FTP_DONE ) {
229                 DBGC ( ftp, "FTP %p sending ", ftp );
230                 DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
231                 xfer_printf ( &ftp->control, ftp_strings[ftp->state],
232                               ftp->uri->path );
233         }
234 }
235
236 /**
237  * Handle new data arriving on FTP control channel
238  *
239  * @v control           FTP control channel interface
240  * @v data              New data
241  * @v len               Length of new data
242  *
243  * Data is collected until a complete line is received, at which point
244  * its information is passed to ftp_reply().
245  */
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 );
250         char *recvbuf = ftp->recvbuf;
251         size_t recvsize = ftp->recvsize;
252         char c;
253         
254         while ( len-- ) {
255                 c = * ( ( char * ) data++ );
256                 switch ( c ) {
257                 case '\r' :
258                 case '\n' :
259                         /* End of line: call ftp_reply() to handle
260                          * completed reply.  Avoid calling ftp_reply()
261                          * twice if we receive both \r and \n.
262                          */
263                         if ( recvsize == 0 )
264                                 ftp_reply ( ftp );
265                         /* Start filling up the status code buffer */
266                         recvbuf = ftp->status_text;
267                         recvsize = sizeof ( ftp->status_text ) - 1;
268                         break;
269                 case '(' :
270                         /* Start filling up the passive parameter buffer */
271                         recvbuf = ftp->passive_text;
272                         recvsize = sizeof ( ftp->passive_text ) - 1;
273                         break;
274                 case ')' :
275                         /* Stop filling the passive parameter buffer */
276                         recvsize = 0;
277                         break;
278                 default :
279                         /* Fill up buffer if applicable */
280                         if ( recvsize > 0 ) {
281                                 *(recvbuf++) = c;
282                                 recvsize--;
283                         }
284                         break;
285                 }
286         }
287
288         /* Store for next invocation */
289         ftp->recvbuf = recvbuf;
290         ftp->recvsize = recvsize;
291
292         return 0;
293 }
294
295 /** FTP control channel operations */
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,
304 };
305
306 /*****************************************************************************
307  *
308  * FTP data channel
309  *
310  */
311
312 /**
313  * Handle FTP data channel being closed
314  *
315  * @v data              FTP data channel interface
316  * @v rc                Reason for closure
317  *
318  * When the data channel is closed, the control channel should be left
319  * alone; the server will send a completion message via the control
320  * channel which we'll pick up.
321  *
322  * If the data channel is closed due to an error, we abort the request.
323  */
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 );
327
328         DBGC ( ftp, "FTP %p data connection closed: %s\n",
329                ftp, strerror ( rc ) );
330         
331         /* If there was an error, close control channel and record status */
332         if ( rc )
333                 ftp_done ( ftp, rc );
334 }
335
336 /**
337  * Handle data delivery via FTP data channel
338  *
339  * @v xfer              FTP data channel interface
340  * @v iobuf             I/O buffer
341  * @ret rc              Return status code
342  */
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 );
347         int rc;
348
349         if ( ( rc = xfer_deliver_iob ( &ftp->xfer, iobuf ) ) != 0 ) {
350                 DBGC ( ftp, "FTP %p failed to deliver data: %s\n",
351                        ftp, strerror ( rc ) );
352                 return rc;
353         }
354
355         return 0;
356 }
357
358 /** FTP data channel operations */
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,
367 };
368
369 /*****************************************************************************
370  *
371  * Data transfer interface
372  *
373  */
374
375 /**
376  * Close FTP data transfer interface
377  *
378  * @v xfer              FTP data transfer interface
379  * @v rc                Reason for close
380  */
381 static void ftp_xfer_closed ( struct xfer_interface *xfer, int rc ) {
382         struct ftp_request *ftp =
383                 container_of ( xfer, struct ftp_request, xfer );
384
385         DBGC ( ftp, "FTP %p data transfer interface closed: %s\n",
386                ftp, strerror ( rc ) );
387         
388         ftp_done ( ftp, rc );
389 }
390
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,
400 };
401
402 /*****************************************************************************
403  *
404  * URI opener
405  *
406  */
407
408 /**
409  * Initiate an FTP connection
410  *
411  * @v xfer              Data transfer interface
412  * @v uri               Uniform Resource Identifier
413  * @ret rc              Return status code
414  */
415 static int ftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
416         struct ftp_request *ftp;
417         struct sockaddr_tcpip server;
418         int rc;
419
420         /* Sanity checks */
421         if ( ! uri->path )
422                 return -EINVAL;
423         if ( ! uri->host )
424                 return -EINVAL;
425
426         /* Allocate and populate structure */
427         ftp = malloc ( sizeof ( *ftp ) );
428         if ( ! ftp )
429                 return -ENOMEM;
430         memset ( ftp, 0, sizeof ( *ftp ) );
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 );
436         ftp->recvbuf = ftp->status_text;
437         ftp->recvsize = sizeof ( ftp->status_text ) - 1;
438
439         DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path );
440
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 )
447                 goto err;
448
449         /* Attach to parent interface, mortalise self, and return */
450         xfer_plug_plug ( &ftp->xfer, xfer );
451         ref_put ( &ftp->refcnt );
452         return 0;
453
454  err:
455         DBGC ( ftp, "FTP %p could not create request: %s\n", 
456                ftp, strerror ( rc ) );
457         ref_put ( &ftp->refcnt );
458         return rc;
459 }
460
461 /** FTP URI opener */
462 struct uri_opener ftp_uri_opener __uri_opener = {
463         .scheme = "ftp",
464         .open   = ftp_open,
465 };