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
send_ack_packet(struct tftp_state * tftp,const void * pkt,size_t len)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
upload_tftp_write(struct upload_backend * be)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