mirror of https://github.com/ipxe/ipxe.git
TFTP upgraded to use a core function library (in tftpcore.c) which will be
shared between TFTP, TFTM and MTFTP protocols.pull/1/head
parent
0b048e9cfb
commit
53a4436d94
|
@ -154,7 +154,6 @@ enum {
|
||||||
#include "udp.h"
|
#include "udp.h"
|
||||||
#include "tcp.h"
|
#include "tcp.h"
|
||||||
#include "bootp.h"
|
#include "bootp.h"
|
||||||
#include "tftp.h"
|
|
||||||
#include "igmp.h"
|
#include "igmp.h"
|
||||||
#include "nfs.h"
|
#include "nfs.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "pxe_types.h"
|
#include "pxe_types.h"
|
||||||
#include "pxe_api.h"
|
#include "pxe_api.h"
|
||||||
#include "etherboot.h"
|
#include "etherboot.h"
|
||||||
|
#include "tftp.h"
|
||||||
|
|
||||||
/* Union used for PXE API calls; we don't know the type of the
|
/* Union used for PXE API calls; we don't know the type of the
|
||||||
* structure until we interpret the opcode. Also, Status is available
|
* structure until we interpret the opcode. Also, Status is available
|
||||||
|
@ -88,7 +89,7 @@ typedef struct pxe_stack {
|
||||||
uint32_t magic_cookie;
|
uint32_t magic_cookie;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
int eof;
|
int eof;
|
||||||
char data[TFTP_MAX_PACKET];
|
char data[TFTP_MAX_BLKSIZE];
|
||||||
} tftpdata;
|
} tftpdata;
|
||||||
struct {
|
struct {
|
||||||
char *buffer;
|
char *buffer;
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
#ifndef TFTP_H
|
#ifndef TFTP_H
|
||||||
#define TFTP_H
|
#define TFTP_H
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
|
||||||
#include "in.h"
|
#include "in.h"
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "nic.h"
|
#include "nic.h"
|
||||||
|
#include "ip.h"
|
||||||
|
#include "udp.h"
|
||||||
|
|
||||||
#define TFTP_PORT 69
|
#define TFTP_PORT 69 /**< Default TFTP server port */
|
||||||
#define TFTP_DEFAULTSIZE_PACKET 512
|
#define TFTP_DEFAULT_BLKSIZE 512
|
||||||
#define TFTP_MAX_PACKET 1432 /* 512 */
|
#define TFTP_MAX_BLKSIZE 1432 /* 512 */
|
||||||
|
|
||||||
#define TFTP_RRQ 1
|
#define TFTP_RRQ 1
|
||||||
#define TFTP_WRQ 2
|
#define TFTP_WRQ 2
|
||||||
|
@ -16,53 +20,132 @@
|
||||||
#define TFTP_ERROR 5
|
#define TFTP_ERROR 5
|
||||||
#define TFTP_OACK 6
|
#define TFTP_OACK 6
|
||||||
|
|
||||||
#define TFTP_CODE_EOF 1
|
#define TFTP_ERR_FILE_NOT_FOUND 1 /**< File not found */
|
||||||
#define TFTP_CODE_MORE 2
|
#define TFTP_ERR_ACCESS_DENIED 2 /**< Access violation */
|
||||||
#define TFTP_CODE_ERROR 3
|
#define TFTP_ERR_DISK_FULL 3 /**< Disk full or allocation exceeded */
|
||||||
#define TFTP_CODE_BOOT 4
|
#define TFTP_ERR_ILLEGAL_OP 4 /**< Illegal TFTP operation */
|
||||||
#define TFTP_CODE_CFG 5
|
#define TFTP_ERR_UNKNOWN_TID 5 /**< Unknown transfer ID */
|
||||||
|
#define TFTP_ERR_FILE_EXISTS 6 /**< File already exists */
|
||||||
|
#define TFTP_ERR_UNKNOWN_USER 7 /**< No such user */
|
||||||
|
#define TFTP_ERR_BAD_OPTS 8 /**< Option negotiation failed */
|
||||||
|
|
||||||
struct tftp_t {
|
/** A TFTP request (RRQ) packet */
|
||||||
|
struct tftp_rrq {
|
||||||
struct iphdr ip;
|
struct iphdr ip;
|
||||||
struct udphdr udp;
|
struct udphdr udp;
|
||||||
uint16_t opcode;
|
uint16_t opcode;
|
||||||
union {
|
char data[TFTP_DEFAULT_BLKSIZE];
|
||||||
uint8_t rrq[TFTP_DEFAULTSIZE_PACKET];
|
|
||||||
struct {
|
|
||||||
uint16_t block;
|
|
||||||
uint8_t download[TFTP_MAX_PACKET];
|
|
||||||
} data;
|
|
||||||
struct {
|
|
||||||
uint16_t block;
|
|
||||||
} ack;
|
|
||||||
struct {
|
|
||||||
uint16_t errcode;
|
|
||||||
uint8_t errmsg[TFTP_DEFAULTSIZE_PACKET];
|
|
||||||
} err;
|
|
||||||
struct {
|
|
||||||
uint8_t data[TFTP_DEFAULTSIZE_PACKET+2];
|
|
||||||
} oack;
|
|
||||||
} u;
|
|
||||||
} PACKED;
|
} PACKED;
|
||||||
|
|
||||||
/* define a smaller tftp packet solely for making requests to conserve stack
|
/** A TFTP data (DATA) packet */
|
||||||
512 bytes should be enough */
|
struct tftp_data {
|
||||||
struct tftpreq_t {
|
|
||||||
struct iphdr ip;
|
struct iphdr ip;
|
||||||
struct udphdr udp;
|
struct udphdr udp;
|
||||||
uint16_t opcode;
|
uint16_t opcode;
|
||||||
union {
|
|
||||||
uint8_t rrq[512];
|
|
||||||
struct {
|
|
||||||
uint16_t block;
|
uint16_t block;
|
||||||
} ack;
|
uint8_t data[TFTP_MAX_BLKSIZE];
|
||||||
struct {
|
|
||||||
uint16_t errcode;
|
|
||||||
uint8_t errmsg[512-2];
|
|
||||||
} err;
|
|
||||||
} u;
|
|
||||||
} PACKED;
|
} PACKED;
|
||||||
|
|
||||||
|
/** A TFTP acknowledgement (ACK) packet */
|
||||||
|
struct tftp_ack {
|
||||||
|
struct iphdr ip;
|
||||||
|
struct udphdr udp;
|
||||||
|
uint16_t opcode;
|
||||||
|
uint16_t block;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
/** A TFTP error (ERROR) packet */
|
||||||
|
struct tftp_error {
|
||||||
|
struct iphdr ip;
|
||||||
|
struct udphdr udp;
|
||||||
|
uint16_t opcode;
|
||||||
|
uint16_t errcode;
|
||||||
|
char errmsg[TFTP_DEFAULT_BLKSIZE];
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
/** A TFTP options acknowledgement (OACK) packet */
|
||||||
|
struct tftp_oack {
|
||||||
|
struct iphdr ip;
|
||||||
|
struct udphdr udp;
|
||||||
|
uint16_t opcode;
|
||||||
|
uint8_t data[TFTP_DEFAULT_BLKSIZE];
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
/** The common header of all TFTP packets */
|
||||||
|
struct tftp_common {
|
||||||
|
struct iphdr ip;
|
||||||
|
struct udphdr udp;
|
||||||
|
uint16_t opcode;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
/** A union encapsulating all TFTP packet types */
|
||||||
|
union tftp_any {
|
||||||
|
struct tftp_common common;
|
||||||
|
struct tftp_rrq rrq;
|
||||||
|
struct tftp_data data;
|
||||||
|
struct tftp_ack ack;
|
||||||
|
struct tftp_error error;
|
||||||
|
struct tftp_oack oack;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TFTP state
|
||||||
|
*
|
||||||
|
* This data structure holds the state for an ongoing TFTP transfer.
|
||||||
|
*/
|
||||||
|
struct tftp_state {
|
||||||
|
/** TFTP server address
|
||||||
|
*
|
||||||
|
* This is the IP address and UDP port from which data packets
|
||||||
|
* will be sent, and to which ACK packets should be sent.
|
||||||
|
*/
|
||||||
|
struct sockaddr_in server;
|
||||||
|
/** TFTP client address
|
||||||
|
*
|
||||||
|
* The IP address, if any, is the multicast address to which
|
||||||
|
* data packets will be sent. The client will always send
|
||||||
|
* packets from its own IP address.
|
||||||
|
*
|
||||||
|
* The UDP port is the port from which the open request will
|
||||||
|
* be sent, and to which data packets will be sent. (Due to
|
||||||
|
* the "design" of the MTFTP protocol, the master client will
|
||||||
|
* receive its first data packet as unicast, and subsequent
|
||||||
|
* packets as multicast.)
|
||||||
|
*/
|
||||||
|
struct sockaddr_in client;
|
||||||
|
/** Master client
|
||||||
|
*
|
||||||
|
* This will be true if the client is the master client for a
|
||||||
|
* multicast protocol (i.e. MTFTP or TFTM). (It will always
|
||||||
|
* be true for a non-multicast protocol, i.e. plain old TFTP).
|
||||||
|
*/
|
||||||
|
int master;
|
||||||
|
/** Data block size
|
||||||
|
*
|
||||||
|
* This is the "blksize" option negotiated with the TFTP
|
||||||
|
* server. (If the TFTP server does not support TFTP options,
|
||||||
|
* this will default to 512).
|
||||||
|
*/
|
||||||
|
unsigned int blksize;
|
||||||
|
/** File size
|
||||||
|
*
|
||||||
|
* This is the value returned in the "tsize" option from the
|
||||||
|
* TFTP server. If the TFTP server does not support the
|
||||||
|
* "tsize" option, this value will be zero.
|
||||||
|
*/
|
||||||
|
off_t tsize;
|
||||||
|
/** Last received block
|
||||||
|
*
|
||||||
|
* The block number of the most recent block received from the
|
||||||
|
* TFTP server. Note that the first data block is block 1; a
|
||||||
|
* value of 0 indicates that no data blocks have yet been
|
||||||
|
* received.
|
||||||
|
*/
|
||||||
|
unsigned int block;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct tftpreq_info_t {
|
struct tftpreq_info_t {
|
||||||
struct sockaddr_in *server;
|
struct sockaddr_in *server;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
|
@ -131,7 +131,7 @@ PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
|
||||||
request.blksize = tftp_open->PacketSize;
|
request.blksize = tftp_open->PacketSize;
|
||||||
DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress,
|
DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress,
|
||||||
tftp_open->TFTPPort, request.name, request.blksize );
|
tftp_open->TFTPPort, request.name, request.blksize );
|
||||||
if ( !request.blksize ) request.blksize = TFTP_DEFAULTSIZE_PACKET;
|
if ( !request.blksize ) request.blksize = TFTP_DEFAULT_BLKSIZE;
|
||||||
/* Make request and get first packet */
|
/* Make request and get first packet */
|
||||||
if ( !tftp_block ( &request, &block ) ) {
|
if ( !tftp_block ( &request, &block ) ) {
|
||||||
tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
|
tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "etherboot.h"
|
#include "etherboot.h"
|
||||||
#include "ip.h"
|
#include "ip.h"
|
||||||
#include "tcp.h"
|
#include "tcp.h"
|
||||||
|
#include "nic.h"
|
||||||
|
|
||||||
void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
|
void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
|
||||||
unsigned int destsock, long send_seq, long recv_seq,
|
unsigned int destsock, long send_seq, long recv_seq,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#if 0
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
*
|
*
|
||||||
* proto_tftm.c -- Etherboot Multicast TFTP
|
* proto_tftm.c -- Etherboot Multicast TFTP
|
||||||
|
@ -481,3 +483,5 @@ static struct protocol tftm_protocol __protocol = {
|
||||||
.default_port = TFTM_PORT,
|
.default_port = TFTM_PORT,
|
||||||
.load = url_tftm,
|
.load = url_tftm,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
238
src/proto/tftp.c
238
src/proto/tftp.c
|
@ -1,169 +1,111 @@
|
||||||
#include "etherboot.h"
|
#include "etherboot.h"
|
||||||
#include "in.h"
|
|
||||||
#include "nic.h"
|
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
#include "errno.h"
|
||||||
#include "tftp.h"
|
#include "tftp.h"
|
||||||
|
#include "tftpcore.h"
|
||||||
|
|
||||||
/* Utility function for tftp_block() */
|
/** @file
|
||||||
static int await_tftp ( int ival, void *ptr __unused,
|
*
|
||||||
unsigned short ptype __unused, struct iphdr *ip,
|
* TFTP protocol
|
||||||
struct udphdr *udp, struct tcphdr *tcp __unused ) {
|
*/
|
||||||
if ( ! udp ) {
|
|
||||||
|
/**
|
||||||
|
* Process a TFTP block
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static inline int process_tftp_data ( struct tftp_state *state,
|
||||||
|
struct tftp_data *data,
|
||||||
|
struct buffer *buffer,
|
||||||
|
int *eof ) {
|
||||||
|
unsigned int blksize;
|
||||||
|
|
||||||
|
/* Check it's the correct DATA block */
|
||||||
|
if ( ntohs ( data->block ) != ( state->block + 1 ) ) {
|
||||||
|
DBG ( "TFTP: got block %d, wanted block %d\n",
|
||||||
|
ntohs ( data->block ), state->block + 1 );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* Check it's an acceptable size */
|
||||||
|
blksize = ( ntohs ( data->udp.len )
|
||||||
|
+ offsetof ( typeof ( *data ), udp )
|
||||||
|
- offsetof ( typeof ( *data ), data ) );
|
||||||
|
if ( blksize > state->blksize ) {
|
||||||
|
DBG ( "TFTP: oversized block size %d (max %d)\n",
|
||||||
|
blksize, state->blksize );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* Place block in the buffer */
|
||||||
|
if ( ! fill_buffer ( buffer, data->data, state->block * state->blksize,
|
||||||
|
blksize ) ) {
|
||||||
|
DBG ( "TFTP: could not place data in buffer: %m\n" );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ( arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr )
|
/* Increment block counter */
|
||||||
return 0;
|
state->block++;
|
||||||
if ( ntohs ( udp->dest ) != ival )
|
/* Set EOF marker */
|
||||||
return 0;
|
*eof = ( blksize < state->blksize );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Download a single block via TFTP. This function is non-static so
|
|
||||||
* that pxe_export.c can call it.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int tftp_block ( struct tftpreq_info_t *request,
|
|
||||||
struct tftpblk_info_t *block ) {
|
|
||||||
static struct sockaddr_in server;
|
|
||||||
static unsigned short lport = 2000; /* local port */
|
|
||||||
struct tftp_t *rcvd = NULL;
|
|
||||||
static struct tftpreq_t xmit;
|
|
||||||
static unsigned short xmitlen = 0;
|
|
||||||
static unsigned short blockidx = 0; /* Last block received */
|
|
||||||
static unsigned short retry = 0; /* Retry attempts on last block */
|
|
||||||
static int blksize = 0;
|
|
||||||
unsigned short recvlen = 0;
|
|
||||||
|
|
||||||
/* If this is a new request (i.e. if name is set), fill in
|
|
||||||
* transmit block with RRQ and send it.
|
|
||||||
*/
|
|
||||||
if ( request ) {
|
|
||||||
rx_qdrain(); /* Flush receive queue */
|
|
||||||
xmit.opcode = htons(TFTP_RRQ);
|
|
||||||
xmitlen = (void*)&xmit.u.rrq - (void*)&xmit +
|
|
||||||
sprintf((char*)xmit.u.rrq, "%s%coctet%cblksize%c%d",
|
|
||||||
request->name, 0, 0, 0, request->blksize)
|
|
||||||
+ 1; /* null terminator */
|
|
||||||
blockidx = 0; /* Reset counters */
|
|
||||||
retry = 0;
|
|
||||||
blksize = TFTP_DEFAULTSIZE_PACKET;
|
|
||||||
lport++; /* Use new local port */
|
|
||||||
server = *(request->server);
|
|
||||||
if ( !udp_transmit(server.sin_addr.s_addr, lport,
|
|
||||||
server.sin_port, xmitlen, &xmit) )
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
/* Exit if no transfer in progress */
|
|
||||||
if ( !blksize ) return (0);
|
|
||||||
/* Loop to wait until we get a packet we're interested in */
|
|
||||||
block->data = NULL; /* Used as flag */
|
|
||||||
while ( block->data == NULL ) {
|
|
||||||
long timeout = rfc2131_sleep_interval ( blockidx ? TFTP_REXMT :
|
|
||||||
TIMEOUT, retry );
|
|
||||||
if ( !await_reply(await_tftp, lport, NULL, timeout) ) {
|
|
||||||
/* No packet received */
|
|
||||||
if ( retry++ > MAX_TFTP_RETRIES ) break;
|
|
||||||
/* Retransmit last packet */
|
|
||||||
if ( !blockidx ) lport++; /* New lport if new RRQ */
|
|
||||||
if ( !udp_transmit(server.sin_addr.s_addr, lport,
|
|
||||||
server.sin_port, xmitlen, &xmit) )
|
|
||||||
return (0);
|
|
||||||
continue; /* Back to waiting for packet */
|
|
||||||
}
|
|
||||||
/* Packet has been received */
|
|
||||||
rcvd = (struct tftp_t *)&nic.packet[ETH_HLEN];
|
|
||||||
recvlen = ntohs(rcvd->udp.len) - sizeof(struct udphdr)
|
|
||||||
- sizeof(rcvd->opcode);
|
|
||||||
server.sin_port = ntohs(rcvd->udp.src);
|
|
||||||
retry = 0; /* Reset retry counter */
|
|
||||||
switch ( htons(rcvd->opcode) ) {
|
|
||||||
case TFTP_ERROR : {
|
|
||||||
printf ( "TFTP error %d (%s)\n",
|
|
||||||
ntohs(rcvd->u.err.errcode),
|
|
||||||
rcvd->u.err.errmsg );
|
|
||||||
return (0); /* abort */
|
|
||||||
}
|
|
||||||
case TFTP_OACK : {
|
|
||||||
const char *p = rcvd->u.oack.data;
|
|
||||||
const char *e = p + recvlen - 10; /* "blksize\0\d\0" */
|
|
||||||
|
|
||||||
*((char*)(p+recvlen-1)) = '\0'; /* Force final 0 */
|
|
||||||
if ( blockidx || !request ) break; /* Too late */
|
|
||||||
if ( recvlen <= TFTP_MAX_PACKET ) /* sanity */ {
|
|
||||||
/* Check for blksize option honoured */
|
|
||||||
while ( p < e ) {
|
|
||||||
if ( strcasecmp("blksize",p) == 0 &&
|
|
||||||
p[7] == '\0' ) {
|
|
||||||
blksize = strtoul(p+8,&p,10);
|
|
||||||
p++; /* skip null */
|
|
||||||
}
|
|
||||||
while ( *(p++) ) {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( blksize < TFTP_DEFAULTSIZE_PACKET ||
|
|
||||||
blksize > request->blksize ) {
|
|
||||||
/* Incorrect blksize - error and abort */
|
|
||||||
xmit.opcode = htons(TFTP_ERROR);
|
|
||||||
xmit.u.err.errcode = 8;
|
|
||||||
xmitlen = (void*)&xmit.u.err.errmsg
|
|
||||||
- (void*)&xmit
|
|
||||||
+ sprintf((char*)xmit.u.err.errmsg,
|
|
||||||
"RFC1782 error")
|
|
||||||
+ 1;
|
|
||||||
udp_transmit(server.sin_addr.s_addr, lport,
|
|
||||||
server.sin_port, xmitlen, &xmit);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case TFTP_DATA :
|
|
||||||
if ( ntohs(rcvd->u.data.block) != ( blockidx + 1 ) )
|
|
||||||
break; /* Re-ACK last block sent */
|
|
||||||
if ( recvlen > ( blksize+sizeof(rcvd->u.data.block) ) )
|
|
||||||
break; /* Too large; ignore */
|
|
||||||
block->data = rcvd->u.data.download;
|
|
||||||
block->block = ++blockidx;
|
|
||||||
block->len = recvlen - sizeof(rcvd->u.data.block);
|
|
||||||
block->eof = ( (unsigned short)block->len < blksize );
|
|
||||||
/* If EOF, zero blksize to indicate transfer done */
|
|
||||||
if ( block->eof ) blksize = 0;
|
|
||||||
break;
|
|
||||||
default: break; /* Do nothing */
|
|
||||||
}
|
|
||||||
/* Send ACK */
|
|
||||||
xmit.opcode = htons(TFTP_ACK);
|
|
||||||
xmit.u.ack.block = htons(blockidx);
|
|
||||||
xmitlen = TFTP_MIN_PACKET;
|
|
||||||
udp_transmit ( server.sin_addr.s_addr, lport, server.sin_port,
|
|
||||||
xmitlen, &xmit );
|
|
||||||
}
|
|
||||||
return ( block->data ? 1 : 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Download a file via TFTP
|
* Download a file via TFTP
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
|
int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
|
||||||
struct buffer *buffer ) {
|
struct buffer *buffer ) {
|
||||||
struct tftpreq_info_t request_data = {
|
struct tftp_state state;
|
||||||
.server = server,
|
union tftp_any *reply;
|
||||||
.name = file,
|
int eof = 0;
|
||||||
.blksize = TFTP_MAX_PACKET,
|
|
||||||
};
|
|
||||||
struct tftpreq_info_t *request = &request_data;
|
|
||||||
struct tftpblk_info_t block;
|
|
||||||
off_t offset = 0;
|
|
||||||
|
|
||||||
|
/* Initialise TFTP state */
|
||||||
|
memset ( &state, 0, sizeof ( state ) );
|
||||||
|
state.server = *server;
|
||||||
|
|
||||||
|
/* Open the file */
|
||||||
|
if ( ! tftp_open ( &state, file, &reply ) ) {
|
||||||
|
DBG ( "TFTP: could not open %@:%d/%s : %m\n",
|
||||||
|
server->sin_addr.s_addr, server->sin_port, file );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process OACK, if any */
|
||||||
|
if ( ntohs ( reply->common.opcode ) == TFTP_OACK ) {
|
||||||
|
if ( ! tftp_process_opts ( &state, &reply->oack ) ) {
|
||||||
|
DBG ( "TFTP: option processing failed : %m\n" );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
reply = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fetch file, a block at a time */
|
||||||
do {
|
do {
|
||||||
if ( ! tftp_block ( request, &block ) )
|
/* Get next block to process. (On the first time
|
||||||
return 0;
|
* through, we may already have a block from
|
||||||
if ( ! fill_buffer ( buffer, block.data, offset, block.len ) )
|
* tftp_open()).
|
||||||
|
*/
|
||||||
|
if ( ! reply ) {
|
||||||
|
if ( ! tftp_ack ( &state, &reply ) ) {
|
||||||
|
DBG ( "TFTP: could not get next block: %m\n" );
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
twiddle();
|
twiddle();
|
||||||
offset += block.len;
|
/* Check it's a DATA block */
|
||||||
request = NULL; /* Send request only once */
|
if ( ntohs ( reply->common.opcode ) != TFTP_DATA ) {
|
||||||
} while ( ! block.eof );
|
DBG ( "TFTP: unexpected opcode %d\n",
|
||||||
|
ntohs ( reply->common.opcode ) );
|
||||||
|
errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Process the DATA block */
|
||||||
|
if ( ! process_tftp_data ( &state, &reply->data, buffer,
|
||||||
|
&eof ) )
|
||||||
|
return 0;
|
||||||
|
reply = NULL;
|
||||||
|
} while ( ! eof );
|
||||||
|
|
||||||
|
/* ACK the final packet, as a courtesy to the server */
|
||||||
|
tftp_ack_nowait ( &state );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,21 +47,32 @@ int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
|
||||||
|
|
||||||
/* Must have valid UDP (and, therefore, also IP) headers */
|
/* Must have valid UDP (and, therefore, also IP) headers */
|
||||||
if ( ! udp ) {
|
if ( ! udp ) {
|
||||||
|
DBG2 ( "TFTPCORE: not UDP\n" );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* Packet must come from the TFTP server */
|
/* Packet must come from the TFTP server */
|
||||||
if ( ip->src.s_addr != state->server.sin_addr.s_addr )
|
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;
|
return 0;
|
||||||
|
}
|
||||||
/* Packet must be addressed to the correct UDP port */
|
/* Packet must be addressed to the correct UDP port */
|
||||||
if ( ntohs ( udp->dest ) != state->client.sin_port )
|
if ( ntohs ( udp->dest ) != state->client.sin_port ) {
|
||||||
|
DBG2 ( "TFTPCORE: to UDP port %d, not to TFTP port %d\n",
|
||||||
|
ntohs ( udp->dest ), state->client.sin_port );
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
/* Packet must be addressed to us, or to our multicast
|
/* Packet must be addressed to us, or to our multicast
|
||||||
* listening address (if we have one).
|
* listening address (if we have one).
|
||||||
*/
|
*/
|
||||||
if ( ! ( ( ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr ) ||
|
if ( ! ( ( ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr ) ||
|
||||||
( ( state->client.sin_addr.s_addr ) &&
|
( ( state->client.sin_addr.s_addr ) &&
|
||||||
( ip->dest.s_addr == state->client.sin_addr.s_addr ) ) ) )
|
( ip->dest.s_addr == state->client.sin_addr.s_addr ) ) ) ) {
|
||||||
|
DBG2 ( "TFTPCORE: to %@, not to %@ (or %@)\n",
|
||||||
|
ip->dest.s_addr, arptable[ARP_CLIENT].ipaddr.s_addr,
|
||||||
|
state->client.sin_addr.s_addr );
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +175,7 @@ int tftp_open ( struct tftp_state *state, const char *filename,
|
||||||
state->server.sin_port = TFTP_PORT;
|
state->server.sin_port = TFTP_PORT;
|
||||||
|
|
||||||
/* Determine whether or not to use lport */
|
/* Determine whether or not to use lport */
|
||||||
fixed_lport = state->server.sin_port;
|
fixed_lport = state->client.sin_port;
|
||||||
|
|
||||||
/* Set up RRQ */
|
/* Set up RRQ */
|
||||||
rrq.opcode = htons ( TFTP_RRQ );
|
rrq.opcode = htons ( TFTP_RRQ );
|
||||||
|
@ -202,7 +213,11 @@ int tftp_open ( struct tftp_state *state, const char *filename,
|
||||||
if ( await_reply ( await_tftp, 0, state, timeout ) ) {
|
if ( await_reply ( await_tftp, 0, state, timeout ) ) {
|
||||||
*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
||||||
state->server.sin_port =
|
state->server.sin_port =
|
||||||
ntohs ( (*reply)->common.udp.dest );
|
ntohs ( (*reply)->common.udp.src );
|
||||||
|
DBG ( "TFTPCORE: got reply from %@:%d (type %d)\n",
|
||||||
|
state->server.sin_addr.s_addr,
|
||||||
|
state->server.sin_port,
|
||||||
|
ntohs ( (*reply)->common.opcode ) );
|
||||||
if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
|
if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
|
||||||
tftp_set_errno ( &(*reply)->error );
|
tftp_set_errno ( &(*reply)->error );
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -211,6 +226,7 @@ int tftp_open ( struct tftp_state *state, const char *filename,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBG ( "TFTPCORE: open request timed out\n" );
|
||||||
errno = PXENV_STATUS_TFTP_OPEN_TIMEOUT;
|
errno = PXENV_STATUS_TFTP_OPEN_TIMEOUT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -250,6 +266,8 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
|
||||||
const char *p;
|
const char *p;
|
||||||
const char *end;
|
const char *end;
|
||||||
|
|
||||||
|
DBG ( "TFTPCORE: processing OACK\n" );
|
||||||
|
|
||||||
/* End of options */
|
/* End of options */
|
||||||
end = ( ( char * ) &oack->udp ) + ntohs ( oack->udp.len );
|
end = ( ( char * ) &oack->udp ) + ntohs ( oack->udp.len );
|
||||||
|
|
||||||
|
@ -266,6 +284,7 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
|
DBG ( "TFTPCORE: got blksize %d\n", state->blksize );
|
||||||
} else if ( strcasecmp ( "tsize", p ) == 0 ) {
|
} else if ( strcasecmp ( "tsize", p ) == 0 ) {
|
||||||
p += 6;
|
p += 6;
|
||||||
state->tsize = strtoul ( p, &p, 10 );
|
state->tsize = strtoul ( p, &p, 10 );
|
||||||
|
@ -275,6 +294,7 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
|
DBG ( "TFTPCORE: got tsize %d\n", state->tsize );
|
||||||
} else if ( strcasecmp ( "multicast", p ) == 0 ) {
|
} else if ( strcasecmp ( "multicast", p ) == 0 ) {
|
||||||
char *e = strchr ( p, ',' );
|
char *e = strchr ( p, ',' );
|
||||||
if ( ( ! e ) || ( e >= end ) ) {
|
if ( ( ! e ) || ( e >= end ) ) {
|
||||||
|
@ -317,7 +337,12 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
|
DBG ( "TFTPCORE: got multicast %@:%d (%s)\n",
|
||||||
|
state->client.sin_addr.s_addr,
|
||||||
|
state->client.sin_port,
|
||||||
|
( state->master ? "master" : "not master" ) );
|
||||||
} else {
|
} else {
|
||||||
|
DBG ( "TFTPCORE: unknown option \"%s\"\n", p );
|
||||||
p += strlen ( p ) + 1; /* skip option name */
|
p += strlen ( p ) + 1; /* skip option name */
|
||||||
p += strlen ( p ) + 1; /* skip option value */
|
p += strlen ( p ) + 1; /* skip option value */
|
||||||
}
|
}
|
||||||
|
@ -351,6 +376,7 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
|
||||||
int tftp_ack_nowait ( struct tftp_state *state ) {
|
int tftp_ack_nowait ( struct tftp_state *state ) {
|
||||||
struct tftp_ack ack;
|
struct tftp_ack ack;
|
||||||
|
|
||||||
|
DBG ( "TFTPCORE: acknowledging data block %d\n", state->block );
|
||||||
ack.opcode = htons ( TFTP_ACK );
|
ack.opcode = htons ( TFTP_ACK );
|
||||||
ack.block = htons ( state->block );
|
ack.block = htons ( state->block );
|
||||||
return udp_transmit ( state->server.sin_addr.s_addr,
|
return udp_transmit ( state->server.sin_addr.s_addr,
|
||||||
|
@ -396,9 +422,11 @@ int tftp_ack ( struct tftp_state *state, union tftp_any **reply ) {
|
||||||
DBG ( "TFTP: could not send ACK: %m\n" );
|
DBG ( "TFTP: could not send ACK: %m\n" );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ( await_reply ( await_tftp, 0, &state, timeout ) ) {
|
if ( await_reply ( await_tftp, 0, state, timeout ) ) {
|
||||||
/* We received a reply */
|
/* We received a reply */
|
||||||
*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
*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 ){
|
if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
|
||||||
tftp_set_errno ( &(*reply)->error );
|
tftp_set_errno ( &(*reply)->error );
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -406,7 +434,7 @@ int tftp_ack ( struct tftp_state *state, union tftp_any **reply ) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DBG ( "TFTP: ACK retries exceeded\n" );
|
DBG ( "TFTP: timed out during read\n" );
|
||||||
errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
|
errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue