Adding upstream version 4.00~pre54+dfsg.
[syslinux-debian/hramrach.git] / com32 / sysdump / be_tftp.c
blob8c92d4d64957fbae14fc72eca3c9779f66aca687
1 /*
2 * TFTP data output backend
3 */
5 #include <string.h>
6 #include <syslinux/pxe.h>
7 #include <syslinux/config.h>
8 #include <netinet/in.h>
9 #include <sys/times.h>
11 #include "backend.h"
13 enum tftp_opcode {
14 TFTP_RRQ = 1,
15 TFTP_WRQ = 2,
16 TFTP_DATA = 3,
17 TFTP_ACK = 4,
18 TFTP_ERROR = 5,
21 struct tftp_state {
22 uint32_t my_ip;
23 uint32_t srv_ip;
24 uint32_t srv_gw;
25 uint16_t my_port;
26 uint16_t srv_port;
27 uint16_t seq;
30 #define RCV_BUF 2048
32 static int send_ack_packet(struct tftp_state *tftp,
33 const void *pkt, size_t len)
35 com32sys_t ireg, oreg;
36 t_PXENV_UDP_WRITE *uw;
37 t_PXENV_UDP_READ *ur;
38 clock_t start;
39 static const clock_t timeouts[] = {
40 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31,
41 37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0
43 const clock_t *timeout;
44 int err = -1;
46 uw = lmalloc(sizeof *uw + len);
47 ur = lmalloc(sizeof *ur + RCV_BUF);
49 memset(&ireg, 0, sizeof ireg);
50 ireg.eax.w[0] = 0x0009;
52 for (timeout = timeouts ; *timeout ; timeout++) {
53 memset(uw, 0, sizeof uw);
54 memcpy(uw+1, pkt, len);
55 uw->ip = tftp->srv_ip;
56 uw->gw = tftp->srv_gw;
57 uw->src_port = tftp->my_port;
58 uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69);
59 uw->buffer_size = len;
60 uw->buffer = FAR_PTR(uw+1);
62 ireg.ebx.w[0] = PXENV_UDP_WRITE;
63 ireg.es = SEG(uw);
64 ireg.edi.w[0] = OFFS(uw);
66 __intcall(0x22, &ireg, &oreg);
68 start = times(NULL);
70 do {
71 memset(ur, 0, sizeof ur);
72 ur->src_ip = tftp->srv_ip;
73 ur->dest_ip = tftp->my_ip;
74 ur->s_port = tftp->srv_port;
75 ur->d_port = tftp->my_port;
76 ur->buffer_size = RCV_BUF;
77 ur->buffer = FAR_PTR(ur+1);
79 ireg.ebx.w[0] = PXENV_UDP_READ;
80 ireg.es = SEG(ur);
81 ireg.edi.w[0] = OFFS(ur);
82 __intcall(0x22, &ireg, &oreg);
84 if (!(oreg.eflags.l & EFLAGS_CF) &&
85 ur->status == PXENV_STATUS_SUCCESS &&
86 tftp->srv_ip == ur->src_ip &&
87 (tftp->srv_port == 0 ||
88 tftp->srv_port == ur->s_port)) {
89 uint16_t *xb = (uint16_t *)(ur+1);
90 if (ntohs(xb[0]) == TFTP_ACK &&
91 ntohs(xb[1]) == tftp->seq) {
92 tftp->srv_port = ur->s_port;
93 err = 0; /* All good! */
94 goto done;
95 } else if (ntohs(xb[1]) == TFTP_ERROR) {
96 goto done;
99 } while ((clock_t)(times(NULL) - start) < *timeout);
102 done:
103 lfree(ur);
104 lfree(uw);
106 return err;
109 static int be_tftp_write(struct backend *be)
111 static uint16_t local_port = 0x4000;
112 struct tftp_state tftp;
113 char buffer[512+4+6];
114 int nlen;
115 const union syslinux_derivative_info *sdi =
116 syslinux_derivative_info();
117 const char *data = be->outbuf;
118 size_t len = be->zbytes;
119 size_t chunk;
121 tftp.my_ip = sdi->pxe.myip;
122 tftp.my_port = htons(local_port++);
123 tftp.srv_ip = pxe_dns(be->argv[1]);
124 tftp.srv_gw = ((tftp.srv_ip ^ tftp.my_ip) & sdi->pxe.ipinfo->netmask)
125 ? sdi->pxe.ipinfo->gateway : 0;
126 tftp.srv_port = 0;
127 tftp.seq = 0;
129 buffer[0] = 0;
130 buffer[1] = TFTP_WRQ;
131 nlen = strlcpy(buffer+2, be->argv[0], 512);
132 memcpy(buffer+3+nlen, "octet", 6);
134 if (send_ack_packet(&tftp, buffer, 2+nlen+1+6))
135 return -1;
137 do {
138 chunk = len >= 512 ? 512 : len;
140 buffer[1] = TFTP_DATA;
141 *((uint16_t *)(buffer+2)) = htons(++tftp.seq);
142 memcpy(buffer+4, data, chunk);
143 data += chunk;
144 len -= chunk;
146 if (send_ack_packet(&tftp, buffer, chunk+4))
147 return -1;
148 } while (chunk == 512);
150 return 0;
153 struct backend be_tftp = {
154 .name = "tftp",
155 .helpmsg = "filename tftp_server",
156 .minargs = 2,
157 .write = be_tftp_write,