Moved protocols to proto/

This commit is contained in:
Michael Brown
2005-05-01 14:04:11 +00:00
parent 0cfcd91558
commit 85d9eae44e
4 changed files with 0 additions and 0 deletions

View File

@@ -1,610 +0,0 @@
#ifdef DOWNLOAD_PROTO_NFS
#include "etherboot.h"
#include "nic.h"
/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
* large portions are copied verbatim) as distributed in OSKit 0.97. A few
* changes were necessary to adapt the code to Etherboot and to fix several
* inconsistencies. Also the RPC message preparation is done "by hand" to
* avoid adding netsprintf() which I find hard to understand and use. */
/* NOTE 2: Etherboot does not care about things beyond the kernel image, so
* it loads the kernel image off the boot server (ARP_SERVER) and does not
* access the client root disk (root-path in dhcpd.conf), which would use
* ARP_ROOTSERVER. The root disk is something the operating system we are
* about to load needs to use. This is different from the OSKit 0.97 logic. */
/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
* If a symlink is encountered, it is followed as far as possible (recursion
* possible, maximum 16 steps). There is no clearing of ".."'s inside the
* path, so please DON'T DO THAT. thx. */
#define START_OPORT 700 /* mountd usually insists on secure ports */
#define OPORT_SWEEP 200 /* make sure we don't leave secure range */
static int oport = START_OPORT;
static int mount_port = -1;
static int nfs_port = -1;
static int fs_mounted = 0;
static unsigned long rpc_id;
/**************************************************************************
RPC_INIT - set up the ID counter to something fairly random
**************************************************************************/
void rpc_init(void)
{
unsigned long t;
t = currticks();
rpc_id = t ^ (t << 8) ^ (t << 16);
}
/**************************************************************************
RPC_PRINTERROR - Print a low level RPC error message
**************************************************************************/
static void rpc_printerror(struct rpc_t *rpc)
{
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
rpc->u.reply.astatus) {
/* rpc_printerror() is called for any RPC related error,
* suppress output if no low level RPC error happened. */
printf("RPC error: (%d,%d,%d)\n", ntohl(rpc->u.reply.rstatus),
ntohl(rpc->u.reply.verifier),
ntohl(rpc->u.reply.astatus));
}
}
/**************************************************************************
AWAIT_RPC - Wait for an rpc packet
**************************************************************************/
static int await_rpc(int ival, void *ptr,
unsigned short ptype, struct iphdr *ip, struct udphdr *udp)
{
struct rpc_t *rpc;
if (!udp)
return 0;
if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
return 0;
if (ntohs(udp->dest) != ival)
return 0;
if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
return 0;
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
return 0;
if (MSG_REPLY != ntohl(rpc->u.reply.type))
return 0;
return 1;
}
/**************************************************************************
RPC_LOOKUP - Lookup RPC Port numbers
**************************************************************************/
static int rpc_lookup(int addr, int prog, int ver, int sport)
{
struct rpc_t buf, *rpc;
unsigned long id;
int retries;
long *p;
id = rpc_id++;
buf.u.call.id = htonl(id);
buf.u.call.type = htonl(MSG_CALL);
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
buf.u.call.prog = htonl(PROG_PORTMAP);
buf.u.call.vers = htonl(2); /* portmapper is version 2 */
buf.u.call.proc = htonl(PORTMAP_GETPORT);
p = (long *)buf.u.call.data;
*p++ = 0; *p++ = 0; /* auth credential */
*p++ = 0; *p++ = 0; /* auth verifier */
*p++ = htonl(prog);
*p++ = htonl(ver);
*p++ = htonl(IP_UDP);
*p++ = 0;
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
long timeout;
udp_transmit(arptable[addr].ipaddr.s_addr, sport, SUNRPC_PORT,
(char *)p - (char *)&buf, &buf);
timeout = rfc2131_sleep_interval(TIMEOUT, retries);
if (await_reply(await_rpc, sport, &id, timeout)) {
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
rpc->u.reply.astatus) {
rpc_printerror(rpc);
return -1;
} else {
return ntohl(rpc->u.reply.data[0]);
}
}
}
return -1;
}
/**************************************************************************
RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
**************************************************************************/
static long *rpc_add_credentials(long *p)
{
int hl;
/* Here's the executive summary on authentication requirements of the
* various NFS server implementations: Linux accepts both AUTH_NONE
* and AUTH_UNIX authentication (also accepts an empty hostname field
* in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
* AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
* scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
* it (if the BOOTP/DHCP reply didn't give one, just use an empty
* hostname). */
hl = (hostnamelen + 3) & ~3;
/* Provide an AUTH_UNIX credential. */
*p++ = htonl(1); /* AUTH_UNIX */
*p++ = htonl(hl+20); /* auth length */
*p++ = htonl(0); /* stamp */
*p++ = htonl(hostnamelen); /* hostname string */
if (hostnamelen & 3) {
*(p + hostnamelen / 4) = 0; /* add zero padding */
}
memcpy(p, hostname, hostnamelen);
p += hl / 4;
*p++ = 0; /* uid */
*p++ = 0; /* gid */
*p++ = 0; /* auxiliary gid list */
/* Provide an AUTH_NONE verifier. */
*p++ = 0; /* AUTH_NONE */
*p++ = 0; /* auth length */
return p;
}
/**************************************************************************
NFS_PRINTERROR - Print a NFS error message
**************************************************************************/
static void nfs_printerror(int err)
{
switch (-err) {
case NFSERR_PERM:
printf("Not owner\n");
break;
case NFSERR_NOENT:
printf("No such file or directory\n");
break;
case NFSERR_ACCES:
printf("Permission denied\n");
break;
case NFSERR_ISDIR:
printf("Directory given where filename expected\n");
break;
case NFSERR_INVAL:
printf("Invalid filehandle\n");
break; // INVAL is not defined in NFSv2, some NFS-servers
// seem to use it in answers to v2 nevertheless.
case 9998:
printf("low-level RPC failure (parameter decoding problem?)\n");
break;
case 9999:
printf("low-level RPC failure (authentication problem?)\n");
break;
default:
printf("Unknown NFS error %d\n", -err);
}
}
/**************************************************************************
NFS_MOUNT - Mount an NFS Filesystem
**************************************************************************/
static int nfs_mount(int server, int port, char *path, char *fh, int sport)
{
struct rpc_t buf, *rpc;
unsigned long id;
int retries;
long *p;
int pathlen = strlen(path);
id = rpc_id++;
buf.u.call.id = htonl(id);
buf.u.call.type = htonl(MSG_CALL);
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
buf.u.call.prog = htonl(PROG_MOUNT);
buf.u.call.vers = htonl(1); /* mountd is version 1 */
buf.u.call.proc = htonl(MOUNT_ADDENTRY);
p = rpc_add_credentials((long *)buf.u.call.data);
*p++ = htonl(pathlen);
if (pathlen & 3) {
*(p + pathlen / 4) = 0; /* add zero padding */
}
memcpy(p, path, pathlen);
p += (pathlen + 3) / 4;
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
long timeout;
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
(char *)p - (char *)&buf, &buf);
timeout = rfc2131_sleep_interval(TIMEOUT, retries);
if (await_reply(await_rpc, sport, &id, timeout)) {
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
rpc_printerror(rpc);
if (rpc->u.reply.rstatus) {
/* RPC failed, no verifier, data[0] */
return -9999;
}
if (rpc->u.reply.astatus) {
/* RPC couldn't decode parameters */
return -9998;
}
return -ntohl(rpc->u.reply.data[0]);
} else {
fs_mounted = 1;
memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
return 0;
}
}
}
return -1;
}
/**************************************************************************
NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
**************************************************************************/
void nfs_umountall(int server)
{
struct rpc_t buf, *rpc;
unsigned long id;
int retries;
long *p;
if (!arptable[server].ipaddr.s_addr) {
/* Haven't sent a single UDP packet to this server */
return;
}
if ((mount_port == -1) || (!fs_mounted)) {
/* Nothing mounted, nothing to umount */
return;
}
id = rpc_id++;
buf.u.call.id = htonl(id);
buf.u.call.type = htonl(MSG_CALL);
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
buf.u.call.prog = htonl(PROG_MOUNT);
buf.u.call.vers = htonl(1); /* mountd is version 1 */
buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
p = rpc_add_credentials((long *)buf.u.call.data);
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
udp_transmit(arptable[server].ipaddr.s_addr, oport, mount_port,
(char *)p - (char *)&buf, &buf);
if (await_reply(await_rpc, oport, &id, timeout)) {
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
rpc->u.reply.astatus) {
rpc_printerror(rpc);
}
fs_mounted = 0;
return;
}
}
}
/***************************************************************************
* NFS_READLINK (AH 2003-07-14)
* This procedure is called when read of the first block fails -
* this probably happens when it's a directory or a symlink
* In case of successful readlink(), the dirname is manipulated,
* so that inside the nfs() function a recursion can be done.
**************************************************************************/
static int nfs_readlink(int server, int port, char *fh, char *path, char *nfh,
int sport)
{
struct rpc_t buf, *rpc;
unsigned long id;
long *p;
int retries;
int pathlen = strlen(path);
id = rpc_id++;
buf.u.call.id = htonl(id);
buf.u.call.type = htonl(MSG_CALL);
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
buf.u.call.prog = htonl(PROG_NFS);
buf.u.call.vers = htonl(2); /* nfsd is version 2 */
buf.u.call.proc = htonl(NFS_READLINK);
p = rpc_add_credentials((long *)buf.u.call.data);
memcpy(p, nfh, NFS_FHSIZE);
p += (NFS_FHSIZE / 4);
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
(char *)p - (char *)&buf, &buf);
if (await_reply(await_rpc, sport, &id, timeout)) {
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
rpc_printerror(rpc);
if (rpc->u.reply.rstatus) {
/* RPC failed, no verifier, data[0] */
return -9999;
}
if (rpc->u.reply.astatus) {
/* RPC couldn't decode parameters */
return -9998;
}
return -ntohl(rpc->u.reply.data[0]);
} else {
// It *is* a link.
// If it's a relative link, append everything to dirname, filename TOO!
retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
path[pathlen++] = '/';
while ( ( retries + pathlen ) > 298 ) {
retries--;
}
if ( retries > 0 ) {
memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
} else { retries = 0; }
path[pathlen + retries] = 0;
} else {
// Else make it the only path.
if ( retries > 298 ) { retries = 298; }
memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
path[retries] = 0;
}
return 0;
}
}
}
return -1;
}
/**************************************************************************
NFS_LOOKUP - Lookup Pathname
**************************************************************************/
static int nfs_lookup(int server, int port, char *fh, char *path, char *nfh,
int sport)
{
struct rpc_t buf, *rpc;
unsigned long id;
long *p;
int retries;
int pathlen = strlen(path);
id = rpc_id++;
buf.u.call.id = htonl(id);
buf.u.call.type = htonl(MSG_CALL);
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
buf.u.call.prog = htonl(PROG_NFS);
buf.u.call.vers = htonl(2); /* nfsd is version 2 */
buf.u.call.proc = htonl(NFS_LOOKUP);
p = rpc_add_credentials((long *)buf.u.call.data);
memcpy(p, fh, NFS_FHSIZE);
p += (NFS_FHSIZE / 4);
*p++ = htonl(pathlen);
if (pathlen & 3) {
*(p + pathlen / 4) = 0; /* add zero padding */
}
memcpy(p, path, pathlen);
p += (pathlen + 3) / 4;
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
(char *)p - (char *)&buf, &buf);
if (await_reply(await_rpc, sport, &id, timeout)) {
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
rpc_printerror(rpc);
if (rpc->u.reply.rstatus) {
/* RPC failed, no verifier, data[0] */
return -9999;
}
if (rpc->u.reply.astatus) {
/* RPC couldn't decode parameters */
return -9998;
}
return -ntohl(rpc->u.reply.data[0]);
} else {
memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
return 0;
}
}
}
return -1;
}
/**************************************************************************
NFS_READ - Read File on NFS Server
**************************************************************************/
static int nfs_read(int server, int port, char *fh, int offset, int len,
int sport)
{
struct rpc_t buf, *rpc;
unsigned long id;
int retries;
long *p;
static int tokens=0;
/*
* Try to implement something similar to a window protocol in
* terms of response to losses. On successful receive, increment
* the number of tokens by 1 (cap at 256). On failure, halve it.
* When the number of tokens is >= 2, use a very short timeout.
*/
id = rpc_id++;
buf.u.call.id = htonl(id);
buf.u.call.type = htonl(MSG_CALL);
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
buf.u.call.prog = htonl(PROG_NFS);
buf.u.call.vers = htonl(2); /* nfsd is version 2 */
buf.u.call.proc = htonl(NFS_READ);
p = rpc_add_credentials((long *)buf.u.call.data);
memcpy(p, fh, NFS_FHSIZE);
p += NFS_FHSIZE / 4;
*p++ = htonl(offset);
*p++ = htonl(len);
*p++ = 0; /* unused parameter */
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
if (tokens >= 2)
timeout = TICKS_PER_SEC/2;
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
(char *)p - (char *)&buf, &buf);
if (await_reply(await_rpc, sport, &id, timeout)) {
if (tokens < 256)
tokens++;
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
rpc_printerror(rpc);
if (rpc->u.reply.rstatus) {
/* RPC failed, no verifier, data[0] */
return -9999;
}
if (rpc->u.reply.astatus) {
/* RPC couldn't decode parameters */
return -9998;
}
return -ntohl(rpc->u.reply.data[0]);
} else {
return 0;
}
} else
tokens >>= 1;
}
return -1;
}
/**************************************************************************
NFS - Download extended BOOTP data, or kernel image from NFS server
**************************************************************************/
int nfs(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
{
static int recursion = 0;
int sport;
int err, namelen = strlen(name);
char dirname[300], *fname;
char dirfh[NFS_FHSIZE]; /* file handle of directory */
char filefh[NFS_FHSIZE]; /* file handle of kernel image */
unsigned int block;
int rlen, size, offs, len;
struct rpc_t *rpc;
rx_qdrain();
sport = oport++;
if (oport > START_OPORT+OPORT_SWEEP) {
oport = START_OPORT;
}
if ( name != dirname ) {
memcpy(dirname, name, namelen + 1);
}
recursion = 0;
nfssymlink:
if ( recursion > NFS_MAXLINKDEPTH ) {
printf ( "\nRecursion: More than %d symlinks followed. Abort.\n", NFS_MAXLINKDEPTH );
return 0;
}
recursion++;
fname = dirname + (namelen - 1);
while (fname >= dirname) {
if (*fname == '/') {
*fname = '\0';
fname++;
break;
}
fname--;
}
if (fname < dirname) {
printf("can't parse file name %s\n", name);
return 0;
}
if (mount_port == -1) {
mount_port = rpc_lookup(ARP_SERVER, PROG_MOUNT, 1, sport);
}
if (nfs_port == -1) {
nfs_port = rpc_lookup(ARP_SERVER, PROG_NFS, 2, sport);
}
if (nfs_port == -1 || mount_port == -1) {
printf("can't get nfs/mount ports from portmapper\n");
return 0;
}
err = nfs_mount(ARP_SERVER, mount_port, dirname, dirfh, sport);
if (err) {
printf("mounting %s: ", dirname);
nfs_printerror(err);
/* just to be sure... */
nfs_umountall(ARP_SERVER);
return 0;
}
err = nfs_lookup(ARP_SERVER, nfs_port, dirfh, fname, filefh, sport);
if (err) {
printf("looking up %s: ", fname);
nfs_printerror(err);
nfs_umountall(ARP_SERVER);
return 0;
}
offs = 0;
block = 1; /* blocks are numbered starting from 1 */
size = -1; /* will be set properly with the first reply */
len = NFS_READ_SIZE; /* first request is always full size */
do {
err = nfs_read(ARP_SERVER, nfs_port, filefh, offs, len, sport);
if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
// An error occured. NFS servers tend to sending
// errors 21 / 22 when symlink instead of real file
// is requested. So check if it's a symlink!
block = nfs_readlink(ARP_SERVER, nfs_port, dirfh, dirname,
filefh, sport);
if ( 0 == block ) {
printf("\nLoading symlink:%s ..",dirname);
goto nfssymlink;
}
nfs_printerror(err);
nfs_umountall(ARP_SERVER);
return 0;
}
if (err) {
printf("reading at offset %d: ", offs);
nfs_printerror(err);
nfs_umountall(ARP_SERVER);
return 0;
}
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
/* size must be found out early to allow EOF detection */
if (size == -1) {
size = ntohl(rpc->u.reply.data[6]);
}
rlen = ntohl(rpc->u.reply.data[18]);
if (rlen > len) {
rlen = len; /* shouldn't happen... */
}
err = fnc((char *)&rpc->u.reply.data[19], block, rlen,
(offs+rlen == size));
if (err <= 0) {
nfs_umountall(ARP_SERVER);
return err;
}
block++;
offs += rlen;
/* last request is done with matching requested read size */
if (size-offs < NFS_READ_SIZE) {
len = size-offs;
}
} while (len != 0);
/* len == 0 means that all the file has been read */
return 1;
}
#endif /* DOWNLOAD_PROTO_NFS */

View File

@@ -1,206 +0,0 @@
#include "etherboot.h"
#include "http.h"
#ifdef DOWNLOAD_PROTO_HTTP
/* The block size is currently chosen to be 512 bytes. This means, we can
allocate the receive buffer on the stack, but it results in a noticeable
performance penalty.
This is what needs to be done in order to increase the block size:
- size negotiation needs to be implemented in TCP
- the buffer needs to be allocated on the heap
- path MTU discovery needs to be implemented
*/ /***/ /* FIXME */
#define BLOCKSIZE TFTP_DEFAULTSIZE_PACKET
/**************************************************************************
SEND_TCP_CALLBACK - Send data using TCP
**************************************************************************/
struct send_recv_state {
int (*fnc)(unsigned char *data, int block, int len, int eof);
char *send_buffer;
char *recv_buffer;
int send_length;
int recv_length;
int bytes_sent;
int block;
int bytes_received;
enum { RESULT_CODE, HEADER, DATA, ERROR, MOVED } recv_state;
int rc;
char location[MAX_URL+1];
};
static int send_tcp_request(int length, void *buffer, void *ptr) {
struct send_recv_state *state = (struct send_recv_state *)ptr;
if (length > state->send_length - state->bytes_sent)
length = state->send_length - state->bytes_sent;
memcpy(buffer, state->send_buffer + state->bytes_sent, length);
state->bytes_sent += length;
return (length);
}
/**************************************************************************
RECV_TCP_CALLBACK - Receive data using TCP
**************************************************************************/
static int recv_tcp_request(int length, const void *buffer, void *ptr) {
struct send_recv_state *state = (struct send_recv_state *)ptr;
/* Assume that the lines in an HTTP header do not straddle a packet */
/* boundary. This is probably a reasonable assumption */
if (state->recv_state == RESULT_CODE) {
while (length > 0) {
/* Find HTTP result code */
if (*(const char *)buffer == ' ') {
const char *ptr = ((const char *)buffer) + 1;
int rc = strtoul(ptr, &ptr, 10);
if (ptr >= (const char *)buffer + length) {
state->recv_state = ERROR;
return 0;
}
state->rc = rc;
state->recv_state = HEADER;
goto header;
}
++(const char *)buffer;
length--;
}
state->recv_state = ERROR;
return 0;
}
if (state->recv_state == HEADER) {
header: while (length > 0) {
/* Check for HTTP redirect */
if (state->rc >= 300 && state->rc < 400 &&
!memcmp(buffer, "Location: ", 10)) {
char *ptr = state->location;
int i;
memcpy(ptr, buffer + 10, MAX_URL);
for (i = 0; i < MAX_URL && *ptr > ' ';
i++, ptr++);
*ptr = '\000';
state->recv_state = MOVED;
return 1;
}
/* Find beginning of line */
while (length > 0) {
length--;
if (*((const char *)buffer)++ == '\n')
break;
}
/* Check for end of header */
if (length >= 2 && !memcmp(buffer, "\r\n", 2)) {
state->recv_state = DATA;
buffer += 2;
length -= 2;
break;
}
}
}
if (state->recv_state == DATA) {
state->bytes_received += length;
while (length > 0) {
int copy_length = BLOCKSIZE - state->recv_length;
if (copy_length > length)
copy_length = length;
memcpy(state->recv_buffer + state->recv_length,
buffer, copy_length);
if ((state->recv_length += copy_length) == BLOCKSIZE) {
if (!state->fnc(state->recv_buffer,
++state->block, BLOCKSIZE, 0))
return 0;
state->recv_length = 0;
}
length -= copy_length;
buffer += copy_length;
}
}
return 1;
}
/**************************************************************************
HTTP_GET - Get data using HTTP
**************************************************************************/
int http(const char *url,
int (*fnc)(unsigned char *, unsigned int, unsigned int, int)) {
static const char GET[] = "GET /%s HTTP/1.0\r\n\r\n";
static char recv_buffer[BLOCKSIZE];
in_addr destip;
int port;
int length;
struct send_recv_state state;
state.fnc = fnc;
state.rc = -1;
state.block = 0;
state.recv_buffer = recv_buffer;
length = strlen(url);
if (length <= MAX_URL) {
memcpy(state.location, url, length+1);
destip = arptable[ARP_SERVER].ipaddr;
port = url_port;
if (port == -1)
port = 80;
goto first_time;
do {
state.rc = -1;
state.block = 0;
url = state.location;
if (memcmp("http://", url, 7))
break;
url += 7;
length = inet_aton(url, &destip);
if (!length) {
/* As we do not have support for DNS, assume*/
/* that HTTP redirects always point to the */
/* same machine */
if (state.recv_state == MOVED) {
while (*url &&
*url != ':' && *url != '/') url++;
} else {
break;
}
}
if (*(url += length) == ':') {
port = strtoul(url, &url, 10);
} else {
port = 80;
}
if (!*url)
url = "/";
if (*url != '/')
break;
url++;
first_time:
length = strlen(url);
state.send_length = sizeof(GET) - 3 + length;
{ char buf[state.send_length + 1];
sprintf(state.send_buffer = buf, GET, url);
state.bytes_sent = 0;
state.bytes_received = 0;
state.recv_state = RESULT_CODE;
state.recv_length = 0;
tcp_transaction(destip.s_addr, 80, &state,
send_tcp_request, recv_tcp_request);
}
} while (state.recv_state == MOVED);
} else {
memcpy(state.location, url, MAX_URL);
state.location[MAX_URL] = '\000';
}
if (state.rc == 200) {
return fnc(recv_buffer, ++state.block, state.recv_length, 1);
} else {
printf("Failed to download %s (rc = %d)\n",
state.location, state.rc);
return 0;
}
}
#endif /* DOWNLOAD_PROTO_HTTP */

View File

@@ -1,541 +0,0 @@
#ifdef DOWNLOAD_PROTO_SLAM
#include "etherboot.h"
#include "nic.h"
#define SLAM_PORT 10000
#define SLAM_MULTICAST_IP ((239<<24)|(255<<16)|(1<<8)|(1<<0))
#define SLAM_MULTICAST_PORT 10000
#define SLAM_LOCAL_PORT 10000
/* Set the timeout intervals to at least 1 second so
* on a 100Mbit ethernet can receive 10000 packets
* in one second.
*
* The only case that is likely to trigger all of the nodes
* firing a nack packet is a slow server. The odds of this
* happening could be reduced being slightly smarter and utilizing
* the multicast channels for nacks. But that only improves the odds
* it doesn't improve the worst case. So unless this proves to be
* a common case having the control data going unicast should increase
* the odds of the data not being dropped.
*
* When doing exponential backoff we increase just the timeout
* interval and not the base to optimize for throughput. This is only
* expected to happen when the server is down. So having some nodes
* pinging immediately should get the transmission restarted quickly after a
* server restart. The host nic won't be to baddly swamped because of
* the random distribution of the nodes.
*
*/
#define SLAM_INITIAL_MIN_TIMEOUT (TICKS_PER_SEC/3)
#define SLAM_INITIAL_TIMEOUT_INTERVAL (TICKS_PER_SEC)
#define SLAM_BASE_MIN_TIMEOUT (2*TICKS_PER_SEC)
#define SLAM_BASE_TIMEOUT_INTERVAL (4*TICKS_PER_SEC)
#define SLAM_BACKOFF_LIMIT 5
#define SLAM_MAX_RETRIES 20
/*** Packets Formats ***
* Data Packet:
* transaction
* total bytes
* block size
* packet #
* data
*
* Status Request Packet
* transaction
* total bytes
* block size
*
* Status Packet
* received packets
* requested packets
* received packets
* requested packets
* ...
* received packets
* requested packtes
* 0
*/
#define MAX_HDR (7 + 7 + 7) /* transaction, total size, block size */
#define MIN_HDR (1 + 1 + 1) /* transactino, total size, block size */
#define MAX_SLAM_REQUEST MAX_HDR
#define MIN_SLAM_REQUEST MIN_HDR
#define MIN_SLAM_DATA (MIN_HDR + 1)
static struct slam_nack {
struct iphdr ip;
struct udphdr udp;
unsigned char data[ETH_MAX_MTU -
(sizeof(struct iphdr) + sizeof(struct udphdr))];
} nack;
struct slam_state {
unsigned char hdr[MAX_HDR];
unsigned long hdr_len;
unsigned long block_size;
unsigned long total_bytes;
unsigned long total_packets;
unsigned long received_packets;
unsigned char *image;
unsigned char *bitmap;
} state;
static void init_slam_state(void)
{
state.hdr_len = sizeof(state.hdr);
memset(state.hdr, 0, state.hdr_len);
state.block_size = 0;
state.total_packets = 0;
state.received_packets = 0;
state.image = 0;
state.bitmap = 0;
}
struct slam_info {
in_addr server_ip;
in_addr multicast_ip;
in_addr local_ip;
uint16_t server_port;
uint16_t multicast_port;
uint16_t local_port;
int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
int sent_nack;
};
#define SLAM_TIMEOUT 0
#define SLAM_REQUEST 1
#define SLAM_DATA 2
static int await_slam(int ival __unused, void *ptr,
unsigned short ptype __unused, struct iphdr *ip, struct udphdr *udp)
{
struct slam_info *info = ptr;
if (!udp) {
return 0;
}
/* I can receive two kinds of packets here, a multicast data packet,
* or a unicast request for information
*/
/* Check for a data request packet */
if ((ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) &&
(ntohs(udp->dest) == info->local_port) &&
(nic.packetlen >=
ETH_HLEN +
sizeof(struct iphdr) +
sizeof(struct udphdr) +
MIN_SLAM_REQUEST)) {
return SLAM_REQUEST;
}
/* Check for a multicast data packet */
if ((ip->dest.s_addr == info->multicast_ip.s_addr) &&
(ntohs(udp->dest) == info->multicast_port) &&
(nic.packetlen >=
ETH_HLEN +
sizeof(struct iphdr) +
sizeof(struct udphdr) +
MIN_SLAM_DATA)) {
return SLAM_DATA;
}
#if 0
printf("#");
printf("dest: %@ port: %d len: %d\n",
ip->dest.s_addr, ntohs(udp->dest), nic.packetlen);
#endif
return 0;
}
static int slam_encode(
unsigned char **ptr, unsigned char *end, unsigned long value)
{
unsigned char *data = *ptr;
int bytes;
bytes = sizeof(value);
while ((bytes > 0) && ((0xff & (value >> ((bytes -1)<<3))) == 0)) {
bytes--;
}
if (bytes <= 0) {
bytes = 1;
}
if (data + bytes >= end) {
return -1;
}
if ((0xe0 & (value >> ((bytes -1)<<3))) == 0) {
/* packed together */
*data = (bytes << 5) | (value >> ((bytes -1)<<3));
} else {
bytes++;
*data = (bytes << 5);
}
bytes--;
data++;
while(bytes) {
*(data++) = 0xff & (value >> ((bytes -1)<<3));
bytes--;
}
*ptr = data;
return 0;
}
static int slam_skip(unsigned char **ptr, unsigned char *end)
{
int bytes;
if (*ptr >= end) {
return -1;
}
bytes = ((**ptr) >> 5) & 7;
if (bytes == 0) {
return -1;
}
if (*ptr + bytes >= end) {
return -1;
}
(*ptr) += bytes;
return 0;
}
static unsigned long slam_decode(unsigned char **ptr, unsigned char *end, int *err)
{
unsigned long value;
unsigned bytes;
if (*ptr >= end) {
*err = -1;
}
bytes = ((**ptr) >> 5) & 7;
if ((bytes == 0) || (bytes > sizeof(unsigned long))) {
*err = -1;
return 0;
}
if ((*ptr) + bytes >= end) {
*err = -1;
}
value = (**ptr) & 0x1f;
bytes--;
(*ptr)++;
while(bytes) {
value <<= 8;
value |= **ptr;
(*ptr)++;
bytes--;
}
return value;
}
static long slam_sleep_interval(int exp)
{
long range;
long divisor;
long interval;
range = SLAM_BASE_TIMEOUT_INTERVAL;
if (exp < 0) {
divisor = RAND_MAX/SLAM_INITIAL_TIMEOUT_INTERVAL;
} else {
if (exp > SLAM_BACKOFF_LIMIT)
exp = SLAM_BACKOFF_LIMIT;
divisor = RAND_MAX/(range << exp);
}
interval = random()/divisor;
if (exp < 0) {
interval += SLAM_INITIAL_MIN_TIMEOUT;
} else {
interval += SLAM_BASE_MIN_TIMEOUT;
}
return interval;
}
static unsigned char *reinit_slam_state(
unsigned char *header, unsigned char *end)
{
unsigned long total_bytes;
unsigned long block_size;
unsigned long bitmap_len;
unsigned long max_packet_len;
unsigned char *data;
int err;
#if 0
printf("reinit\n");
#endif
data = header;
state.hdr_len = 0;
err = slam_skip(&data, end); /* transaction id */
total_bytes = slam_decode(&data, end, &err);
block_size = slam_decode(&data, end, &err);
if (err) {
printf("ALERT: slam size out of range\n");
return 0;
}
state.block_size = block_size;
state.total_bytes = total_bytes;
state.total_packets = (total_bytes + block_size - 1)/block_size;
state.hdr_len = data - header;
state.received_packets = 0;
data = state.hdr;
slam_encode(&data, &state.hdr[sizeof(state.hdr)], state.total_packets);
max_packet_len = data - state.hdr;
memcpy(state.hdr, header, state.hdr_len);
#if 0
printf("block_size: %ld\n", block_size);
printf("total_bytes: %ld\n", total_bytes);
printf("total_packets: %ld\n", state.total_packets);
printf("hdr_len: %ld\n", state.hdr_len);
printf("max_packet_len: %ld\n", max_packet_len);
#endif
if (state.block_size > ETH_MAX_MTU - (
sizeof(struct iphdr) + sizeof(struct udphdr) +
state.hdr_len + max_packet_len)) {
printf("ALERT: slam blocksize to large\n");
return 0;
}
if (state.bitmap) {
forget(state.bitmap);
}
bitmap_len = (state.total_packets + 1 + 7)/8;
state.bitmap = allot(bitmap_len);
state.image = allot(total_bytes);
if ((unsigned long)state.image < 1024*1024) {
printf("ALERT: slam filesize to large for available memory\n");
return 0;
}
memset(state.bitmap, 0, bitmap_len);
return header + state.hdr_len;
}
static int slam_recv_data(unsigned char *data)
{
unsigned long packet;
unsigned long data_len;
int err;
struct udphdr *udp;
udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)];
err = 0;
packet = slam_decode(&data, &nic.packet[nic.packetlen], &err);
if (err || (packet > state.total_packets)) {
printf("ALERT: Invalid packet number\n");
return 0;
}
/* Compute the expected data length */
if (packet != state.total_packets -1) {
data_len = state.block_size;
} else {
data_len = state.total_bytes % state.block_size;
}
/* If the packet size is wrong drop the packet and then continue */
if (ntohs(udp->len) != (data_len + (data - (unsigned char*)udp))) {
printf("ALERT: udp packet is not the correct size\n");
return 1;
}
if (nic.packetlen < data_len + (data - nic.packet)) {
printf("ALERT: Ethernet packet shorter than data_len\n");
return 1;
}
if (data_len > state.block_size) {
data_len = state.block_size;
}
if (((state.bitmap[packet >> 3] >> (packet & 7)) & 1) == 0) {
/* Non duplicate packet */
state.bitmap[packet >> 3] |= (1 << (packet & 7));
memcpy(state.image + (packet*state.block_size), data, data_len);
state.received_packets++;
} else {
#ifdef MDEBUG
printf("<DUP>\n");
#endif
}
return 1;
}
static void transmit_nack(unsigned char *ptr, struct slam_info *info)
{
int nack_len;
/* Ensure the packet is null terminated */
*ptr++ = 0;
nack_len = ptr - (unsigned char *)&nack;
build_udp_hdr(info->server_ip.s_addr,
info->local_port, info->server_port, 1, nack_len, &nack);
ip_transmit(nack_len, &nack);
#if defined(MDEBUG) && 0
printf("Sent NACK to %@ bytes: %d have:%ld/%ld\n",
info->server_ip, nack_len,
state.received_packets, state.total_packets);
#endif
}
static void slam_send_nack(struct slam_info *info)
{
unsigned char *ptr, *end;
/* Either I timed out or I was explicitly
* asked for a request packet
*/
ptr = &nack.data[0];
/* Reserve space for the trailling null */
end = &nack.data[sizeof(nack.data) -1];
if (!state.bitmap) {
slam_encode(&ptr, end, 0);
slam_encode(&ptr, end, 1);
}
else {
/* Walk the bitmap */
unsigned long i;
unsigned long len;
unsigned long max;
int value;
int last;
/* Compute the last bit and store an inverted trailer */
max = state.total_packets;
value = ((state.bitmap[(max -1) >> 3] >> ((max -1) & 7) ) & 1);
value = !value;
state.bitmap[max >> 3] &= ~(1 << (max & 7));
state.bitmap[max >> 3] |= value << (max & 7);
len = 0;
last = 1; /* Start with the received packets */
for(i = 0; i <= max; i++) {
value = (state.bitmap[i>>3] >> (i & 7)) & 1;
if (value == last) {
len++;
} else {
if (slam_encode(&ptr, end, len))
break;
last = value;
len = 1;
}
}
}
info->sent_nack = 1;
transmit_nack(ptr, info);
}
static void slam_send_disconnect(struct slam_info *info)
{
if (info->sent_nack) {
/* A disconnect is a packet with just the null terminator */
transmit_nack(&nack.data[0], info);
}
info->sent_nack = 0;
}
static int proto_slam(struct slam_info *info)
{
int retry;
long timeout;
init_slam_state();
retry = -1;
rx_qdrain();
/* Arp for my server */
if (arptable[ARP_SERVER].ipaddr.s_addr != info->server_ip.s_addr) {
arptable[ARP_SERVER].ipaddr.s_addr = info->server_ip.s_addr;
memset(arptable[ARP_SERVER].node, 0, ETH_ALEN);
}
/* If I'm running over multicast join the multicast group */
join_group(IGMP_SERVER, info->multicast_ip.s_addr);
for(;;) {
unsigned char *header;
unsigned char *data;
int type;
header = data = 0;
timeout = slam_sleep_interval(retry);
type = await_reply(await_slam, 0, info, timeout);
/* Compute the timeout for next time */
if (type == SLAM_TIMEOUT) {
/* If I timeouted recompute the next timeout */
if (retry++ > SLAM_MAX_RETRIES) {
return 0;
}
} else {
retry = 0;
}
if ((type == SLAM_DATA) || (type == SLAM_REQUEST)) {
/* Check the incomming packet and reinit the data
* structures if necessary.
*/
header = &nic.packet[ETH_HLEN +
sizeof(struct iphdr) + sizeof(struct udphdr)];
data = header + state.hdr_len;
if (memcmp(state.hdr, header, state.hdr_len) != 0) {
/* Something is fishy reset the transaction */
data = reinit_slam_state(header, &nic.packet[nic.packetlen]);
if (!data) {
return 0;
}
}
}
if (type == SLAM_DATA) {
if (!slam_recv_data(data)) {
return 0;
}
if (state.received_packets == state.total_packets) {
/* We are done get out */
break;
}
}
if ((type == SLAM_TIMEOUT) || (type == SLAM_REQUEST)) {
/* Either I timed out or I was explicitly
* asked by a request packet
*/
slam_send_nack(info);
}
}
slam_send_disconnect(info);
/* Leave the multicast group */
leave_group(IGMP_SERVER);
/* FIXME don't overwrite myself */
/* load file to correct location */
return info->fnc(state.image, 1, state.total_bytes, 1);
}
int url_slam(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
{
struct slam_info info;
/* Set the defaults */
info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
info.server_port = SLAM_PORT;
info.multicast_ip.s_addr = htonl(SLAM_MULTICAST_IP);
info.multicast_port = SLAM_MULTICAST_PORT;
info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
info.local_port = SLAM_LOCAL_PORT;
info.fnc = fnc;
info.sent_nack = 0;
/* Now parse the url */
if (url_port != -1) {
info.server_port = url_port;
}
if (name[0]) {
/* multicast ip */
name += inet_aton(name, &info.multicast_ip);
if (name[0] == ':') {
name++;
info.multicast_port = strtoul(name, &name, 10);
}
}
if (name[0]) {
printf("\nBad url\n");
return 0;
}
return proto_slam(&info);
}
#endif /* DOWNLOAD_PROTO_SLAM */

View File

@@ -1,491 +0,0 @@
/**************************************************************************
*
* proto_tftm.c -- Etherboot Multicast TFTP
* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* This code is based on the DOWNLOAD_PROTO_TFTM section of
* Etherboot 5.3 core/nic.c and:
*
* Anselm Martin Hoffmeister's previous proto_tftm.c multicast work
* Eric Biederman's proto_slam.c
*
* $Revision$
* $Author$
* $Date$
*
* REVISION HISTORY:
* ================
* 09-07-2003 timlegge Release Version, Capable of Multicast Booting
* 08-30-2003 timlegge Initial version, Assumes consecutive blocks
*
* Indent Options: indent -kr -i8
***************************************************************************/
#ifdef DOWNLOAD_PROTO_TFTM
#include "etherboot.h"
#include "nic.h"
//#define TFTM_DEBUG
#ifdef TFTM_DEBUG
#define debug(x) printf x
#else
#define debug(x)
#endif
struct tftm_info {
in_addr server_ip;
in_addr multicast_ip;
in_addr local_ip;
uint16_t server_port;
uint16_t multicast_port;
uint16_t local_port;
int (*fnc) (unsigned char *, unsigned int, unsigned int, int);
int sent_nack;
const char *name; /* Filename */
};
struct tftm_state {
unsigned long block_size;
unsigned long total_bytes;
unsigned long total_packets;
char ismaster;
unsigned long received_packets;
unsigned char *image;
unsigned char *bitmap;
char recvd_oack;
} state;
#define TFTM_PORT 1758
#define TFTM_MIN_PACKET 1024
int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
unsigned long *filesize, struct tftm_info *info);
static int await_tftm(int ival, void *ptr, unsigned short ptype __unused,
struct iphdr *ip, struct udphdr *udp)
{
struct tftm_info *info = ptr;
/* Check for Unicast data being received */
if (ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) {
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; /* Unicast Data Received */
}
/* Also check for Multicast data being received */
if ((ip->dest.s_addr == info->multicast_ip.s_addr) &&
(ntohs(udp->dest) == info->multicast_port) &&
(nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) +
sizeof(struct udphdr))) {
return 1; /* Multicast data received */
}
return 0;
}
int proto_tftm(struct tftm_info *info)
{
int retry = 0;
static unsigned short iport = 2000;
unsigned short oport = 0;
unsigned short len, block = 0, prevblock = 0;
struct tftp_t *tr;
struct tftpreq_t tp;
unsigned long filesize = 0;
state.image = 0;
state.bitmap = 0;
rx_qdrain();
/* Warning: the following assumes the layout of bootp_t.
But that's fixed by the IP, UDP and BOOTP specs. */
/* Send a tftm-request to the server */
tp.opcode = htons(TFTP_RRQ); /* Const for "\0x0" "\0x1" =^= ReadReQuest */
len =
sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
sprintf((char *) tp.u.rrq,
"%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
info->name, 0, 0, 0, 0, 0, TFTM_MIN_PACKET, 0, 0) + 1;
if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
TFTM_PORT, len, &tp))
return (0);
/* loop to listen for packets and to receive the file */
for (;;) {
long timeout;
#ifdef CONGESTED
timeout =
rfc2131_sleep_interval(block ? TFTP_REXMT : TIMEOUT,
retry);
#else
timeout = rfc2131_sleep_interval(TIMEOUT, retry);
#endif
/* Calls the await_reply function in nic.c which in turn calls
await_tftm (1st parameter) as above */
if (!await_reply(await_tftm, iport, info, timeout)) {
if (!block && retry++ < MAX_TFTP_RETRIES) { /* maybe initial request was lost */
if (!udp_transmit
(arptable[ARP_SERVER].ipaddr.s_addr,
++iport, TFTM_PORT, len, &tp))
return (0);
continue;
}
#ifdef CONGESTED
if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT)) { /* we resend our last ack */
#ifdef MDEBUG
printf("<REXMT>\n");
#endif
debug(("Timed out receiving file"));
len =
sizeof(tp.ip) + sizeof(tp.udp) +
sizeof(tp.opcode) +
sprintf((char *) tp.u.rrq,
"%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
info->name, 0, 0, 0, 0, 0,
TFTM_MIN_PACKET, 0, 0) + 1;
udp_transmit
(arptable[ARP_SERVER].ipaddr.s_addr,
++iport, TFTM_PORT, len, &tp);
continue;
}
#endif
break; /* timeout */
}
tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
if (tr->opcode == ntohs(TFTP_ERROR)) {
printf("TFTP error %d (%s)\n",
ntohs(tr->u.err.errcode), tr->u.err.errmsg);
break;
}
if (tr->opcode == ntohs(TFTP_OACK)) {
int i =
opt_get_multicast(tr, &len, &filesize, info);
if (i == 0 || (i != 7 && !state.recvd_oack)) { /* Multicast unsupported */
/* Transmit an error message to the server to end the transmission */
printf
("TFTM-Server doesn't understand options [blksize tsize multicast]\n");
tp.opcode = htons(TFTP_ERROR);
tp.u.err.errcode = 8;
/*
* Warning: the following assumes the layout of bootp_t.
* But that's fixed by the IP, UDP and BOOTP specs.
*/
len =
sizeof(tp.ip) + sizeof(tp.udp) +
sizeof(tp.opcode) +
sizeof(tp.u.err.errcode) +
/*
* Normally bad form to omit the format string, but in this case
* the string we are copying from is fixed. sprintf is just being
* used as a strcpy and strlen.
*/
sprintf((char *) tp.u.err.errmsg,
"RFC2090 error") + 1;
udp_transmit(arptable[ARP_SERVER].ipaddr.
s_addr, iport,
ntohs(tr->udp.src), len, &tp);
block = tp.u.ack.block = 0; /* this ensures, that */
/* the packet does not get */
/* processed as data! */
return (0);
} else {
unsigned long bitmap_len;
/* */
if (!state.recvd_oack) {
state.total_packets =
1 + (filesize -
(filesize %
state.block_size)) /
state.block_size;
bitmap_len =
(state.total_packets + 7) / 8;
if (!state.image) {
state.bitmap =
allot(bitmap_len);
state.image =
allot(filesize);
if ((unsigned long) state.
image < 1024 * 1024) {
printf
("ALERT: tftp filesize to large for available memory\n");
return 0;
}
memset(state.bitmap, 0,
bitmap_len);
}
/* If I'm running over multicast join the multicast group */
join_group(IGMP_SERVER,
info->multicast_ip.
s_addr);
}
state.recvd_oack = 1;
}
} else if (tr->opcode == htons(TFTP_DATA)) {
unsigned long data_len;
unsigned char *data;
struct udphdr *udp;
udp =
(struct udphdr *) &nic.packet[ETH_HLEN +
sizeof(struct
iphdr)];
len =
ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
data =
nic.packet + ETH_HLEN + sizeof(struct iphdr) +
sizeof(struct udphdr) + 4;
if (len > TFTM_MIN_PACKET) /* shouldn't happen */
continue; /* ignore it */
block = ntohs(tp.u.ack.block = tr->u.data.block);
if (block > state.total_packets) {
printf("ALERT: Invalid packet number\n");
continue;
}
/* Compute the expected data length */
if (block != state.total_packets) {
data_len = state.block_size;
} else {
data_len = filesize % state.block_size;
}
/* If the packet size is wrong drop the packet and then continue */
if (ntohs(udp->len) !=
(data_len + (data - (unsigned char *) udp))) {
printf
("ALERT: udp packet is not the correct size: %d\n",
block);
continue;
}
if (nic.packetlen < data_len + (data - nic.packet)) {
printf
("ALERT: Ethernet packet shorter than data_len: %d\n",
block);
continue;
}
if (data_len > state.block_size) {
data_len = state.block_size;
}
if (((state.
bitmap[block >> 3] >> (block & 7)) & 1) ==
0) {
/* Non duplicate packet */
state.bitmap[block >> 3] |=
(1 << (block & 7));
memcpy(state.image +
((block - 1) * state.block_size),
data, data_len);
state.received_packets++;
} else {
/* printf("<DUP>\n"); */
}
}
else { /* neither TFTP_OACK, TFTP_DATA nor TFTP_ERROR */
break;
}
if (state.received_packets <= state.total_packets) {
unsigned long b;
unsigned long len;
unsigned long max;
int value;
int last;
/* Compute the last bit and store an inverted trailer */
max = state.total_packets + 1;
value =
((state.
bitmap[(max - 1) >> 3] >> ((max -
1) & 7)) & 1);
value = !value;
state.bitmap[max >> 3] &= ~(1 << (max & 7));
state.bitmap[max >> 3] |= value << (max & 7);
len = 0;
last = 0; /* Start with the received packets */
for (b = 1; b <= max; b++) {
value =
(state.bitmap[b >> 3] >> (b & 7)) & 1;
if (value == 0) {
tp.u.ack.block = htons(b - 1); /* Acknowledge the previous block */
break;
}
}
}
if (state.ismaster) {
tp.opcode = htons(TFTP_ACK);
oport = ntohs(tr->udp.src);
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, oport, TFTP_MIN_PACKET, &tp); /* ack */
}
if (state.received_packets == state.total_packets) {
/* If the client is finished and not the master,
* ack the last packet */
if (!state.ismaster) {
tp.opcode = htons(TFTP_ACK);
/* Ack Last packet to end xfer */
tp.u.ack.block = htons(state.total_packets);
oport = ntohs(tr->udp.src);
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, oport, TFTP_MIN_PACKET, &tp); /* ack */
}
/* We are done get out */
forget(state.bitmap);
break;
}
if ((unsigned short) (block - prevblock) != 1) {
/* Retransmission or OACK, don't process via callback
* and don't change the value of prevblock. */
continue;
}
prevblock = block;
retry = 0; /* It's the right place to zero the timer? */
}
/* Leave the multicast group */
leave_group(IGMP_SERVER);
return info->fnc(state.image, 1, filesize, 1);
}
int url_tftm(const char *name,
int (*fnc) (unsigned char *, unsigned int, unsigned int, int))
{
int ret;
struct tftm_info info;
/* Set the defaults */
info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
info.server_port = TFTM_PORT;
info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
info.local_port = TFTM_PORT; /* Does not matter. So take tftm port too. */
info.multicast_ip.s_addr = info.local_ip.s_addr;
info.multicast_port = TFTM_PORT;
info.fnc = fnc;
state.ismaster = 0;
info.name = name;
state.block_size = 0;
state.total_bytes = 0;
state.total_packets = 0;
state.received_packets = 0;
state.image = 0;
state.bitmap = 0;
state.recvd_oack = 0;
if (name[0] != '/') {
/* server ip given, so use it */
name += inet_aton(info.name, &info.server_ip);
/* No way to specify another port for now */
}
if (name[0] != '/') {
printf("Bad tftm-URI: [%s]\n", info.name);
return 0;
}
ret = proto_tftm(&info);
return ret;
}
/******************************
* Parse the multicast options
*******************************/
int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
unsigned long *filesize, struct tftm_info *info)
{
const char *p = tr->u.oack.data, *e = 0;
int i = 0;
*len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
if (*len > TFTM_MIN_PACKET)
return -1;
e = p + *len;
while (*p != '\0' && p < e) {
if (!strcasecmp("tsize", p)) {
p += 6;
if ((*filesize = strtoul(p, &p, 10)) > 0)
i |= 4;
debug(("\n"));
debug(("tsize=%d\n", *filesize));
while (p < e && *p)
p++;
if (p < e)
p++;
} else if (!strcasecmp("blksize", p)) {
i |= 2;
p += 8;
state.block_size = strtoul(p, &p, 10);
if (state.block_size != TFTM_MIN_PACKET) {
printf
("TFTM-Server rejected required transfer blocksize %d\n",
TFTM_MIN_PACKET);
return 0;
}
debug(("blksize=%d\n", state.block_size));
while (p < e && *p)
p++;
if (p < e)
p++;
} else if (!strncmp(p, "multicast", 10)) {
i |= 1;
p += 10;
debug(("multicast options: %s\n", p));
p += 1 + inet_aton(p, &info->multicast_ip);
debug(("multicast ip = %@\n", info->multicast_ip));
info->multicast_port = strtoul(p, &p, 10);
++p;
debug(("multicast port = %d\n",
info->multicast_port));
state.ismaster = (*p == '1' ? 1 : 0);
debug(("multicast ismaster = %d\n",
state.ismaster));
while (p < e && *p)
p++;
if (p < e)
p++;
}
}
if (p > e)
return 0;
return i;
}
#endif /* DOWNLOAD_PROTO_TFTP */