Home | History | Annotate | Download | only in libupload
      1 /*
      2  * TFTP data output backend
      3  */
      4 
      5 #include <string.h>
      6 #include <stdio.h>
      7 #include <syslinux/pxe.h>
      8 #include <syslinux/config.h>
      9 #include <netinet/in.h>
     10 #include <sys/times.h>
     11 #include "upload_backend.h"
     12 
     13 enum tftp_opcode {
     14     TFTP_RRQ	= 1,
     15     TFTP_WRQ	= 2,
     16     TFTP_DATA	= 3,
     17     TFTP_ACK	= 4,
     18     TFTP_ERROR	= 5,
     19 };
     20 
     21 struct tftp_error {
     22 	uint16_t opcode;
     23 	uint16_t errcode;
     24 	char errmsg[0];
     25 } __attribute__ (( packed ));
     26 
     27 struct tftp_state {
     28     uint32_t my_ip;
     29     uint32_t srv_ip;
     30     uint32_t srv_gw;
     31     uint16_t my_port;
     32     uint16_t srv_port;
     33     uint16_t seq;
     34 };
     35 
     36 const char *tftp_string_error_message[]={
     37 "",
     38 "File not found",
     39 "Access Denied",
     40 "Disk Full",
     41 "Illegal Operation",
     42 "Unknown Transfert ID",
     43 "File already exists",
     44 "Unknown User",
     45 "Negociation failed",
     46 "Unable to resolve hostname", // not in RFC
     47 "Unable to connect", // not in RFC
     48 "No Error",
     49 };
     50 
     51 #define RCV_BUF	2048
     52 
     53 static int send_ack_packet(struct tftp_state *tftp,
     54 			   const void *pkt, size_t len)
     55 {
     56     t_PXENV_UDP_WRITE *uw;
     57     t_PXENV_UDP_READ  *ur;
     58     clock_t start;
     59     static const clock_t timeouts[] = {
     60 	2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31,
     61 	37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0
     62     };
     63     const clock_t *timeout;
     64     int err = -1;
     65 
     66     uw = lmalloc(sizeof *uw + len);
     67     ur = lmalloc(sizeof *ur + RCV_BUF);
     68 
     69     for (timeout = timeouts ; *timeout ; timeout++) {
     70 	memset(uw, 0, sizeof *uw);
     71 	memcpy(uw+1, pkt, len);
     72 	uw->ip = tftp->srv_ip;
     73 	uw->gw = tftp->srv_gw;
     74 	uw->src_port = tftp->my_port;
     75 	uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69);
     76 	uw->buffer_size = len;
     77 	uw->buffer = FAR_PTR(uw+1);
     78 
     79 	pxe_call(PXENV_UDP_WRITE, uw);
     80 
     81 	start = times(NULL);
     82 
     83 	do {
     84 	    memset(ur, 0, sizeof *ur);
     85 	    ur->src_ip = tftp->srv_ip;
     86 	    ur->dest_ip = tftp->my_ip;
     87 	    ur->s_port = tftp->srv_port;
     88 	    ur->d_port = tftp->my_port;
     89 	    ur->buffer_size = RCV_BUF;
     90 	    ur->buffer = FAR_PTR(ur+1);
     91 
     92 	    err = pxe_call(PXENV_UDP_READ, ur);
     93 
     94 	    if (!err &&	ur->status == PXENV_STATUS_SUCCESS &&
     95 		tftp->srv_ip == ur->src_ip &&
     96 		(tftp->srv_port == 0 ||
     97 		 tftp->srv_port == ur->s_port)) {
     98 		uint16_t *xb = (uint16_t *)(ur+1);
     99 		if (ntohs(xb[0]) == TFTP_ACK &&
    100 		    ntohs(xb[1]) == tftp->seq) {
    101 		    tftp->srv_port = ur->s_port;
    102 		    err = TFTP_OK;		/* All good! */
    103 		    goto done;
    104 		} else if (ntohs(xb[0]) == TFTP_ERROR) {
    105 		    struct tftp_error *te = (struct tftp_error *)(ur+1);
    106 		    if (te->errcode == TFTP_ERR_UNKNOWN_ERROR) {
    107 		    	tftp_string_error_message[TFTP_ERR_UNKNOWN_ERROR]=strdup(te->errmsg);
    108 		    }
    109  		    err=-ntohs(te->errcode); // Return the associated error code
    110 		    goto done;
    111 		}
    112 	    }
    113 	} while ((clock_t)(times(NULL) - start) < *timeout);
    114     }
    115 
    116 done:
    117     lfree(ur);
    118     lfree(uw);
    119 
    120     return err;
    121 }
    122 
    123 static int upload_tftp_write(struct upload_backend *be)
    124 {
    125     static uint16_t local_port = 0x4000;
    126     struct tftp_state tftp;
    127     char buffer[512+4+6];
    128     int nlen;
    129     int err=TFTP_OK;
    130     const union syslinux_derivative_info *sdi =
    131 	syslinux_derivative_info();
    132     const char *data = be->outbuf;
    133     size_t len = be->zbytes;
    134     size_t chunk;
    135 
    136     tftp.my_ip    = sdi->pxe.myip;
    137     tftp.my_port  = htons(local_port++);
    138     tftp.srv_gw   = ((tftp.srv_ip ^ tftp.my_ip) & sdi->pxe.ipinfo->netmask)
    139 	? sdi->pxe.ipinfo->gateway : 0;
    140     tftp.srv_port = 0;
    141     tftp.seq      = 0;
    142 
    143     if (be->argv[1]) {
    144 	tftp.srv_ip   = pxe_dns(be->argv[1]);
    145 	if (!tftp.srv_ip) {
    146 //	    printf("\nUnable to resolve hostname: %s\n", be->argv[1]);
    147 	    return -TFTP_ERR_UNABLE_TO_RESOLVE;
    148 	}
    149     } else {
    150 	tftp.srv_ip   = sdi->pxe.ipinfo->serverip;
    151 	if (!tftp.srv_ip) {
    152 //	    printf("\nNo server IP address\n");
    153 	    return -TFTP_ERR_UNABLE_TO_CONNECT;
    154 	}
    155     }
    156 
    157 /*    printf("server %u.%u.%u.%u... ",
    158 	   ((uint8_t *)&tftp.srv_ip)[0],
    159 	   ((uint8_t *)&tftp.srv_ip)[1],
    160 	   ((uint8_t *)&tftp.srv_ip)[2],
    161 	   ((uint8_t *)&tftp.srv_ip)[3]);*/
    162 
    163     buffer[0] = 0;
    164     buffer[1] = TFTP_WRQ;
    165     nlen = strlcpy(buffer+2, be->argv[0], 512);
    166     memcpy(buffer+3+nlen, "octet", 6);
    167 
    168     if ((err=send_ack_packet(&tftp, buffer, 2+nlen+1+6))!=TFTP_OK)
    169 	return err;
    170 
    171     do {
    172 	chunk = len >= 512 ? 512 : len;
    173 
    174 	buffer[1] = TFTP_DATA;
    175 	*((uint16_t *)(buffer+2)) = htons(++tftp.seq);
    176 	memcpy(buffer+4, data, chunk);
    177 	data += chunk;
    178 	len -= chunk;
    179 
    180 	if ((err=send_ack_packet(&tftp, buffer, chunk+4))!=TFTP_OK)
    181 	    return err;
    182     } while (chunk == 512);
    183 
    184     return TFTP_OK;
    185 }
    186 
    187 struct upload_backend upload_tftp = {
    188     .name       = "tftp",
    189     .helpmsg    = "filename [tftp_server]",
    190     .minargs    = 1,
    191     .write      = upload_tftp_write,
    192 };
    193