mirror of
https://github.com/xcat2/xNBA.git
synced 2025-01-18 21:43:14 +00:00
Now have enough functions to implement a standard TFTP client in around 50
lines of code.
This commit is contained in:
parent
7d63b39e3d
commit
0b048e9cfb
25
src/include/tftpcore.h
Normal file
25
src/include/tftpcore.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef TFTPCORE_H
|
||||
#define TFTPCORE_H
|
||||
|
||||
#include "tftp.h"
|
||||
|
||||
extern int await_tftp ( int ival, void *ptr, unsigned short ptype,
|
||||
struct iphdr *ip, struct udphdr *udp,
|
||||
struct tcphdr *tcp );
|
||||
|
||||
extern int tftp_open ( struct tftp_state *state, const char *filename,
|
||||
union tftp_any **reply );
|
||||
|
||||
extern int tftp_process_opts ( struct tftp_state *state,
|
||||
struct tftp_oack *oack );
|
||||
|
||||
extern int tftp_ack_nowait ( struct tftp_state *state );
|
||||
|
||||
extern int tftp_ack ( struct tftp_state *state, union tftp_any **reply );
|
||||
|
||||
extern int tftp_error ( struct tftp_state *state, int errcode,
|
||||
const char *errmsg );
|
||||
|
||||
extern void tftp_set_errno ( struct tftp_error *error );
|
||||
|
||||
#endif /* TFTPCORE_H */
|
@ -2,6 +2,7 @@
|
||||
#include "tcp.h" /* for struct tcphdr */
|
||||
#include "errno.h"
|
||||
#include "etherboot.h"
|
||||
#include "tftpcore.h"
|
||||
|
||||
/** @file
|
||||
*
|
||||
@ -16,6 +17,9 @@
|
||||
* Wait for a TFTP packet
|
||||
*
|
||||
* @v ptr Pointer to a struct tftp_state
|
||||
* @v tftp_state::server::sin_addr TFTP server IP address
|
||||
* @v tftp_state::client::sin_addr Client multicast IP address, or 0.0.0.0
|
||||
* @v tftp_state::client::sin_port Client UDP port
|
||||
* @v ip IP header
|
||||
* @v udp UDP header
|
||||
* @ret True This is our TFTP packet
|
||||
@ -74,16 +78,20 @@ int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
|
||||
* @v filename File name
|
||||
* @ret True Received a non-error response
|
||||
* @ret False Received error response / no response
|
||||
* @ret tftp_state::server::sin_port TFTP server UDP port
|
||||
* @ret tftp_state::client::sin_port Client UDP port
|
||||
* @ret tftp_state::client::blksize Always #TFTP_DEFAULT_BLKSIZE
|
||||
* @ret *tftp The server's response, if any
|
||||
* @ret tftp_state::blksize Always #TFTP_DEFAULT_BLKSIZE
|
||||
* @ret *reply The server's response, if any
|
||||
* @err #PXENV_STATUS_TFTP_OPEN_TIMEOUT TFTP open timed out
|
||||
* @err other As returned by udp_transmit()
|
||||
* @err other As set by tftp_set_errno()
|
||||
*
|
||||
* Send a TFTP/TFTM/MTFTP RRQ (read request) to a TFTP server, and
|
||||
* return the server's reply (which may be an OACK, DATA or ERROR
|
||||
* packet). The server's reply will not be acknowledged, or processed
|
||||
* in any way.
|
||||
*
|
||||
* If tftp_state::server::sin_port is 0, the standard tftp server port
|
||||
* If tftp_state::server::sin_port is 0, the standard TFTP server port
|
||||
* (#TFTP_PORT) will be used.
|
||||
*
|
||||
* If tftp_state::client::sin_addr is not 0.0.0.0, it will be used as
|
||||
@ -121,6 +129,10 @@ int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
|
||||
* be assumed until the OACK packet is processed (by a subsequent call
|
||||
* to tftp_process_opts()).
|
||||
*
|
||||
* tftp_state::server::sin_port will be set to the UDP port from which
|
||||
* the server's response originated. This may or may not be the port
|
||||
* to which the open request was sent.
|
||||
*
|
||||
* The options "blksize", "tsize" and "multicast" will always be
|
||||
* appended to a TFTP open request. Servers that do not understand
|
||||
* any of these options should simply ignore them.
|
||||
@ -129,9 +141,11 @@ int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
|
||||
* the caller is responsible for calling join_group() and
|
||||
* leave_group() at appropriate times.
|
||||
*
|
||||
* If the response from the server is a TFTP ERROR packet, tftp_open()
|
||||
* will return False and #errno will be set accordingly.
|
||||
*/
|
||||
int tftp_open ( struct tftp_state *state, const char *filename,
|
||||
union tftp_any **tftp ) {
|
||||
union tftp_any **reply ) {
|
||||
static unsigned short lport = 2000; /* local port */
|
||||
int fixed_lport;
|
||||
struct tftp_rrq rrq;
|
||||
@ -164,7 +178,7 @@ int tftp_open ( struct tftp_state *state, const char *filename,
|
||||
state->blksize = TFTP_DEFAULT_BLKSIZE;
|
||||
|
||||
/* Nullify received packet pointer */
|
||||
*tftp = NULL;
|
||||
*reply = NULL;
|
||||
|
||||
/* Transmit RRQ until we get a response */
|
||||
for ( retry = 0 ; retry < MAX_TFTP_RETRIES ; retry++ ) {
|
||||
@ -186,7 +200,13 @@ int tftp_open ( struct tftp_state *state, const char *filename,
|
||||
|
||||
/* Wait for response */
|
||||
if ( await_reply ( await_tftp, 0, state, timeout ) ) {
|
||||
*tftp = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
||||
*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
||||
state->server.sin_port =
|
||||
ntohs ( (*reply)->common.udp.dest );
|
||||
if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
|
||||
tftp_set_errno ( &(*reply)->error );
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -321,13 +341,14 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
|
||||
* @v tftp_state::block Most recently received block number
|
||||
* @ret True Acknowledgement packet was sent
|
||||
* @ret False Acknowledgement packet was not sent
|
||||
* @err other As returned by udp_transmit()
|
||||
*
|
||||
* Send a TFTP ACK packet for the most recently received block.
|
||||
*
|
||||
* This sends only a single ACK packet; it does not wait for the
|
||||
* server's response.
|
||||
*/
|
||||
int tftp_ack ( struct tftp_state *state ) {
|
||||
int tftp_ack_nowait ( struct tftp_state *state ) {
|
||||
struct tftp_ack ack;
|
||||
|
||||
ack.opcode = htons ( TFTP_ACK );
|
||||
@ -337,6 +358,59 @@ int tftp_ack ( struct tftp_state *state ) {
|
||||
sizeof ( ack ), &ack );
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledge a TFTP packet and wait for a response
|
||||
*
|
||||
* @v state TFTP transfer state
|
||||
* @v tftp_state::server::sin_addr TFTP server IP address
|
||||
* @v tftp_state::server::sin_port TFTP server UDP port
|
||||
* @v tftp_state::client::sin_port Client UDP port
|
||||
* @v tftp_state::block Most recently received block number
|
||||
* @ret True Received a non-error response
|
||||
* @ret False Received error response / no response
|
||||
* @ret *reply The server's response, if any
|
||||
* @err #PXENV_STATUS_TFTP_READ_TIMEOUT Timed out waiting for a response
|
||||
* @err other As returned by tftp_ack_nowait()
|
||||
* @err other As set by tftp_set_errno()
|
||||
*
|
||||
* Send a TFTP ACK packet for the most recently received data block,
|
||||
* and keep transmitting this ACK until we get a response from the
|
||||
* server (e.g. a new data block).
|
||||
*
|
||||
* If the response is a TFTP DATA packet, no processing is done.
|
||||
* Specifically, the block number is not checked to ensure that this
|
||||
* is indeed the next data block in the sequence, nor is
|
||||
* tftp_state::block updated with the new block number.
|
||||
*
|
||||
* If the response from the server is a TFTP ERROR packet, tftp_open()
|
||||
* will return False and #errno will be set accordingly.
|
||||
*/
|
||||
int tftp_ack ( struct tftp_state *state, union tftp_any **reply ) {
|
||||
int retry;
|
||||
|
||||
*reply = NULL;
|
||||
for ( retry = 0 ; retry < MAX_TFTP_RETRIES ; retry++ ) {
|
||||
long timeout = rfc2131_sleep_interval ( TFTP_REXMT, retry );
|
||||
/* ACK the last data block */
|
||||
if ( ! tftp_ack_nowait ( state ) ) {
|
||||
DBG ( "TFTP: could not send ACK: %m\n" );
|
||||
return 0;
|
||||
}
|
||||
if ( await_reply ( await_tftp, 0, &state, timeout ) ) {
|
||||
/* We received a reply */
|
||||
*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
||||
if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
|
||||
tftp_set_errno ( &(*reply)->error );
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
DBG ( "TFTP: ACK retries exceeded\n" );
|
||||
errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a TFTP error
|
||||
*
|
||||
@ -344,7 +418,7 @@ int tftp_ack ( struct tftp_state *state ) {
|
||||
* @v tftp_state::server::sin_addr TFTP server IP address
|
||||
* @v tftp_state::server::sin_port TFTP server UDP port
|
||||
* @v tftp_state::client::sin_port Client UDP port
|
||||
* @v err TFTP error code
|
||||
* @v errcode TFTP error code
|
||||
* @v errmsg Descriptive error string
|
||||
* @ret True Error packet was sent
|
||||
* @ret False Error packet was not sent
|
||||
@ -352,14 +426,36 @@ int tftp_ack ( struct tftp_state *state ) {
|
||||
* Send a TFTP ERROR packet back to the server to terminate the
|
||||
* transfer.
|
||||
*/
|
||||
int tftp_error ( struct tftp_state *state, int err, const char *errmsg ) {
|
||||
int tftp_error ( struct tftp_state *state, int errcode, const char *errmsg ) {
|
||||
struct tftp_error error;
|
||||
|
||||
DBG ( "TFTPCORE: aborting with error %d (%s)\n", err, errmsg );
|
||||
DBG ( "TFTPCORE: aborting with error %d (%s)\n", errcode, errmsg );
|
||||
error.opcode = htons ( TFTP_ERROR );
|
||||
error.errcode = htons ( err );
|
||||
error.errcode = htons ( errcode );
|
||||
strncpy ( error.errmsg, errmsg, sizeof ( error.errmsg ) );
|
||||
return udp_transmit ( state->server.sin_addr.s_addr,
|
||||
state->client.sin_port, state->server.sin_port,
|
||||
sizeof ( error ), &error );
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a TFTP error
|
||||
*
|
||||
* @v error Pointer to a struct tftp_error
|
||||
*
|
||||
* Sets #errno based on the error code in a TFTP ERROR packet.
|
||||
*/
|
||||
void tftp_set_errno ( struct tftp_error *error ) {
|
||||
static int errmap[] = {
|
||||
[TFTP_ERR_FILE_NOT_FOUND] = PXENV_STATUS_TFTP_FILE_NOT_FOUND,
|
||||
[TFTP_ERR_ACCESS_DENIED] = PXENV_STATUS_TFTP_ACCESS_VIOLATION,
|
||||
[TFTP_ERR_ILLEGAL_OP] = PXENV_STATUS_TFTP_UNKNOWN_OPCODE,
|
||||
};
|
||||
unsigned int errcode = ntohs ( error->errcode );
|
||||
|
||||
errno = 0;
|
||||
if ( errcode < ( sizeof(errmap) / sizeof(errmap[0]) ) )
|
||||
errno = errmap[errcode];
|
||||
if ( ! errno )
|
||||
errno = PXENV_STATUS_TFTP_ERROR_OPCODE;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user