aboutsummaryrefslogtreecommitdiffstats
path: root/com32/sysdump/be_tftp.c
blob: 17275b1621f7b9645e189c0d001784ffdb014366 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
 * TFTP data output backend
 */

#include <string.h>
#include <syslinux/pxe.h>
#include <syslinux/config.h>
#include <netinet/in.h>
#include <sys/times.h>

#include "backend.h"

enum tftp_opcode {
    TFTP_RRQ	= 1,
    TFTP_WRQ	= 2,
    TFTP_DATA	= 3,
    TFTP_ACK	= 4,
    TFTP_ERROR	= 5,
};

static uint16_t local_port = 0x4000;

static int send_ack_packet(struct backend *be, const void *pkt, size_t len)
{
    com32sys_t ireg, oreg;
    t_PXENV_UDP_WRITE *uw = __com32.cs_bounce;
    t_PXENV_UDP_READ  *ur = __com32.cs_bounce;
    clock_t start;
    static const clock_t timeouts[] = {
	2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31,
	37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0
    };
    const clock_t *timeout;

    memset(&ireg, 0, sizeof ireg);
    ireg.eax.w[0] = 0x0009;

    for (timeout = timeouts ; *timeout ; timeout++) {
	memset(uw, 0, sizeof uw);
	memcpy(uw+1, pkt, len);
	uw->ip = be->tftp.srv_ip;
	uw->src_port = be->tftp.my_port;
	uw->dst_port = be->tftp.srv_port ? be->tftp.srv_port : htons(69);
	uw->buffer_size = len;
	uw->buffer = FAR_PTR(uw+1);

	ireg.ebx.w[0] = PXENV_UDP_WRITE;
	ireg.es = SEG(uw);
	ireg.edi.w[0] = OFFS(uw);
	
	__intcall(0x22, &ireg, &oreg);

	start = times(NULL);

	do {
	    memset(ur, 0, sizeof ur);
	    ur->src_ip = be->tftp.srv_ip;
	    ur->dest_ip = be->tftp.my_ip;
	    ur->s_port = be->tftp.srv_port;
	    ur->d_port = be->tftp.my_port;
	    ur->buffer_size = __com32.cs_bounce_size - sizeof *ur;
	    ur->buffer = FAR_PTR(ur+1);
	    
	    ireg.ebx.w[0] = PXENV_UDP_READ;
	    ireg.es = SEG(ur);
	    ireg.edi.w[0] = OFFS(ur);
	    __intcall(0x22, &ireg, &oreg);

	    if (!(oreg.eflags.l & EFLAGS_CF) &&
		ur->status == PXENV_STATUS_SUCCESS &&
		be->tftp.srv_ip == ur->src_ip &&
		(be->tftp.srv_port == 0 ||
		 be->tftp.srv_port == ur->s_port)) {
		uint16_t *xb = (uint16_t *)(ur+1);
		if (ntohs(xb[0]) == TFTP_ACK &&
		    ntohs(xb[1]) == be->tftp.seq) {
		    be->tftp.srv_port = ur->s_port;
		    return 0;		/* All good! */
		} else if (ntohs(xb[1]) == TFTP_ERROR) {
		    return -1;		/* All bad! */
		}
	    }
	} while ((clock_t)(times(NULL) - start) < *timeout);
    }

    return -1;			/* No success... */
}

static int be_tftp_open(struct backend *be, const char *argv[])
{
    char buffer[512+4+6];
    int nlen;
    const union syslinux_derivative_info *sdi =
	syslinux_derivative_info();

    be->tftp.my_ip    = sdi->pxe.myip;
    be->tftp.my_port  = htons(local_port++);
    be->tftp.srv_ip   = pxe_dns(argv[1]);
    be->tftp.srv_port = 0;
    be->tftp.seq      = 0;

    buffer[0] = 0;
    buffer[1] = TFTP_WRQ;
    nlen = strlcpy(buffer+2, argv[0], 512);
    memcpy(buffer+3+nlen, "octet", 6);

    return send_ack_packet(be, buffer, 2+nlen+1+6);
}

static int be_tftp_write(struct backend *be, const char *buf, size_t len)
{
    char buffer[512+4];

    buffer[0] = 0;
    buffer[1] = TFTP_DATA;
    *((uint16_t *)(buffer+2)) = htons(++be->tftp.seq);
    memcpy(buffer+4, buf, len);

    return send_ack_packet(be, buffer, len+4);
}

struct backend be_tftp = {
    .name       = "tftp",
    .blocksize	= 512,
    .open       = be_tftp_open,
    .write      = be_tftp_write,
};