Add default Smack manifest for syslinux.spec
[external/syslinux.git] / com32 / sysdump / be_tftp.c
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
12 #include "backend.h"
13
14 enum tftp_opcode {
15     TFTP_RRQ    = 1,
16     TFTP_WRQ    = 2,
17     TFTP_DATA   = 3,
18     TFTP_ACK    = 4,
19     TFTP_ERROR  = 5,
20 };
21
22 struct tftp_state {
23     uint32_t my_ip;
24     uint32_t srv_ip;
25     uint32_t srv_gw;
26     uint16_t my_port;
27     uint16_t srv_port;
28     uint16_t seq;
29 };
30
31 #define RCV_BUF 2048
32
33 static int send_ack_packet(struct tftp_state *tftp,
34                            const void *pkt, size_t len)
35 {
36     com32sys_t ireg, oreg;
37     t_PXENV_UDP_WRITE *uw;
38     t_PXENV_UDP_READ  *ur;
39     clock_t start;
40     static const clock_t timeouts[] = {
41         2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31,
42         37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0
43     };
44     const clock_t *timeout;
45     int err = -1;
46
47     uw = lmalloc(sizeof *uw + len);
48     ur = lmalloc(sizeof *ur + RCV_BUF);
49
50     memset(&ireg, 0, sizeof ireg);
51     ireg.eax.w[0] = 0x0009;
52
53     for (timeout = timeouts ; *timeout ; timeout++) {
54         memset(uw, 0, sizeof uw);
55         memcpy(uw+1, pkt, len);
56         uw->ip = tftp->srv_ip;
57         uw->gw = tftp->srv_gw;
58         uw->src_port = tftp->my_port;
59         uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69);
60         uw->buffer_size = len;
61         uw->buffer = FAR_PTR(uw+1);
62
63         ireg.ebx.w[0] = PXENV_UDP_WRITE;
64         ireg.es = SEG(uw);
65         ireg.edi.w[0] = OFFS(uw);
66
67         __intcall(0x22, &ireg, &oreg);
68
69         start = times(NULL);
70
71         do {
72             memset(ur, 0, sizeof ur);
73             ur->src_ip = tftp->srv_ip;
74             ur->dest_ip = tftp->my_ip;
75             ur->s_port = tftp->srv_port;
76             ur->d_port = tftp->my_port;
77             ur->buffer_size = RCV_BUF;
78             ur->buffer = FAR_PTR(ur+1);
79
80             ireg.ebx.w[0] = PXENV_UDP_READ;
81             ireg.es = SEG(ur);
82             ireg.edi.w[0] = OFFS(ur);
83             __intcall(0x22, &ireg, &oreg);
84
85             if (!(oreg.eflags.l & EFLAGS_CF) &&
86                 ur->status == PXENV_STATUS_SUCCESS &&
87                 tftp->srv_ip == ur->src_ip &&
88                 (tftp->srv_port == 0 ||
89                  tftp->srv_port == ur->s_port)) {
90                 uint16_t *xb = (uint16_t *)(ur+1);
91                 if (ntohs(xb[0]) == TFTP_ACK &&
92                     ntohs(xb[1]) == tftp->seq) {
93                     tftp->srv_port = ur->s_port;
94                     err = 0;            /* All good! */
95                     goto done;
96                 } else if (ntohs(xb[1]) == TFTP_ERROR) {
97                     goto done;
98                 }
99             }
100         } while ((clock_t)(times(NULL) - start) < *timeout);
101     }
102
103 done:
104     lfree(ur);
105     lfree(uw);
106
107     return err;
108 }
109
110 static int be_tftp_write(struct backend *be)
111 {
112     static uint16_t local_port = 0x4000;
113     struct tftp_state tftp;
114     char buffer[512+4+6];
115     int nlen;
116     const union syslinux_derivative_info *sdi =
117         syslinux_derivative_info();
118     const char *data = be->outbuf;
119     size_t len = be->zbytes;
120     size_t chunk;
121
122     tftp.my_ip    = sdi->pxe.myip;
123     tftp.my_port  = htons(local_port++);
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;
128
129     if (be->argv[1]) {
130         tftp.srv_ip   = pxe_dns(be->argv[1]);
131         if (!tftp.srv_ip) {
132             printf("\nUnable to resolve hostname: %s\n", be->argv[1]);
133             return -1;
134         }
135     } else {
136         tftp.srv_ip   = sdi->pxe.ipinfo->serverip;
137         if (!tftp.srv_ip) {
138             printf("\nNo server IP address\n");
139             return -1;
140         }
141     }
142
143     printf("server %u.%u.%u.%u... ",
144            ((uint8_t *)&tftp.srv_ip)[0],
145            ((uint8_t *)&tftp.srv_ip)[1],
146            ((uint8_t *)&tftp.srv_ip)[2],
147            ((uint8_t *)&tftp.srv_ip)[3]);
148
149     buffer[0] = 0;
150     buffer[1] = TFTP_WRQ;
151     nlen = strlcpy(buffer+2, be->argv[0], 512);
152     memcpy(buffer+3+nlen, "octet", 6);
153
154     if (send_ack_packet(&tftp, buffer, 2+nlen+1+6))
155         return -1;
156
157     do {
158         chunk = len >= 512 ? 512 : len;
159
160         buffer[1] = TFTP_DATA;
161         *((uint16_t *)(buffer+2)) = htons(++tftp.seq);
162         memcpy(buffer+4, data, chunk);
163         data += chunk;
164         len -= chunk;
165
166         if (send_ack_packet(&tftp, buffer, chunk+4))
167             return -1;
168     } while (chunk == 512);
169
170     return 0;
171 }
172
173 struct backend be_tftp = {
174     .name       = "tftp",
175     .helpmsg    = "filename [tftp_server]",
176     .minargs    = 1,
177     .write      = be_tftp_write,
178 };