Home | History | Annotate | Download | only in pxe
      1 #include <minmax.h>
      2 #include <net.h>
      3 #include "pxe.h"
      4 #include "url.h"
      5 #include "tftp.h"
      6 
      7 const uint8_t TimeoutTable[] = {
      8     2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
      9     53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
     10 };
     11 struct tftp_packet {
     12     uint16_t opcode;
     13     uint16_t serial;
     14     char data[];
     15 };
     16 
     17 static void tftp_error(struct inode *file, uint16_t errnum,
     18 		       const char *errstr);
     19 
     20 static void tftp_close_file(struct inode *inode)
     21 {
     22     struct pxe_pvt_inode *socket = PVT(inode);
     23     if (!socket->tftp_goteof) {
     24 	tftp_error(inode, 0, "No error, file close");
     25     }
     26     core_udp_close(socket);
     27 }
     28 
     29 /**
     30  * Send an ERROR packet.  This is used to terminate a connection.
     31  *
     32  * @inode:	Inode structure
     33  * @errnum:	Error number (network byte order)
     34  * @errstr:	Error string (included in packet)
     35  */
     36 static void tftp_error(struct inode *inode, uint16_t errnum,
     37 		       const char *errstr)
     38 {
     39     static struct {
     40 	uint16_t err_op;
     41 	uint16_t err_num;
     42 	char err_msg[64];
     43     } __packed err_buf;
     44     int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
     45     struct pxe_pvt_inode *socket = PVT(inode);
     46 
     47     err_buf.err_op  = TFTP_ERROR;
     48     err_buf.err_num = errnum;
     49     memcpy(err_buf.err_msg, errstr, len);
     50     err_buf.err_msg[len] = '\0';
     51 
     52     core_udp_send(socket, &err_buf, 4 + len + 1);
     53 }
     54 
     55 /**
     56  * Send ACK packet. This is a common operation and so is worth canning.
     57  *
     58  * @param: inode,   Inode pointer
     59  * @param: ack_num, Packet # to ack (host byte order)
     60  *
     61  */
     62 static void ack_packet(struct inode *inode, uint16_t ack_num)
     63 {
     64     static uint16_t ack_packet_buf[2];
     65     struct pxe_pvt_inode *socket = PVT(inode);
     66 
     67     /* Packet number to ack */
     68     ack_packet_buf[0]     = TFTP_ACK;
     69     ack_packet_buf[1]     = htons(ack_num);
     70 
     71     core_udp_send(socket, ack_packet_buf, 4);
     72 }
     73 
     74 /*
     75  * Get a fresh packet if the buffer is drained, and we haven't hit
     76  * EOF yet.  The buffer should be filled immediately after draining!
     77  */
     78 static void tftp_get_packet(struct inode *inode)
     79 {
     80     uint16_t last_pkt;
     81     const uint8_t *timeout_ptr;
     82     uint8_t timeout;
     83     uint16_t buffersize;
     84     uint16_t serial;
     85     jiffies_t oldtime;
     86     struct tftp_packet *pkt = NULL;
     87     uint16_t buf_len;
     88     struct pxe_pvt_inode *socket = PVT(inode);
     89     uint16_t src_port;
     90     uint32_t src_ip;
     91     int err;
     92 
     93     /*
     94      * Start by ACKing the previous packet; this should cause
     95      * the next packet to be sent.
     96      */
     97     timeout_ptr = TimeoutTable;
     98     timeout = *timeout_ptr++;
     99     oldtime = jiffies();
    100 
    101  ack_again:
    102     ack_packet(inode, socket->tftp_lastpkt);
    103 
    104     while (timeout) {
    105 	buf_len = socket->tftp_blksize + 4;
    106 	err = core_udp_recv(socket, socket->tftp_pktbuf, &buf_len,
    107 			    &src_ip, &src_port);
    108 	if (err) {
    109 	    jiffies_t now = jiffies();
    110 
    111 	    if (now-oldtime >= timeout) {
    112 		oldtime = now;
    113 		timeout = *timeout_ptr++;
    114 		if (!timeout)
    115 		    break;
    116 		goto ack_again;
    117 	    }
    118             continue;
    119 	}
    120 
    121 	if (buf_len < 4)	/* Bad size for a DATA packet */
    122 	    continue;
    123 
    124         pkt = (struct tftp_packet *)(socket->tftp_pktbuf);
    125         if (pkt->opcode != TFTP_DATA)    /* Not a data packet */
    126             continue;
    127 
    128         /* If goes here, recevie OK, break */
    129         break;
    130     }
    131 
    132     /* time runs out */
    133     if (timeout == 0)
    134 	kaboom();
    135 
    136     last_pkt = socket->tftp_lastpkt;
    137     last_pkt++;
    138     serial = ntohs(pkt->serial);
    139     if (serial != last_pkt) {
    140         /*
    141          * Wrong packet, ACK the packet and try again.
    142          * This is presumably because the ACK got lost,
    143          * so the server just resent the previous packet.
    144          */
    145 #if 0
    146 	printf("Wrong packet, wanted %04x, got %04x\n", \
    147                htons(last_pkt), htons(*(uint16_t *)(data+2)));
    148 #endif
    149         goto ack_again;
    150     }
    151 
    152     /* It's the packet we want.  We're also EOF if the size < blocksize */
    153     socket->tftp_lastpkt = last_pkt;    /* Update last packet number */
    154     buffersize = buf_len - 4;		/* Skip TFTP header */
    155     socket->tftp_dataptr = socket->tftp_pktbuf + 4;
    156     socket->tftp_filepos += buffersize;
    157     socket->tftp_bytesleft = buffersize;
    158     if (buffersize < socket->tftp_blksize) {
    159         /* it's the last block, ACK packet immediately */
    160         ack_packet(inode, serial);
    161 
    162         /* Make sure we know we are at end of file */
    163         inode->size 		= socket->tftp_filepos;
    164         socket->tftp_goteof	= 1;
    165         tftp_close_file(inode);
    166     }
    167 }
    168 
    169 const struct pxe_conn_ops tftp_conn_ops = {
    170     .fill_buffer	= tftp_get_packet,
    171     .close		= tftp_close_file,
    172 };
    173 
    174 /**
    175  * Open a TFTP connection to the server
    176  *
    177  * @param:inode, the inode to store our state in
    178  * @param:ip, the ip to contact to get the file
    179  * @param:filename, the file we wanna open
    180  *
    181  * @out: open_file_t structure, stores in file->open_file
    182  * @out: the lenght of this file, stores in file->file_len
    183  *
    184  */
    185 void tftp_open(struct url_info *url, int flags, struct inode *inode,
    186 	       const char **redir)
    187 {
    188     struct pxe_pvt_inode *socket = PVT(inode);
    189     char *buf;
    190     uint16_t buf_len;
    191     char *p;
    192     char *options;
    193     char *data;
    194     static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
    195     char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
    196     char reply_packet_buf[PKTBUF_SIZE];
    197     int err;
    198     int buffersize;
    199     int rrq_len;
    200     const uint8_t  *timeout_ptr;
    201     jiffies_t timeout;
    202     jiffies_t oldtime;
    203     uint16_t opcode;
    204     uint16_t blk_num;
    205     uint64_t opdata;
    206     uint16_t src_port;
    207     uint32_t src_ip;
    208 
    209     (void)redir;		/* TFTP does not redirect */
    210     (void)flags;
    211 
    212     if (url->type != URL_OLD_TFTP) {
    213 	/*
    214 	 * The TFTP URL specification allows the TFTP to end with a
    215 	 * ;mode= which we just ignore.
    216 	 */
    217 	url_unescape(url->path, ';');
    218     }
    219 
    220     if (!url->port)
    221 	url->port = TFTP_PORT;
    222 
    223     socket->ops = &tftp_conn_ops;
    224     if (core_udp_open(socket))
    225 	return;
    226 
    227     buf = rrq_packet_buf;
    228     *(uint16_t *)buf = TFTP_RRQ;  /* TFTP opcode */
    229     buf += 2;
    230 
    231     buf = stpcpy(buf, url->path);
    232 
    233     buf++;			/* Point *past* the final NULL */
    234     memcpy(buf, rrq_tail, sizeof rrq_tail);
    235     buf += sizeof rrq_tail;
    236 
    237     rrq_len = buf - rrq_packet_buf;
    238 
    239     timeout_ptr = TimeoutTable;   /* Reset timeout */
    240 sendreq:
    241     timeout = *timeout_ptr++;
    242     if (!timeout)
    243 	return;			/* No file available... */
    244     oldtime = jiffies();
    245 
    246     core_udp_sendto(socket, rrq_packet_buf, rrq_len, url->ip, url->port);
    247 
    248     /* If the WRITE call fails, we let the timeout take care of it... */
    249 wait_pkt:
    250     for (;;) {
    251 	buf_len = sizeof(reply_packet_buf);
    252 
    253 	err = core_udp_recv(socket, reply_packet_buf, &buf_len,
    254 			    &src_ip, &src_port);
    255 	if (err) {
    256 	    jiffies_t now = jiffies();
    257 	    if (now - oldtime >= timeout)
    258 		 goto sendreq;
    259 	} else {
    260 	    /* Make sure the packet actually came from the server and
    261 	       is long enough for a TFTP opcode */
    262 	    dprintf("tftp_open: got packet buflen=%d\n", buf_len);
    263 	    if ((src_ip == url->ip) && (buf_len >= 2))
    264 		break;
    265 	}
    266     }
    267 
    268     core_udp_disconnect(socket);
    269     core_udp_connect(socket, src_ip, src_port);
    270 
    271     /* filesize <- -1 == unknown */
    272     inode->size = -1;
    273     socket->tftp_blksize = TFTP_BLOCKSIZE;
    274     buffersize = buf_len - 2;	  /* bytes after opcode */
    275 
    276     /*
    277      * Get the opcode type, and parse it
    278      */
    279     opcode = *(uint16_t *)reply_packet_buf;
    280     switch (opcode) {
    281     case TFTP_ERROR:
    282         inode->size = 0;
    283 	goto done;        /* ERROR reply; don't try again */
    284 
    285     case TFTP_DATA:
    286         /*
    287          * If the server doesn't support any options, we'll get a
    288          * DATA reply instead of OACK. Stash the data in the file
    289          * buffer and go with the default value for all options...
    290          *
    291          * We got a DATA packet, meaning no options are
    292          * suported. Save the data away and consider the
    293          * length undefined, *unless* this is the only
    294          * data packet...
    295          */
    296         buffersize -= 2;
    297         if (buffersize < 0)
    298             goto wait_pkt;
    299         data = reply_packet_buf + 2;
    300         blk_num = ntohs(*(uint16_t *)data);
    301         data += 2;
    302         if (blk_num != 1)
    303             goto wait_pkt;
    304         socket->tftp_lastpkt = blk_num;
    305         if (buffersize > TFTP_BLOCKSIZE)
    306             goto err_reply;	/* Corrupt */
    307 
    308 	socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE + 4);
    309 	if (!socket->tftp_pktbuf)
    310 	    goto err_reply;	/* Internal error */
    311 
    312         if (buffersize < TFTP_BLOCKSIZE) {
    313             /*
    314              * This is the final EOF packet, already...
    315              * We know the filesize, but we also want to
    316              * ack the packet and set the EOF flag.
    317              */
    318             inode->size = buffersize;
    319             socket->tftp_goteof = 1;
    320             ack_packet(inode, blk_num);
    321         }
    322 
    323         socket->tftp_bytesleft = buffersize;
    324         socket->tftp_dataptr = socket->tftp_pktbuf;
    325         memcpy(socket->tftp_pktbuf, data, buffersize);
    326 	goto done;
    327 
    328     case TFTP_OACK:
    329         /*
    330          * Now we need to parse the OACK packet to get the transfer
    331          * and packet sizes.
    332          */
    333 
    334         options = reply_packet_buf + 2;
    335 	p = options;
    336 
    337 	while (buffersize) {
    338 	    const char *opt = p;
    339 
    340 	    /*
    341 	     * If we find an option which starts with a NUL byte,
    342 	     * (a null option), we're either seeing garbage that some
    343 	     * TFTP servers add to the end of the packet, or we have
    344 	     * no clue how to parse the rest of the packet (what is
    345 	     * an option name and what is a value?)  In either case,
    346 	     * discard the rest.
    347 	     */
    348 	    if (!*opt)
    349 		goto done;
    350 
    351             while (buffersize) {
    352                 if (!*p)
    353                     break;	/* Found a final null */
    354                 *p++ |= 0x20;
    355 		buffersize--;
    356             }
    357 	    if (!buffersize)
    358 		break;		/* Unterminated option */
    359 
    360 	    /* Consume the terminal null */
    361 	    p++;
    362 	    buffersize--;
    363 
    364 	    if (!buffersize)
    365 		break;		/* No option data */
    366 
    367 	    opdata = 0;
    368 
    369             /* do convert a number-string to decimal number, just like atoi */
    370             while (buffersize--) {
    371 		uint8_t d = *p++;
    372                 if (d == '\0')
    373                     break;              /* found a final null */
    374 		d -= '0';
    375                 if (d > 9)
    376                     goto err_reply;     /* Not a decimal digit */
    377                 opdata = opdata*10 + d;
    378             }
    379 
    380 	    if (!strcmp(opt, "tsize"))
    381 		inode->size = opdata;
    382 	    else if (!strcmp(opt, "blksize"))
    383 		socket->tftp_blksize = opdata;
    384 	    else
    385 		goto err_reply; /* Non-negotitated option returned,
    386 				   no idea what it means ...*/
    387 
    388 
    389 	}
    390 
    391 	if (socket->tftp_blksize < 64 || socket->tftp_blksize > PKTBUF_SIZE)
    392 	    goto err_reply;
    393 
    394 	/* Parsing successful, allocate buffer */
    395 	socket->tftp_pktbuf = malloc(socket->tftp_blksize + 4);
    396 	if (!socket->tftp_pktbuf)
    397 	    goto err_reply;
    398 	else
    399 	    goto done;
    400 
    401     default:
    402 	printf("TFTP unknown opcode %d\n", ntohs(opcode));
    403 	goto err_reply;
    404     }
    405 
    406 err_reply:
    407     /* Build the TFTP error packet */
    408     tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
    409     inode->size = 0;
    410 
    411 done:
    412     if (!inode->size)
    413 	core_udp_close(socket);
    414 
    415     return;
    416 }
    417