From 167c26c92b3c831c96b3fe4514cd7bd75507c229 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 30 Apr 2005 14:46:49 +0000 Subject: [PATCH] tftp functions moved to proto/tftp.c --- src/core/nic.c | 160 +------------------------------------------ src/proto/tftp.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 157 deletions(-) create mode 100644 src/proto/tftp.c diff --git a/src/core/nic.c b/src/core/nic.c index 4d492e78..35dbd1db 100644 --- a/src/core/nic.c +++ b/src/core/nic.c @@ -342,7 +342,7 @@ int download_url ( char *url, unparse_url ( &url_info ); /* Call protocol's method to download the file */ - return proto->load ( url, &server, process ); + return proto->load ( url, &server, url_info.file, process ); error_out: unparse_url ( &url_info ); @@ -659,161 +659,6 @@ void rx_qdrain(void) } #ifdef DOWNLOAD_PROTO_TFTP -/************************************************************************** -TFTP - Download extended BOOTP data, or kernel image -**************************************************************************/ -static int await_tftp(int ival, void *ptr __unused, - unsigned short ptype __unused, struct iphdr *ip, struct udphdr *udp, - struct tcphdr *tcp __unused) -{ - if (!udp) { - return 0; - } - if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr) - return 0; - if (ntohs(udp->dest) != ival) - return 0; - return 1; -} - -int tftp ( const char *name, - int (*fnc)(unsigned char *, unsigned int, unsigned int, int) ) -{ - struct tftpreq_info_t request_data = - { name, TFTP_PORT, TFTP_MAX_PACKET }; - struct tftpreq_info_t *request = &request_data; - struct tftpblk_info_t block; - int rc; - - while ( tftp_block ( request, &block ) ) { - request = NULL; /* Send request only once */ - rc = fnc ( block.data, block.block, block.len, block.eof ); - if ( rc <= 0 ) return (rc); - if ( block.eof ) { - /* fnc should not have returned */ - printf ( "TFTP download complete, but\n" ); - return (0); - } - } - return (0); -} - -int tftp_block ( struct tftpreq_info_t *request, struct tftpblk_info_t *block ) -{ - static unsigned short lport = 2000; /* local port */ - static unsigned short rport = TFTP_PORT; /* remote 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 */ - rport = request->port; - if ( !udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, lport, - rport, 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(arptable[ARP_SERVER].ipaddr.s_addr, - lport, rport, 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); - rport = 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( - arptable[ARP_SERVER].ipaddr.s_addr, - lport, rport, 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 ( arptable[ARP_SERVER].ipaddr.s_addr, - lport, rport, xmitlen, &xmit ); - } - return ( block->data ? 1 : 0 ); -} #endif /* DOWNLOAD_PROTO_TFTP */ #ifdef RARP_NOT_BOOTP @@ -1778,7 +1623,8 @@ int decode_rfc1533(unsigned char *p, unsigned int block, unsigned int len, int e memcpy(fname, extpath+2, TAG_LEN(extpath)); fname[(int)TAG_LEN(extpath)] = '\0'; printf("Loading BOOTP-extension file: %s\n",fname); - tftp(fname, decode_rfc1533); +#warning "BOOTP extension files are broken" + /* tftp(fname, decode_rfc1533); */ } return 1; /* proceed with next block */ } diff --git a/src/proto/tftp.c b/src/proto/tftp.c new file mode 100644 index 00000000..9c0e5563 --- /dev/null +++ b/src/proto/tftp.c @@ -0,0 +1,174 @@ +#include "etherboot.h" +#include "in.h" +#include "nic.h" +#include "proto.h" +#include "tftp.h" + +/* Utility function for tftp_block() */ +static int await_tftp ( int ival, void *ptr __unused, + unsigned short ptype __unused, struct iphdr *ip, + struct udphdr *udp, struct tcphdr *tcp __unused ) { + if ( ! udp ) { + return 0; + } + if ( arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr ) + return 0; + if ( ntohs ( udp->dest ) != ival ) + return 0; + 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 ( ! server.sin_port ) + server.sin_port = TFTP_PORT; + 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 + * + */ +int tftp ( char *url __unused, + struct sockaddr_in *server, + char *file, + int ( * process ) ( unsigned char *data, + unsigned int blocknum, + unsigned int len, int eof ) ) { + struct tftpreq_info_t request_data = { + .server = server, + .name = file, + .blksize = TFTP_MAX_PACKET, + }; + struct tftpreq_info_t *request = &request_data; + struct tftpblk_info_t block; + int rc; + + while ( tftp_block ( request, &block ) ) { + request = NULL; /* Send request only once */ + rc = process ( block.data, block.block, block.len, block.eof ); + if ( rc <= 0 ) return ( rc ); + } + return 0; +} + +struct protocol tftp_protocol __default_protocol = { + "tftp", tftp +};