176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * TFTP data output backend
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h>
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h>
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <syslinux/pxe.h>
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <syslinux/config.h>
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <netinet/in.h>
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <sys/times.h>
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "upload_backend.h"
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanenum tftp_opcode {
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    TFTP_RRQ	= 1,
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    TFTP_WRQ	= 2,
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    TFTP_DATA	= 3,
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    TFTP_ACK	= 4,
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    TFTP_ERROR	= 5,
1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct tftp_error {
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t opcode;
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t errcode;
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	char errmsg[0];
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} __attribute__ (( packed ));
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct tftp_state {
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t my_ip;
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t srv_ip;
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint32_t srv_gw;
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint16_t my_port;
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint16_t srv_port;
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uint16_t seq;
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanconst char *tftp_string_error_message[]={
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"",
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"File not found",
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"Access Denied",
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"Disk Full",
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"Illegal Operation",
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"Unknown Transfert ID",
4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"File already exists",
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"Unknown User",
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"Negociation failed",
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"Unable to resolve hostname", // not in RFC
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"Unable to connect", // not in RFC
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman"No Error",
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define RCV_BUF	2048
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int send_ack_packet(struct tftp_state *tftp,
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			   const void *pkt, size_t len)
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    t_PXENV_UDP_WRITE *uw;
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    t_PXENV_UDP_READ  *ur;
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    clock_t start;
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    static const clock_t timeouts[] = {
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31,
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    };
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const clock_t *timeout;
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int err = -1;
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    uw = lmalloc(sizeof *uw + len);
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    ur = lmalloc(sizeof *ur + RCV_BUF);
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    for (timeout = timeouts ; *timeout ; timeout++) {
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memset(uw, 0, sizeof *uw);
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memcpy(uw+1, pkt, len);
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uw->ip = tftp->srv_ip;
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uw->gw = tftp->srv_gw;
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uw->src_port = tftp->my_port;
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69);
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uw->buffer_size = len;
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uw->buffer = FAR_PTR(uw+1);
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pxe_call(PXENV_UDP_WRITE, uw);
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	start = times(NULL);
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	do {
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    memset(ur, 0, sizeof *ur);
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    ur->src_ip = tftp->srv_ip;
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    ur->dest_ip = tftp->my_ip;
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    ur->s_port = tftp->srv_port;
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    ur->d_port = tftp->my_port;
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    ur->buffer_size = RCV_BUF;
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    ur->buffer = FAR_PTR(ur+1);
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    err = pxe_call(PXENV_UDP_READ, ur);
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    if (!err &&	ur->status == PXENV_STATUS_SUCCESS &&
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tftp->srv_ip == ur->src_ip &&
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		(tftp->srv_port == 0 ||
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 tftp->srv_port == ur->s_port)) {
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		uint16_t *xb = (uint16_t *)(ur+1);
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (ntohs(xb[0]) == TFTP_ACK &&
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    ntohs(xb[1]) == tftp->seq) {
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    tftp->srv_port = ur->s_port;
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    err = TFTP_OK;		/* All good! */
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    goto done;
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else if (ntohs(xb[0]) == TFTP_ERROR) {
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    struct tftp_error *te = (struct tftp_error *)(ur+1);
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    if (te->errcode == TFTP_ERR_UNKNOWN_ERROR) {
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    	tftp_string_error_message[TFTP_ERR_UNKNOWN_ERROR]=strdup(te->errmsg);
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    }
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 		    err=-ntohs(te->errcode); // Return the associated error code
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    goto done;
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    }
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} while ((clock_t)(times(NULL) - start) < *timeout);
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmandone:
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    lfree(ur);
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    lfree(uw);
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return err;
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int upload_tftp_write(struct upload_backend *be)
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    static uint16_t local_port = 0x4000;
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct tftp_state tftp;
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    char buffer[512+4+6];
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int nlen;
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    int err=TFTP_OK;
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const union syslinux_derivative_info *sdi =
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	syslinux_derivative_info();
13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    const char *data = be->outbuf;
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    size_t len = be->zbytes;
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    size_t chunk;
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    tftp.my_ip    = sdi->pxe.myip;
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    tftp.my_port  = htons(local_port++);
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    tftp.srv_gw   = ((tftp.srv_ip ^ tftp.my_ip) & sdi->pxe.ipinfo->netmask)
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	? sdi->pxe.ipinfo->gateway : 0;
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    tftp.srv_port = 0;
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    tftp.seq      = 0;
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (be->argv[1]) {
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tftp.srv_ip   = pxe_dns(be->argv[1]);
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (!tftp.srv_ip) {
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman//	    printf("\nUnable to resolve hostname: %s\n", be->argv[1]);
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    return -TFTP_ERR_UNABLE_TO_RESOLVE;
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } else {
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tftp.srv_ip   = sdi->pxe.ipinfo->serverip;
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (!tftp.srv_ip) {
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman//	    printf("\nNo server IP address\n");
15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    return -TFTP_ERR_UNABLE_TO_CONNECT;
15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*    printf("server %u.%u.%u.%u... ",
15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	   ((uint8_t *)&tftp.srv_ip)[0],
15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	   ((uint8_t *)&tftp.srv_ip)[1],
16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	   ((uint8_t *)&tftp.srv_ip)[2],
16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	   ((uint8_t *)&tftp.srv_ip)[3]);*/
16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    buffer[0] = 0;
16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    buffer[1] = TFTP_WRQ;
16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    nlen = strlcpy(buffer+2, be->argv[0], 512);
16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    memcpy(buffer+3+nlen, "octet", 6);
16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if ((err=send_ack_packet(&tftp, buffer, 2+nlen+1+6))!=TFTP_OK)
16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return err;
17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    do {
17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	chunk = len >= 512 ? 512 : len;
17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	buffer[1] = TFTP_DATA;
17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	*((uint16_t *)(buffer+2)) = htons(++tftp.seq);
17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memcpy(buffer+4, data, chunk);
17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	data += chunk;
17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	len -= chunk;
17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ((err=send_ack_packet(&tftp, buffer, chunk+4))!=TFTP_OK)
18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    return err;
18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } while (chunk == 512);
18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return TFTP_OK;
18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct upload_backend upload_tftp = {
18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .name       = "tftp",
18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .helpmsg    = "filename [tftp_server]",
19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .minargs    = 1,
19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    .write      = upload_tftp_write,
19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
193