mirror of https://github.com/ipxe/ipxe.git
Update TFTP and FTP to take the same temporary URI scheme as HTTP
parent
afa752f5fb
commit
df0397f334
|
@ -38,12 +38,12 @@ enum ftp_state {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct ftp_request {
|
struct ftp_request {
|
||||||
/** Server address */
|
/** URI being fetched */
|
||||||
struct sockaddr_tcpip server;
|
struct uri *uri;
|
||||||
/** File to download */
|
|
||||||
const char *filename;
|
|
||||||
/** Data buffer to fill */
|
/** Data buffer to fill */
|
||||||
struct buffer *buffer;
|
struct buffer *buffer;
|
||||||
|
/** Asynchronous operation */
|
||||||
|
struct async async;
|
||||||
|
|
||||||
/** Current state */
|
/** Current state */
|
||||||
enum ftp_state state;
|
enum ftp_state state;
|
||||||
|
@ -57,16 +57,13 @@ struct ftp_request {
|
||||||
char status_text[4];
|
char status_text[4];
|
||||||
/** Passive-mode parameters, as text */
|
/** Passive-mode parameters, as text */
|
||||||
char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */
|
char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */
|
||||||
|
|
||||||
/** TCP application for the control channel */
|
/** TCP application for the control channel */
|
||||||
struct tcp_application tcp;
|
struct tcp_application tcp;
|
||||||
/** TCP application for the data channel */
|
/** TCP application for the data channel */
|
||||||
struct tcp_application tcp_data;
|
struct tcp_application tcp_data;
|
||||||
|
|
||||||
/** Asynchronous operation for this FTP operation */
|
|
||||||
struct async async;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct async_operation * ftp_get ( struct ftp_request *ftp );
|
extern int ftp_get ( struct uri *uri, struct buffer *buffer,
|
||||||
|
struct async *parent );
|
||||||
|
|
||||||
#endif /* _GPXE_FTP_H */
|
#endif /* _GPXE_FTP_H */
|
||||||
|
|
|
@ -86,12 +86,13 @@ union tftp_any {
|
||||||
* This data structure holds the state for an ongoing TFTP transfer.
|
* This data structure holds the state for an ongoing TFTP transfer.
|
||||||
*/
|
*/
|
||||||
struct tftp_session {
|
struct tftp_session {
|
||||||
/** UDP connection */
|
/** URI being fetched */
|
||||||
struct udp_connection udp;
|
struct uri *uri;
|
||||||
/** Filename */
|
|
||||||
const char *filename;
|
|
||||||
/** Data buffer to fill */
|
/** Data buffer to fill */
|
||||||
struct buffer *buffer;
|
struct buffer *buffer;
|
||||||
|
/** Asynchronous operation */
|
||||||
|
struct async async;
|
||||||
|
|
||||||
/** Requested data block size
|
/** Requested data block size
|
||||||
*
|
*
|
||||||
* This is the "blksize" option requested from the TFTP
|
* This is the "blksize" option requested from the TFTP
|
||||||
|
@ -133,15 +134,15 @@ struct tftp_session {
|
||||||
* (i.e. that no blocks have yet been received).
|
* (i.e. that no blocks have yet been received).
|
||||||
*/
|
*/
|
||||||
int state;
|
int state;
|
||||||
|
/** UDP connection */
|
||||||
/** Asynchronous operation for this session */
|
struct udp_connection udp;
|
||||||
struct async async;
|
|
||||||
/** Retransmission timer */
|
/** Retransmission timer */
|
||||||
struct retry_timer timer;
|
struct retry_timer timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Function prototypes */
|
/* Function prototypes */
|
||||||
|
|
||||||
extern struct async_operation * tftp_get ( struct tftp_session *tftp );
|
extern int tftp_get ( struct uri *uri, struct buffer *buffer,
|
||||||
|
struct async *parent );
|
||||||
|
|
||||||
#endif /* _GPXE_TFTP_H */
|
#endif /* _GPXE_TFTP_H */
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <gpxe/async.h>
|
#include <gpxe/async.h>
|
||||||
#include <gpxe/buffer.h>
|
#include <gpxe/buffer.h>
|
||||||
|
#include <gpxe/uri.h>
|
||||||
#include <gpxe/ftp.h>
|
#include <gpxe/ftp.h>
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
|
@ -20,44 +21,23 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** An FTP control channel string */
|
/** FTP control channel strings
|
||||||
struct ftp_string {
|
|
||||||
/** String format */
|
|
||||||
const char *format;
|
|
||||||
/** Offset to string data
|
|
||||||
*
|
*
|
||||||
* This is the offset within the struct ftp_request to the
|
* These are used as printf() format strings. Since only one of them
|
||||||
* pointer to the string data. Use ftp_string_data() to get a
|
* (RETR) takes an argument, we always supply that argument to the
|
||||||
* pointer to the actual data.
|
* snprintf() call.
|
||||||
*/
|
*/
|
||||||
off_t data_offset;
|
static const char * ftp_strings[] = {
|
||||||
|
[FTP_CONNECT] = "",
|
||||||
|
[FTP_USER] = "USER anonymous\r\n",
|
||||||
|
[FTP_PASS] = "PASS etherboot@etherboot.org\r\n",
|
||||||
|
[FTP_TYPE] = "TYPE I\r\n",
|
||||||
|
[FTP_PASV] = "PASV\r\n",
|
||||||
|
[FTP_RETR] = "RETR %s\r\n",
|
||||||
|
[FTP_QUIT] = "QUIT\r\n",
|
||||||
|
[FTP_DONE] = "",
|
||||||
};
|
};
|
||||||
|
|
||||||
/** FTP control channel strings */
|
|
||||||
static const struct ftp_string ftp_strings[] = {
|
|
||||||
[FTP_CONNECT] = { "", 0 },
|
|
||||||
[FTP_USER] = { "USER anonymous\r\n", 0 },
|
|
||||||
[FTP_PASS] = { "PASS etherboot@etherboot.org\r\n", 0 },
|
|
||||||
[FTP_TYPE] = { "TYPE I\r\n", 0 },
|
|
||||||
[FTP_PASV] = { "PASV\r\n", 0 },
|
|
||||||
[FTP_RETR] = { "RETR %s\r\n",
|
|
||||||
offsetof ( struct ftp_request, filename ) },
|
|
||||||
[FTP_QUIT] = { "QUIT\r\n", 0 },
|
|
||||||
[FTP_DONE] = { "", 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get data associated with an FTP control channel string
|
|
||||||
*
|
|
||||||
* @v ftp FTP request
|
|
||||||
* @v data_offset Data offset field from ftp_string structure
|
|
||||||
* @ret data Pointer to data
|
|
||||||
*/
|
|
||||||
static inline const void * ftp_string_data ( struct ftp_request *ftp,
|
|
||||||
off_t data_offset ) {
|
|
||||||
return * ( ( void ** ) ( ( ( void * ) ftp ) + data_offset ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get FTP request from control TCP application
|
* Get FTP request from control TCP application
|
||||||
*
|
*
|
||||||
|
@ -163,10 +143,8 @@ static void ftp_reply ( struct ftp_request *ftp ) {
|
||||||
ftp->already_sent = 0;
|
ftp->already_sent = 0;
|
||||||
|
|
||||||
if ( ftp->state < FTP_DONE ) {
|
if ( ftp->state < FTP_DONE ) {
|
||||||
const struct ftp_string *string = &ftp_strings[ftp->state];
|
|
||||||
DBGC ( ftp, "FTP %p sending ", ftp );
|
DBGC ( ftp, "FTP %p sending ", ftp );
|
||||||
DBGC ( ftp, string->format,
|
DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
|
||||||
ftp_string_data ( ftp, string->data_offset ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -250,14 +228,11 @@ static void ftp_acked ( struct tcp_application *app, size_t len ) {
|
||||||
static void ftp_senddata ( struct tcp_application *app,
|
static void ftp_senddata ( struct tcp_application *app,
|
||||||
void *buf, size_t len ) {
|
void *buf, size_t len ) {
|
||||||
struct ftp_request *ftp = tcp_to_ftp ( app );
|
struct ftp_request *ftp = tcp_to_ftp ( app );
|
||||||
const struct ftp_string *string;
|
|
||||||
|
|
||||||
/* Send the as-yet-unACKed portion of the string for the
|
/* Send the as-yet-unACKed portion of the string for the
|
||||||
* current state.
|
* current state.
|
||||||
*/
|
*/
|
||||||
string = &ftp_strings[ftp->state];
|
len = snprintf ( buf, len, ftp_strings[ftp->state], ftp->uri->path );
|
||||||
len = snprintf ( buf, len, string->format,
|
|
||||||
ftp_string_data ( ftp, string->data_offset ) );
|
|
||||||
tcp_send ( app, buf + ftp->already_sent, len - ftp->already_sent );
|
tcp_send ( app, buf + ftp->already_sent, len - ftp->already_sent );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,24 +335,83 @@ static struct tcp_operations ftp_data_tcp_operations = {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reap asynchronous operation
|
||||||
|
*
|
||||||
|
* @v async Asynchronous operation
|
||||||
|
*/
|
||||||
|
static void ftp_reap ( struct async *async ) {
|
||||||
|
struct ftp_request *ftp =
|
||||||
|
container_of ( async, struct ftp_request, async );
|
||||||
|
|
||||||
|
free ( ftp );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** FTP asynchronous operations */
|
||||||
|
static struct async_operations ftp_async_operations = {
|
||||||
|
.reap = ftp_reap,
|
||||||
|
};
|
||||||
|
|
||||||
|
#warning "Quick name resolution hack"
|
||||||
|
#include <byteswap.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate an FTP connection
|
* Initiate an FTP connection
|
||||||
*
|
*
|
||||||
* @v ftp FTP request
|
* @v uri Uniform Resource Identifier
|
||||||
|
* @v buffer Buffer into which to download file
|
||||||
|
* @v parent Parent asynchronous operation
|
||||||
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
struct async_operation * ftp_get ( struct ftp_request *ftp ) {
|
int ftp_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
|
||||||
|
struct ftp_request *ftp = NULL;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->filename );
|
/* Sanity checks */
|
||||||
|
if ( ! uri->path ) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate and populate FTP structure */
|
||||||
|
ftp = malloc ( sizeof ( *ftp ) );
|
||||||
|
if ( ! ftp ) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
memset ( ftp, 0, sizeof ( *ftp ) );
|
||||||
|
ftp->uri = uri;
|
||||||
|
ftp->buffer = buffer;
|
||||||
ftp->state = FTP_CONNECT;
|
ftp->state = FTP_CONNECT;
|
||||||
ftp->already_sent = 0;
|
ftp->already_sent = 0;
|
||||||
ftp->recvbuf = ftp->status_text;
|
ftp->recvbuf = ftp->status_text;
|
||||||
ftp->recvsize = sizeof ( ftp->status_text ) - 1;
|
ftp->recvsize = sizeof ( ftp->status_text ) - 1;
|
||||||
ftp->tcp.tcp_op = &ftp_tcp_operations;
|
ftp->tcp.tcp_op = &ftp_tcp_operations;
|
||||||
ftp->tcp_data.tcp_op = &ftp_data_tcp_operations;
|
ftp->tcp_data.tcp_op = &ftp_data_tcp_operations;
|
||||||
if ( ( rc = tcp_connect ( &ftp->tcp, &ftp->server, 0 ) ) != 0 )
|
|
||||||
ftp_done ( ftp, rc );
|
|
||||||
|
|
||||||
return &ftp->async;
|
#warning "Quick name resolution hack"
|
||||||
|
union {
|
||||||
|
struct sockaddr_tcpip st;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
} server;
|
||||||
|
server.sin.sin_port = htons ( FTP_PORT );
|
||||||
|
server.sin.sin_family = AF_INET;
|
||||||
|
if ( inet_aton ( uri->host, &server.sin.sin_addr ) == 0 ) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path );
|
||||||
|
|
||||||
|
if ( ( rc = tcp_connect ( &ftp->tcp, &server.st, 0 ) ) != 0 )
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
async_init ( &ftp->async, &ftp_async_operations, parent );
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
DBGC ( ftp, "FTP %p could not create request: %s\n",
|
||||||
|
ftp, strerror ( rc ) );
|
||||||
|
free ( ftp );
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <vsprintf.h>
|
#include <vsprintf.h>
|
||||||
#include <gpxe/async.h>
|
#include <gpxe/async.h>
|
||||||
#include <gpxe/tftp.h>
|
#include <gpxe/tftp.h>
|
||||||
|
#include <gpxe/uri.h>
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
*
|
*
|
||||||
|
@ -203,7 +204,7 @@ static int tftp_send_rrq ( struct tftp_session *tftp, void *buf, size_t len ) {
|
||||||
void *data;
|
void *data;
|
||||||
void *end;
|
void *end;
|
||||||
|
|
||||||
DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, tftp->filename );
|
DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, tftp->uri->path );
|
||||||
|
|
||||||
data = rrq->data;
|
data = rrq->data;
|
||||||
end = ( buf + len );
|
end = ( buf + len );
|
||||||
|
@ -211,7 +212,7 @@ static int tftp_send_rrq ( struct tftp_session *tftp, void *buf, size_t len ) {
|
||||||
goto overflow;
|
goto overflow;
|
||||||
data += ( snprintf ( data, ( end - data ),
|
data += ( snprintf ( data, ( end - data ),
|
||||||
"%s%coctet%cblksize%c%d%ctsize%c0",
|
"%s%coctet%cblksize%c%d%ctsize%c0",
|
||||||
tftp->filename, 0, 0, 0,
|
tftp->uri->path, 0, 0, 0,
|
||||||
tftp->request_blksize, 0, 0 ) + 1 );
|
tftp->request_blksize, 0, 0 ) + 1 );
|
||||||
if ( data > end )
|
if ( data > end )
|
||||||
goto overflow;
|
goto overflow;
|
||||||
|
@ -452,38 +453,85 @@ static struct udp_operations tftp_udp_operations = {
|
||||||
.newdata = tftp_newdata,
|
.newdata = tftp_newdata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reap asynchronous operation
|
||||||
|
*
|
||||||
|
* @v async Asynchronous operation
|
||||||
|
*/
|
||||||
|
static void tftp_reap ( struct async *async ) {
|
||||||
|
struct tftp_session *tftp =
|
||||||
|
container_of ( async, struct tftp_session, async );
|
||||||
|
|
||||||
|
free ( tftp );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TFTP asynchronous operations */
|
||||||
|
static struct async_operations tftp_async_operations = {
|
||||||
|
.reap = tftp_reap,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate TFTP download
|
* Initiate TFTP download
|
||||||
*
|
*
|
||||||
* @v tftp TFTP session
|
* @v uri Uniform Resource Identifier
|
||||||
* @ret aop Asynchronous operation
|
* @v buffer Buffer into which to download file
|
||||||
|
* @v parent Parent asynchronous operation
|
||||||
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
struct async_operation * tftp_get ( struct tftp_session *tftp ) {
|
int tftp_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
|
||||||
|
struct tftp_session *tftp = NULL;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
assert ( tftp->filename != NULL );
|
/* Sanity checks */
|
||||||
assert ( tftp->buffer != NULL );
|
if ( ! uri->path ) {
|
||||||
assert ( tftp->udp.peer.st_family != 0 );
|
rc = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialise TFTP session */
|
/* Allocate and populate TFTP structure */
|
||||||
|
tftp = malloc ( sizeof ( *tftp ) );
|
||||||
|
if ( ! tftp ) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
memset ( tftp, 0, sizeof ( *tftp ) );
|
||||||
|
tftp->uri = uri;
|
||||||
|
tftp->buffer = buffer;
|
||||||
if ( ! tftp->request_blksize )
|
if ( ! tftp->request_blksize )
|
||||||
tftp->request_blksize = TFTP_MAX_BLKSIZE;
|
tftp->request_blksize = TFTP_MAX_BLKSIZE;
|
||||||
tftp->blksize = TFTP_DEFAULT_BLKSIZE;
|
tftp->blksize = TFTP_DEFAULT_BLKSIZE;
|
||||||
tftp->tsize = 0;
|
|
||||||
tftp->tid = 0;
|
|
||||||
tftp->state = -1;
|
tftp->state = -1;
|
||||||
tftp->udp.udp_op = &tftp_udp_operations;
|
tftp->udp.udp_op = &tftp_udp_operations;
|
||||||
tftp->timer.expired = tftp_timer_expired;
|
tftp->timer.expired = tftp_timer_expired;
|
||||||
|
|
||||||
/* Open UDP connection */
|
|
||||||
if ( ( rc = udp_open ( &tftp->udp, 0 ) ) != 0 ) {
|
#warning "Quick name resolution hack"
|
||||||
async_done ( &tftp->async, rc );
|
union {
|
||||||
goto out;
|
struct sockaddr_tcpip st;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
} server;
|
||||||
|
server.sin.sin_port = htons ( TFTP_PORT );
|
||||||
|
server.sin.sin_family = AF_INET;
|
||||||
|
if ( inet_aton ( uri->host, &server.sin.sin_addr ) == 0 ) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
udp_connect ( &tftp->udp, &server.st );
|
||||||
|
|
||||||
|
|
||||||
|
/* Open UDP connection */
|
||||||
|
if ( ( rc = udp_open ( &tftp->udp, 0 ) ) != 0 )
|
||||||
|
goto err;
|
||||||
|
|
||||||
/* Transmit initial RRQ */
|
/* Transmit initial RRQ */
|
||||||
tftp_send_packet ( tftp );
|
tftp_send_packet ( tftp );
|
||||||
|
|
||||||
out:
|
async_init ( &tftp->async, &tftp_async_operations, parent );
|
||||||
return &tftp->async;
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
DBGC ( tftp, "TFTP %p could not create session: %s\n",
|
||||||
|
tftp, strerror ( rc ) );
|
||||||
|
free ( tftp );
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <gpxe/dhcp.h>
|
#include <gpxe/dhcp.h>
|
||||||
#include <gpxe/tftp.h>
|
#include <gpxe/tftp.h>
|
||||||
#include <gpxe/http.h>
|
#include <gpxe/http.h>
|
||||||
|
#include <gpxe/ftp.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch file
|
* Fetch file
|
||||||
|
@ -73,15 +74,17 @@ int fetch ( const char *uri_string, userptr_t *data, size_t *len ) {
|
||||||
int ( * download ) ( struct uri *uri, struct buffer *buffer,
|
int ( * download ) ( struct uri *uri, struct buffer *buffer,
|
||||||
struct async *parent );
|
struct async *parent );
|
||||||
|
|
||||||
#if 0
|
if ( ! uri->scheme ) {
|
||||||
server.sin.sin_port = htons ( TFTP_PORT );
|
download = tftp_get;
|
||||||
udp_connect ( &tftp.udp, &server.st );
|
} else {
|
||||||
tftp.filename = filename;
|
if ( strcmp ( uri->scheme, "http" ) == 0 ) {
|
||||||
tftp.buffer = &buffer;
|
|
||||||
aop = tftp_get ( &tftp );
|
|
||||||
#else
|
|
||||||
download = http_get;
|
download = http_get;
|
||||||
#endif
|
} else if ( strcmp ( uri->scheme, "ftp" ) == 0 ) {
|
||||||
|
download = ftp_get;
|
||||||
|
} else {
|
||||||
|
download = tftp_get;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async_init_orphan ( &async );
|
async_init_orphan ( &async );
|
||||||
if ( ( rc = download ( uri, &buffer, &async ) ) != 0 )
|
if ( ( rc = download ( uri, &buffer, &async ) ) != 0 )
|
||||||
|
|
Loading…
Reference in New Issue