|
@@ -6,6 +6,7 @@
|
6
|
6
|
#include <errno.h>
|
7
|
7
|
#include <gpxe/async.h>
|
8
|
8
|
#include <gpxe/buffer.h>
|
|
9
|
+#include <gpxe/uri.h>
|
9
|
10
|
#include <gpxe/ftp.h>
|
10
|
11
|
|
11
|
12
|
/** @file
|
|
@@ -20,43 +21,22 @@
|
20
|
21
|
*
|
21
|
22
|
*/
|
22
|
23
|
|
23
|
|
-/** An FTP control channel string */
|
24
|
|
-struct ftp_string {
|
25
|
|
- /** String format */
|
26
|
|
- const char *format;
|
27
|
|
- /** Offset to string data
|
28
|
|
- *
|
29
|
|
- * This is the offset within the struct ftp_request to the
|
30
|
|
- * pointer to the string data. Use ftp_string_data() to get a
|
31
|
|
- * pointer to the actual data.
|
32
|
|
- */
|
33
|
|
- off_t data_offset;
|
34
|
|
-};
|
35
|
|
-
|
36
|
|
-/** FTP control channel strings */
|
37
|
|
-static const struct ftp_string ftp_strings[] = {
|
38
|
|
- [FTP_CONNECT] = { "", 0 },
|
39
|
|
- [FTP_USER] = { "USER anonymous\r\n", 0 },
|
40
|
|
- [FTP_PASS] = { "PASS etherboot@etherboot.org\r\n", 0 },
|
41
|
|
- [FTP_TYPE] = { "TYPE I\r\n", 0 },
|
42
|
|
- [FTP_PASV] = { "PASV\r\n", 0 },
|
43
|
|
- [FTP_RETR] = { "RETR %s\r\n",
|
44
|
|
- offsetof ( struct ftp_request, filename ) },
|
45
|
|
- [FTP_QUIT] = { "QUIT\r\n", 0 },
|
46
|
|
- [FTP_DONE] = { "", 0 },
|
47
|
|
-};
|
48
|
|
-
|
49
|
|
-/**
|
50
|
|
- * Get data associated with an FTP control channel string
|
|
24
|
+/** FTP control channel strings
|
51
|
25
|
*
|
52
|
|
- * @v ftp FTP request
|
53
|
|
- * @v data_offset Data offset field from ftp_string structure
|
54
|
|
- * @ret data Pointer to data
|
|
26
|
+ * These are used as printf() format strings. Since only one of them
|
|
27
|
+ * (RETR) takes an argument, we always supply that argument to the
|
|
28
|
+ * snprintf() call.
|
55
|
29
|
*/
|
56
|
|
-static inline const void * ftp_string_data ( struct ftp_request *ftp,
|
57
|
|
- off_t data_offset ) {
|
58
|
|
- return * ( ( void ** ) ( ( ( void * ) ftp ) + data_offset ) );
|
59
|
|
-}
|
|
30
|
+static const char * ftp_strings[] = {
|
|
31
|
+ [FTP_CONNECT] = "",
|
|
32
|
+ [FTP_USER] = "USER anonymous\r\n",
|
|
33
|
+ [FTP_PASS] = "PASS etherboot@etherboot.org\r\n",
|
|
34
|
+ [FTP_TYPE] = "TYPE I\r\n",
|
|
35
|
+ [FTP_PASV] = "PASV\r\n",
|
|
36
|
+ [FTP_RETR] = "RETR %s\r\n",
|
|
37
|
+ [FTP_QUIT] = "QUIT\r\n",
|
|
38
|
+ [FTP_DONE] = "",
|
|
39
|
+};
|
60
|
40
|
|
61
|
41
|
/**
|
62
|
42
|
* Get FTP request from control TCP application
|
|
@@ -163,10 +143,8 @@ static void ftp_reply ( struct ftp_request *ftp ) {
|
163
|
143
|
ftp->already_sent = 0;
|
164
|
144
|
|
165
|
145
|
if ( ftp->state < FTP_DONE ) {
|
166
|
|
- const struct ftp_string *string = &ftp_strings[ftp->state];
|
167
|
146
|
DBGC ( ftp, "FTP %p sending ", ftp );
|
168
|
|
- DBGC ( ftp, string->format,
|
169
|
|
- ftp_string_data ( ftp, string->data_offset ) );
|
|
147
|
+ DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
|
170
|
148
|
}
|
171
|
149
|
|
172
|
150
|
return;
|
|
@@ -250,14 +228,11 @@ static void ftp_acked ( struct tcp_application *app, size_t len ) {
|
250
|
228
|
static void ftp_senddata ( struct tcp_application *app,
|
251
|
229
|
void *buf, size_t len ) {
|
252
|
230
|
struct ftp_request *ftp = tcp_to_ftp ( app );
|
253
|
|
- const struct ftp_string *string;
|
254
|
231
|
|
255
|
232
|
/* Send the as-yet-unACKed portion of the string for the
|
256
|
233
|
* current state.
|
257
|
234
|
*/
|
258
|
|
- string = &ftp_strings[ftp->state];
|
259
|
|
- len = snprintf ( buf, len, string->format,
|
260
|
|
- ftp_string_data ( ftp, string->data_offset ) );
|
|
235
|
+ len = snprintf ( buf, len, ftp_strings[ftp->state], ftp->uri->path );
|
261
|
236
|
tcp_send ( app, buf + ftp->already_sent, len - ftp->already_sent );
|
262
|
237
|
}
|
263
|
238
|
|
|
@@ -360,24 +335,83 @@ static struct tcp_operations ftp_data_tcp_operations = {
|
360
|
335
|
*
|
361
|
336
|
*/
|
362
|
337
|
|
|
338
|
+/**
|
|
339
|
+ * Reap asynchronous operation
|
|
340
|
+ *
|
|
341
|
+ * @v async Asynchronous operation
|
|
342
|
+ */
|
|
343
|
+static void ftp_reap ( struct async *async ) {
|
|
344
|
+ struct ftp_request *ftp =
|
|
345
|
+ container_of ( async, struct ftp_request, async );
|
|
346
|
+
|
|
347
|
+ free ( ftp );
|
|
348
|
+}
|
|
349
|
+
|
|
350
|
+/** FTP asynchronous operations */
|
|
351
|
+static struct async_operations ftp_async_operations = {
|
|
352
|
+ .reap = ftp_reap,
|
|
353
|
+};
|
|
354
|
+
|
|
355
|
+#warning "Quick name resolution hack"
|
|
356
|
+#include <byteswap.h>
|
|
357
|
+
|
363
|
358
|
/**
|
364
|
359
|
* Initiate an FTP connection
|
365
|
360
|
*
|
366
|
|
- * @v ftp FTP request
|
|
361
|
+ * @v uri Uniform Resource Identifier
|
|
362
|
+ * @v buffer Buffer into which to download file
|
|
363
|
+ * @v parent Parent asynchronous operation
|
|
364
|
+ * @ret rc Return status code
|
367
|
365
|
*/
|
368
|
|
-struct async_operation * ftp_get ( struct ftp_request *ftp ) {
|
|
366
|
+int ftp_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
|
|
367
|
+ struct ftp_request *ftp = NULL;
|
369
|
368
|
int rc;
|
370
|
369
|
|
371
|
|
- DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->filename );
|
|
370
|
+ /* Sanity checks */
|
|
371
|
+ if ( ! uri->path ) {
|
|
372
|
+ rc = -EINVAL;
|
|
373
|
+ goto err;
|
|
374
|
+ }
|
372
|
375
|
|
|
376
|
+ /* Allocate and populate FTP structure */
|
|
377
|
+ ftp = malloc ( sizeof ( *ftp ) );
|
|
378
|
+ if ( ! ftp ) {
|
|
379
|
+ rc = -ENOMEM;
|
|
380
|
+ goto err;
|
|
381
|
+ }
|
|
382
|
+ memset ( ftp, 0, sizeof ( *ftp ) );
|
|
383
|
+ ftp->uri = uri;
|
|
384
|
+ ftp->buffer = buffer;
|
373
|
385
|
ftp->state = FTP_CONNECT;
|
374
|
386
|
ftp->already_sent = 0;
|
375
|
387
|
ftp->recvbuf = ftp->status_text;
|
376
|
388
|
ftp->recvsize = sizeof ( ftp->status_text ) - 1;
|
377
|
389
|
ftp->tcp.tcp_op = &ftp_tcp_operations;
|
378
|
390
|
ftp->tcp_data.tcp_op = &ftp_data_tcp_operations;
|
379
|
|
- if ( ( rc = tcp_connect ( &ftp->tcp, &ftp->server, 0 ) ) != 0 )
|
380
|
|
- ftp_done ( ftp, rc );
|
381
|
391
|
|
382
|
|
- return &ftp->async;
|
|
392
|
+#warning "Quick name resolution hack"
|
|
393
|
+ union {
|
|
394
|
+ struct sockaddr_tcpip st;
|
|
395
|
+ struct sockaddr_in sin;
|
|
396
|
+ } server;
|
|
397
|
+ server.sin.sin_port = htons ( FTP_PORT );
|
|
398
|
+ server.sin.sin_family = AF_INET;
|
|
399
|
+ if ( inet_aton ( uri->host, &server.sin.sin_addr ) == 0 ) {
|
|
400
|
+ rc = -EINVAL;
|
|
401
|
+ goto err;
|
|
402
|
+ }
|
|
403
|
+
|
|
404
|
+ DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path );
|
|
405
|
+
|
|
406
|
+ if ( ( rc = tcp_connect ( &ftp->tcp, &server.st, 0 ) ) != 0 )
|
|
407
|
+ goto err;
|
|
408
|
+
|
|
409
|
+ async_init ( &ftp->async, &ftp_async_operations, parent );
|
|
410
|
+ return 0;
|
|
411
|
+
|
|
412
|
+ err:
|
|
413
|
+ DBGC ( ftp, "FTP %p could not create request: %s\n",
|
|
414
|
+ ftp, strerror ( rc ) );
|
|
415
|
+ free ( ftp );
|
|
416
|
+ return rc;
|
383
|
417
|
}
|