mirror of
https://github.com/xcat2/xNBA.git
synced 2025-01-11 10:17:49 +00:00
Warnings purge
This commit is contained in:
parent
ef7f6e72a1
commit
b234d4a13e
@ -3,11 +3,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ip.h"
|
||||
#include "igmp.h"
|
||||
#include "background.h"
|
||||
#include "nic.h"
|
||||
#include "etherboot.h"
|
||||
#include <ip.h>
|
||||
#include <igmp.h>
|
||||
#include <background.h>
|
||||
#include <nic.h>
|
||||
#include <etherboot.h>
|
||||
|
||||
static unsigned long last_igmpv1 = 0;
|
||||
static struct igmptable_t igmptable[MAX_IGMP];
|
||||
@ -50,8 +50,7 @@ static void send_igmp_reports ( unsigned long now ) {
|
||||
igmp.igmp.chksum = ipchksum ( &igmp.igmp,
|
||||
sizeof ( igmp.igmp ) );
|
||||
ip_transmit ( sizeof ( igmp ), &igmp );
|
||||
DBG ( "IGMP sent report to %@\n",
|
||||
igmp.igmp.group.s_addr );
|
||||
DBG ( "IGMP sent report to %s\n", inet_ntoa ( igmp.igmp.group ) );
|
||||
/* Don't send another igmp report until asked */
|
||||
igmptable[i].time = 0;
|
||||
}
|
||||
@ -84,7 +83,7 @@ static void process_igmp ( unsigned long now, unsigned short ptype __unused,
|
||||
interval = ( igmp->response_time * TICKS_PER_SEC ) /10;
|
||||
}
|
||||
|
||||
DBG ( "IGMP received query for %@\n", igmp->group.s_addr );
|
||||
DBG ( "IGMP received query for %s\n", inet_ntoa ( igmp->group ) );
|
||||
for ( i = 0 ; i < MAX_IGMP ; i++ ) {
|
||||
uint32_t group = igmptable[i].group.s_addr;
|
||||
if ( ( group == 0 ) ||
|
||||
@ -101,7 +100,8 @@ static void process_igmp ( unsigned long now, unsigned short ptype __unused,
|
||||
if ( ( ( igmp->type == IGMPv1_REPORT ) ||
|
||||
( igmp->type == IGMPv2_REPORT ) ) &&
|
||||
( ip->dest.s_addr == igmp->group.s_addr ) ) {
|
||||
DBG ( "IGMP received report for %@\n", igmp->group.s_addr);
|
||||
DBG ( "IGMP received report for %s\n",
|
||||
inet_ntoa ( igmp->group ) );
|
||||
for ( i = 0 ; i < MAX_IGMP ; i++ ) {
|
||||
if ( ( igmptable[i].group.s_addr ==
|
||||
igmp->group.s_addr ) &&
|
||||
@ -142,7 +142,7 @@ void leave_group ( int slot ) {
|
||||
igmp.igmp.group.s_addr = igmptable[slot].group.s_addr;
|
||||
igmp.igmp.chksum = ipchksum ( &igmp.igmp, sizeof ( igmp ) );
|
||||
ip_transmit ( sizeof ( igmp ), &igmp );
|
||||
DBG ( "IGMP left group %@\n", igmp.igmp.group.s_addr );
|
||||
DBG ( "IGMP left group %s\n", inet_ntoa ( igmp.igmp.group ) );
|
||||
}
|
||||
memset ( &igmptable[slot], 0, sizeof ( igmptable[0] ) );
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ static void rpc_printerror(struct rpc_t *rpc)
|
||||
rpc->u.reply.astatus) {
|
||||
/* rpc_printerror() is called for any RPC related error,
|
||||
* suppress output if no low level RPC error happened. */
|
||||
DBG("RPC error: (%d,%d,%d)\n", ntohl(rpc->u.reply.rstatus),
|
||||
DBG("RPC error: (%ld,%ld,%ld)\n", ntohl(rpc->u.reply.rstatus),
|
||||
ntohl(rpc->u.reply.verifier),
|
||||
ntohl(rpc->u.reply.astatus));
|
||||
}
|
||||
@ -503,14 +503,14 @@ static int nfs ( char *url __unused, struct sockaddr_in *server,
|
||||
mount_server.sin_addr = nfs_server.sin_addr = server->sin_addr;
|
||||
mount_server.sin_port = rpc_lookup(server, PROG_MOUNT, 1, sport);
|
||||
if ( ! mount_server.sin_port ) {
|
||||
DBG ( "Cannot get mount port from %!:%d\n",
|
||||
server->sin_addr.s_addr, server->sin_port );
|
||||
DBG ( "Cannot get mount port from %s:%d\n",
|
||||
inet_ntoa ( server->sin_addr ), server->sin_port );
|
||||
return 0;
|
||||
}
|
||||
nfs_server.sin_port = rpc_lookup(server, PROG_NFS, 2, sport);
|
||||
if ( ! mount_server.sin_port ) {
|
||||
DBG ( "Cannot get nfs port from %!:%d\n",
|
||||
server->sin_addr.s_addr, server->sin_port );
|
||||
DBG ( "Cannot get nfs port from %s:%d\n",
|
||||
inet_ntoa ( server->sin_addr ), server->sin_port );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
208
src/proto/tftm.c
208
src/proto/tftm.c
@ -1,208 +0,0 @@
|
||||
#include "etherboot.h"
|
||||
#include "proto.h"
|
||||
#include "errno.h"
|
||||
#include "tftp.h"
|
||||
#include "tftpcore.h"
|
||||
|
||||
/** @file
|
||||
*
|
||||
* TFTM protocol
|
||||
*
|
||||
* TFTM is a protocol defined in RFC2090 as a multicast extension to
|
||||
* TFTP.
|
||||
*/
|
||||
|
||||
static inline int tftm_process_opts ( struct tftp_state *state,
|
||||
struct tftp_oack *oack ) {
|
||||
struct in_addr old_mcast_addr = state->multicast.sin_addr;
|
||||
|
||||
if ( ! tftp_process_opts ( state, oack ) )
|
||||
return 0;
|
||||
|
||||
if ( old_mcast_addr.s_addr != state->multicast.sin_addr.s_addr ) {
|
||||
if ( old_mcast_addr.s_addr ) {
|
||||
DBG ( "TFTM: Leaving multicast group %@\n",
|
||||
old_mcast_addr.s_addr );
|
||||
leave_group ( IGMP_SERVER );
|
||||
}
|
||||
DBG ( "TFTM: Joining multicast group %@\n",
|
||||
state->multicast.sin_addr.s_addr );
|
||||
join_group ( IGMP_SERVER, state->multicast.sin_addr.s_addr );
|
||||
}
|
||||
|
||||
DBG ( "TFTM: I am a %s client\n",
|
||||
( state->master ? "master" : "slave" ) );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static inline int tftm_process_data ( struct tftp_state *state,
|
||||
struct tftp_data *data,
|
||||
struct buffer *buffer ) {
|
||||
unsigned int blksize;
|
||||
off_t offset;
|
||||
|
||||
/* Calculate block size and offset within file */
|
||||
blksize = ( ntohs ( data->udp.len )
|
||||
+ offsetof ( typeof ( *data ), udp )
|
||||
- offsetof ( typeof ( *data ), data ) );
|
||||
offset = ( ntohs ( data->block ) - 1 ) * state->blksize;
|
||||
|
||||
/* Check for oversized block */
|
||||
if ( blksize > state->blksize ) {
|
||||
DBG ( "TFTM: oversized block size %d (max %d)\n",
|
||||
blksize, state->blksize );
|
||||
errno = PXENV_STATUS_TFTP_INVALID_PACKET_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Place block in the buffer */
|
||||
if ( ! fill_buffer ( buffer, data->data, offset, blksize ) ) {
|
||||
DBG ( "TFTM: could not place data in buffer: %m\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If this is the last block, record the filesize (in case the
|
||||
* server didn't supply a tsize option.
|
||||
*/
|
||||
if ( blksize < state->blksize ) {
|
||||
state->tsize = offset + blksize;
|
||||
}
|
||||
|
||||
/* Record the last received block */
|
||||
state->block = ntohs ( data->block );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static inline int tftm_next ( struct tftp_state *state,
|
||||
union tftp_any **reply,
|
||||
struct buffer *buffer ) {
|
||||
long listen_timeout;
|
||||
|
||||
listen_timeout = rfc2131_sleep_interval ( TIMEOUT, MAX_TFTP_RETRIES );
|
||||
|
||||
/* If we are not the master client, just listen for the next
|
||||
* packet
|
||||
*/
|
||||
if ( ! state->master ) {
|
||||
if ( tftp_get ( state, listen_timeout, reply ) ) {
|
||||
/* Heard a non-error packet */
|
||||
return 1;
|
||||
}
|
||||
if ( *reply ) {
|
||||
/* Received an error packet */
|
||||
return 0;
|
||||
}
|
||||
/* Didn't hear anything; try prodding the server */
|
||||
state->master = 1;
|
||||
}
|
||||
/* We are the master client; trigger the next packet
|
||||
* that we want
|
||||
*/
|
||||
state->block = buffer->fill / state->blksize;
|
||||
return tftp_ack ( state, reply );
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file via TFTM
|
||||
*
|
||||
* @v server TFTP server
|
||||
* @v file File name
|
||||
* @v buffer Buffer into which to load file
|
||||
* @ret True File was downloaded successfully
|
||||
* @ret False File was not downloaded successfully
|
||||
* @err #PXENV_STATUS_TFTP_UNKNOWN_OPCODE Unknown type of TFTP block received
|
||||
* @err other As returned by tftp_open()
|
||||
* @err other As returned by tftp_process_opts()
|
||||
* @err other As returned by tftp_ack()
|
||||
* @err other As returned by tftp_process_data()
|
||||
*
|
||||
* Download a file from a TFTP server into the specified buffer using
|
||||
* the TFTM protocol.
|
||||
*/
|
||||
static int tftm ( char *url __unused, struct sockaddr_in *server, char *file,
|
||||
struct buffer *buffer ) {
|
||||
struct tftp_state state;
|
||||
union tftp_any *reply;
|
||||
int rc = 0;
|
||||
|
||||
/* Initialise TFTP state */
|
||||
memset ( &state, 0, sizeof ( state ) );
|
||||
state.server = *server;
|
||||
|
||||
/* Start as the master. This means that if the TFTP server
|
||||
* doesn't actually support multicast, we'll still ACK the
|
||||
* packets and it should all proceed as for a normal TFTP
|
||||
* connection.
|
||||
*/
|
||||
state.master = 1;
|
||||
|
||||
/* Open the file */
|
||||
if ( ! tftp_open ( &state, file, &reply, 1 ) ) {
|
||||
DBG ( "TFTM: could not open %@:%d/%s : %m\n",
|
||||
server->sin_addr.s_addr, server->sin_port, file );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fetch file, a block at a time */
|
||||
while ( 1 ) {
|
||||
twiddle();
|
||||
/* Process the current packet */
|
||||
switch ( ntohs ( reply->common.opcode ) ) {
|
||||
case TFTP_OACK:
|
||||
/* Options can be received at any time */
|
||||
if ( ! tftm_process_opts ( &state, &reply->oack ) ) {
|
||||
DBG ( "TFTM: failed to process OACK: %m\n" );
|
||||
tftp_error ( &state, TFTP_ERR_BAD_OPTS, NULL );
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case TFTP_DATA:
|
||||
if ( ! tftm_process_data ( &state, &reply->data,
|
||||
buffer ) ) {
|
||||
DBG ( "TFTM: failed to process DATA: %m\n" );
|
||||
tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
|
||||
NULL );
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DBG ( "TFTM: unexpected packet type %d\n",
|
||||
ntohs ( reply->common.opcode ) );
|
||||
errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
|
||||
tftp_error ( &state, TFTP_ERR_ILLEGAL_OP, NULL );
|
||||
goto out;
|
||||
}
|
||||
/* If we know the filesize, and we have all the data, stop */
|
||||
if ( state.tsize && ( buffer->fill == state.tsize ) )
|
||||
break;
|
||||
/* Fetch the next packet */
|
||||
if ( ! tftm_next ( &state, &reply, buffer ) ) {
|
||||
DBG ( "TFTM: could not get next block: %m\n" );
|
||||
if ( ! reply ) {
|
||||
tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
|
||||
NULL );
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* ACK the final packet, as a courtesy to the server */
|
||||
tftp_ack_nowait ( &state );
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
if ( state.multicast.sin_addr.s_addr ) {
|
||||
leave_group ( IGMP_SERVER );
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct protocol tftm_protocol __protocol = {
|
||||
.name = "x-tftm",
|
||||
.default_port = TFTP_PORT,
|
||||
.load = tftm,
|
||||
};
|
@ -1,541 +0,0 @@
|
||||
#include "tftp.h"
|
||||
#include "old_tcp.h" /* for struct tcphdr */
|
||||
#include "errno.h"
|
||||
#include "etherboot.h"
|
||||
#include "tftpcore.h"
|
||||
|
||||
/** @file */
|
||||
|
||||
/**
|
||||
* await_reply() filter for TFTP packets
|
||||
*
|
||||
* @v ptr Pointer to a struct tftp_state
|
||||
* @v tftp_state::server::sin_addr TFTP server IP address
|
||||
* @v tftp_state::lport Client UDP port
|
||||
* @v tftp_state::multicast::sin_addr Multicast IP address, or 0.0.0.0
|
||||
* @v tftp_state::multicast::sin_port Multicast UDP port, or 0
|
||||
* @v ip IP header
|
||||
* @v udp UDP header
|
||||
* @ret True This is our TFTP packet
|
||||
* @ret False This is not one of our TFTP packets
|
||||
*
|
||||
* Wait for a TFTP packet that is part of the current connection
|
||||
* (i.e. comes from the TFTP server, has the correct destination port,
|
||||
* and is addressed either to our IP address and UDP port, or to our
|
||||
* multicast listening address and UDP port).
|
||||
*
|
||||
* Use await_tftp() in code such as
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* if ( await_reply ( await_tftp, 0, &tftp_state, timeout ) ) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* @endcode
|
||||
*/
|
||||
static int await_tftp ( int ival __unused, void *ptr,
|
||||
unsigned short ptype __unused, struct iphdr *ip,
|
||||
struct udphdr *udp, struct tcphdr *tcp __unused ) {
|
||||
struct tftp_state *state = ptr;
|
||||
|
||||
/* Must have valid UDP (and, therefore, also IP) headers */
|
||||
if ( ! udp ) {
|
||||
DBG2 ( "TFTPCORE: not UDP\n" );
|
||||
return 0;
|
||||
}
|
||||
/* Packet must come from the TFTP server */
|
||||
if ( ip->src.s_addr != state->server.sin_addr.s_addr ) {
|
||||
DBG2 ( "TFTPCORE: from %@, not from TFTP server %@\n",
|
||||
ip->src.s_addr, state->server.sin_addr.s_addr );
|
||||
return 0;
|
||||
}
|
||||
/* Packet may be addressed to our IP address and unicast UDP
|
||||
* port
|
||||
*/
|
||||
if ( ( ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr ) &&
|
||||
( ntohs ( udp->dest ) == state->lport ) ) {
|
||||
return 1;
|
||||
}
|
||||
/* Packet may be addressed to our multicast IP address and UDP
|
||||
* port, if we have one
|
||||
*/
|
||||
if ( ( state->multicast.sin_addr.s_addr ) &&
|
||||
( ip->dest.s_addr == state->multicast.sin_addr.s_addr ) &&
|
||||
( ntohs ( udp->dest ) == state->multicast.sin_port ) ) {
|
||||
return 1;
|
||||
}
|
||||
DBG2 ( "TFTPCORE: to %@:%d, not to %@:%d (or %@:%d)\n",
|
||||
ip->dest.s_addr, ntohs ( udp->dest ),
|
||||
arptable[ARP_CLIENT].ipaddr.s_addr, state->lport,
|
||||
state->multicast.sin_addr.s_addr, state->multicast.sin_port );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a TFTP packet
|
||||
*
|
||||
* @v state TFTP transfer state
|
||||
* @v tftp_state::server::sin_addr TFTP server IP address
|
||||
* @v tftp_state::lport Client UDP port
|
||||
* @v tftp_state::multicast::sin_addr Multicast IP address, or 0.0.0.0
|
||||
* @v tftp_state::multicast::sin_port Multicast UDP port, or 0
|
||||
* @v timeout Time to wait for a response
|
||||
* @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 No response received in time
|
||||
* @err other As set by tftp_set_errno()
|
||||
*
|
||||
* Retrieve the next packet sent by the TFTP server, if any is sent
|
||||
* within the specified timeout period. The packet is returned via
|
||||
* #reply. If no packet is received within the timeout period, a NULL
|
||||
* value will be stored in #reply.
|
||||
*
|
||||
* If the response from the server is a TFTP ERROR packet, tftp_get()
|
||||
* will return False and #errno will be set accordingly.
|
||||
*
|
||||
* You can differentiate between "received no response" and "received
|
||||
* an error response" by checking #reply; if #reply is NULL then no
|
||||
* response was received.
|
||||
*/
|
||||
int tftp_get ( struct tftp_state *state, long timeout,
|
||||
union tftp_any **reply ) {
|
||||
|
||||
*reply = NULL;
|
||||
|
||||
if ( ! await_reply ( await_tftp, 0, state, timeout ) ) {
|
||||
errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
||||
DBG ( "TFTPCORE: got reply (type %d)\n",
|
||||
ntohs ( (*reply)->common.opcode ) );
|
||||
if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
|
||||
tftp_set_errno ( &(*reply)->error );
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue a TFTP open request (RRQ)
|
||||
*
|
||||
* @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, or 0
|
||||
* @v tftp_state::lport Client UDP port, or 0
|
||||
* @v tftp_state::multicast::sin_addr Multicast IP address, or 0.0.0.0
|
||||
* @v tftp_state::multicast::sin_port Multicast UDP port, or 0
|
||||
* @v tftp_state::blksize Requested blksize, or 0
|
||||
* @v filename File name
|
||||
* @v multicast Enable/disable rfc2090 multicast TFTP
|
||||
* @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::lport Client UDP port
|
||||
* @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
|
||||
* (#TFTP_PORT) will be used.
|
||||
*
|
||||
* If tftp_state::lport is 0, the standard mechanism of
|
||||
* using a new, unique port number for each TFTP request will be used.
|
||||
*
|
||||
* If tftp_state::multicast::sin_addr is not 0.0.0.0, it (and
|
||||
* tftp_state::multicast::sin_port) will be used as a multicast
|
||||
* listening address for replies from the TFTP server.
|
||||
*
|
||||
* For the various different types of TFTP server, you should treat
|
||||
* tftp_state::lport and tftp_state::multicast as follows:
|
||||
*
|
||||
* - Standard TFTP server: set tftp_state::lport to 0,
|
||||
* tftp_state::multicast::sin_addr to 0.0.0.0 and
|
||||
* tftp_state::multicast::sin_port to 0. tftp_open() will set
|
||||
* tftp_state::lport to the assigned local UDP port.
|
||||
*
|
||||
* - TFTM server: set tftp_state::lport to 0,
|
||||
* tftp_state::multicast::sin_addr to 0.0.0.0 and
|
||||
* tftp_state::multicast::sin_port to 0. tftp_open() will set
|
||||
* tftp_state::lport to the assigned local UDP port. (Your call
|
||||
* to tftp_process_opts() will then overwrite both
|
||||
* tftp_state::multicast::sin_addr and
|
||||
* tftp_state::multicast::sin_port with the values specified in
|
||||
* the OACK packet.)
|
||||
*
|
||||
* - MTFTP server: set tftp_state::multicast::sin_addr to the
|
||||
* multicast address and both tftp_state::lport and
|
||||
* tftp_state::multicast::sin_port to the multicast port (both of
|
||||
* which must be previously known, e.g. provided by a DHCP
|
||||
* server). tftp_open() will not alter these values.
|
||||
*
|
||||
* If tftp_state::blksize is 0, the maximum blocksize
|
||||
* (#TFTP_MAX_BLKSIZE) will be requested.
|
||||
*
|
||||
* On exit, tftp_state::blksize will always contain
|
||||
* #TFTP_DEFAULT_BLKSIZE, since this is the blocksize value that must
|
||||
* 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" and "tsize" will always be appended to a TFTP
|
||||
* open request. The option "multicast" will be appended to the
|
||||
* request if #multicast is True. Servers that do not understand any
|
||||
* of these options should simply ignore them.
|
||||
*
|
||||
* tftp_open() will not automatically join or leave multicast groups;
|
||||
* 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 **reply, int multicast ) {
|
||||
static unsigned short lport = 2000; /* local port */
|
||||
int fixed_lport;
|
||||
struct tftp_rrq rrq;
|
||||
char *p;
|
||||
unsigned int rrqlen;
|
||||
int retry;
|
||||
|
||||
/* Flush receive queue */
|
||||
rx_qdrain();
|
||||
|
||||
/* Default to blksize of TFTP_MAX_BLKSIZE if none specified */
|
||||
if ( ! state->blksize )
|
||||
state->blksize = TFTP_MAX_BLKSIZE;
|
||||
|
||||
/* Use default TFTP server port if none specified */
|
||||
if ( ! state->server.sin_port )
|
||||
state->server.sin_port = TFTP_PORT;
|
||||
|
||||
/* Determine whether or not to use lport */
|
||||
fixed_lport = state->lport;
|
||||
|
||||
/* Set up RRQ */
|
||||
rrq.opcode = htons ( TFTP_RRQ );
|
||||
p = rrq.data;
|
||||
p += sprintf ( p, "%s%coctet%cblksize%c%d%ctsize%c0",
|
||||
filename, 0, 0, 0, state->blksize, 0, 0 ) + 1;
|
||||
if ( multicast ) {
|
||||
p += sprintf ( p, "multicast%c", 0 ) + 1;
|
||||
}
|
||||
rrqlen = ( p - ( char * ) &rrq );
|
||||
|
||||
/* Set negotiated blksize to default value */
|
||||
state->blksize = TFTP_DEFAULT_BLKSIZE;
|
||||
|
||||
/* Nullify received packet pointer */
|
||||
*reply = NULL;
|
||||
|
||||
/* Transmit RRQ until we get a response */
|
||||
for ( retry = 0 ; retry < MAX_TFTP_RETRIES ; retry++ ) {
|
||||
long timeout = rfc2131_sleep_interval ( TIMEOUT, retry );
|
||||
|
||||
/* Set client UDP port, if not already fixed */
|
||||
if ( ! fixed_lport )
|
||||
state->lport = ++lport;
|
||||
|
||||
/* Send the RRQ */
|
||||
DBG ( "TFTPCORE: requesting %@:%d/%s from port %d\n",
|
||||
state->server.sin_addr.s_addr, state->server.sin_port,
|
||||
rrq.data, state->lport );
|
||||
if ( ! udp_transmit ( state->server.sin_addr.s_addr,
|
||||
state->lport, state->server.sin_port,
|
||||
rrqlen, &rrq ) )
|
||||
return 0;
|
||||
|
||||
/* Wait for response */
|
||||
if ( tftp_get ( state, timeout, reply ) ) {
|
||||
/* We got a non-error response */
|
||||
state->server.sin_port
|
||||
= ntohs ( (*reply)->common.udp.src );
|
||||
DBG ( "TFTP server is at %@:%d\n",
|
||||
state->server.sin_addr.s_addr,
|
||||
state->server.sin_port );
|
||||
return 1;
|
||||
}
|
||||
if ( *reply ) {
|
||||
/* We got an error response; abort */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
DBG ( "TFTPCORE: open request timed out\n" );
|
||||
errno = PXENV_STATUS_TFTP_OPEN_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a TFTP OACK packet
|
||||
*
|
||||
* @v state TFTP transfer state
|
||||
* @v oack The TFTP OACK packet
|
||||
* @ret True Options were processed successfully
|
||||
* @ret False Options were not processed successfully
|
||||
* @ret tftp_state::blksize Negotiated blksize
|
||||
* @ret tftp_state::tsize File size (if known), or 0
|
||||
* @ret tftp_state::multicast::sin_addr Multicast IP address, or 0.0.0.0
|
||||
* @ret tftp_state::multicast::sin_port Multicast UDP port, or 0
|
||||
* @ret tftp_state::master Client is master
|
||||
* @err EINVAL An invalid option value was encountered
|
||||
*
|
||||
* Process the options returned by the TFTP server in an rfc2347 OACK
|
||||
* packet. The options "blksize" (rfc2348), "tsize" (rfc2349) and
|
||||
* "multicast" (rfc2090) are recognised and processed; any other
|
||||
* options are silently ignored.
|
||||
*
|
||||
* Where an option is not present in the OACK packet, the
|
||||
* corresponding field(s) in #state will be left unaltered.
|
||||
*
|
||||
* Calling tftp_process_opts() does not send an acknowledgement for
|
||||
* the OACK packet; this is the responsibility of the caller.
|
||||
*
|
||||
* @note If the "blksize" option is not present, tftp_state::blksize
|
||||
* will @b not be implicitly set to #TFTP_DEFAULT_BLKSIZE. However,
|
||||
* since tftp_open() always sets tftp_state::blksize to
|
||||
* #TFTP_DEFAULT_BLKSIZE before returning, you probably don't need to
|
||||
* worry about this.
|
||||
*/
|
||||
int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
|
||||
const char *p;
|
||||
const char *end;
|
||||
|
||||
DBG ( "TFTPCORE: processing OACK\n" );
|
||||
|
||||
/* End of options */
|
||||
end = ( ( char * ) &oack->udp ) + ntohs ( oack->udp.len );
|
||||
|
||||
/* Only possible error */
|
||||
errno = EINVAL;
|
||||
|
||||
for ( p = oack->data ; p < end ; ) {
|
||||
if ( strcasecmp ( "blksize", p ) == 0 ) {
|
||||
p += 8;
|
||||
state->blksize = strtoul ( p, &p, 10 );
|
||||
if ( *p ) {
|
||||
DBG ( "TFTPCORE: garbage \"%s\" "
|
||||
"after blksize\n", p );
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
DBG ( "TFTPCORE: got blksize %d\n", state->blksize );
|
||||
} else if ( strcasecmp ( "tsize", p ) == 0 ) {
|
||||
p += 6;
|
||||
state->tsize = strtoul ( p, &p, 10 );
|
||||
if ( *p ) {
|
||||
DBG ( "TFTPCORE: garbage \"%s\" "
|
||||
"after tsize\n", p );
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
DBG ( "TFTPCORE: got tsize %d\n", state->tsize );
|
||||
} else if ( strcasecmp ( "multicast", p ) == 0 ) {
|
||||
p += 10;
|
||||
char *e = strchr ( p, ',' );
|
||||
if ( ( ! e ) || ( e >= end ) ) {
|
||||
DBG ( "TFTPCORE: malformed multicast field "
|
||||
"\"%s\"\n", p );
|
||||
return 0;
|
||||
}
|
||||
/* IP address may be missing, in which case we
|
||||
* should leave state->multicast.sin_addr
|
||||
* unaltered.
|
||||
*/
|
||||
if ( e != p ) {
|
||||
int rc;
|
||||
*e = '\0';
|
||||
rc = inet_aton ( p,
|
||||
&state->multicast.sin_addr );
|
||||
*e = ',';
|
||||
if ( ! rc ) {
|
||||
DBG ( "TFTPCORE: malformed multicast "
|
||||
"IP address \"%s\"\n", p );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
p = e + 1;
|
||||
/* UDP port may also be missing */
|
||||
if ( *p != ',' ) {
|
||||
state->multicast.sin_port
|
||||
= strtoul ( p, &p, 10 );
|
||||
if ( *p != ',' ) {
|
||||
DBG ( "TFTPCORE: garbage \"%s\" "
|
||||
"after multicast port\n", p );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
/* "Master Client" must always be present */
|
||||
state->master = strtoul ( p, &p, 10 );
|
||||
if ( *p ) {
|
||||
DBG ( "TFTPCORE: garbage \"%s\" "
|
||||
"after multicast mc\n", p );
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
DBG ( "TFTPCORE: got multicast %@:%d (%s)\n",
|
||||
state->multicast.sin_addr.s_addr,
|
||||
state->multicast.sin_port,
|
||||
( state->master ? "master" : "not master" ) );
|
||||
} else {
|
||||
DBG ( "TFTPCORE: unknown option \"%s\"\n", p );
|
||||
p += strlen ( p ) + 1; /* skip option name */
|
||||
p += strlen ( p ) + 1; /* skip option value */
|
||||
}
|
||||
}
|
||||
|
||||
if ( p > end ) {
|
||||
DBG ( "TFTPCORE: overran options in OACK\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledge a TFTP packet
|
||||
*
|
||||
* @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::lport Client UDP port
|
||||
* @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_nowait ( struct tftp_state *state ) {
|
||||
struct tftp_ack ack;
|
||||
|
||||
DBG ( "TFTPCORE: acknowledging data block %d\n", state->block );
|
||||
ack.opcode = htons ( TFTP_ACK );
|
||||
ack.block = htons ( state->block );
|
||||
return udp_transmit ( state->server.sin_addr.s_addr,
|
||||
state->lport, state->server.sin_port,
|
||||
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::lport 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 ( tftp_get ( state, timeout, reply ) ) {
|
||||
/* We got a non-error response */
|
||||
return 1;
|
||||
}
|
||||
if ( *reply ) {
|
||||
/* We got an error response */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
DBG ( "TFTP: timed out during read\n" );
|
||||
errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a TFTP error
|
||||
*
|
||||
* @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::lport Client UDP port
|
||||
* @v errcode TFTP error code
|
||||
* @v errmsg Descriptive error string, or NULL
|
||||
* @ret True Error packet was sent
|
||||
* @ret False Error packet was not sent
|
||||
*
|
||||
* Send a TFTP ERROR packet back to the server to terminate the
|
||||
* transfer.
|
||||
*
|
||||
* If #errmsg is NULL, the current error message string as returned by
|
||||
* strerror(errno) will be used as the error text.
|
||||
*/
|
||||
int tftp_error ( struct tftp_state *state, int errcode, const char *errmsg ) {
|
||||
struct tftp_error error;
|
||||
|
||||
DBG ( "TFTPCORE: aborting with error %d (%s)\n", errcode, errmsg );
|
||||
error.opcode = htons ( TFTP_ERROR );
|
||||
error.errcode = htons ( errcode );
|
||||
strncpy ( error.errmsg, errmsg ? errmsg : strerror ( errno ),
|
||||
sizeof ( error.errmsg ) );
|
||||
return udp_transmit ( state->server.sin_addr.s_addr,
|
||||
state->lport, 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