1 #include <minmax.h>
2 #include <net.h>
3 #include "pxe.h"
4 #include "url.h"
5 #include "tftp.h"
6
7 const uint8_t TimeoutTable[] = {
8 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
9 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
10 };
11 struct tftp_packet {
12 uint16_t opcode;
13 uint16_t serial;
14 char data[];
15 };
16
17 static void tftp_error(struct inode *file, uint16_t errnum,
18 const char *errstr);
19
tftp_close_file(struct inode * inode)20 static void tftp_close_file(struct inode *inode)
21 {
22 struct pxe_pvt_inode *socket = PVT(inode);
23 if (!socket->tftp_goteof) {
24 tftp_error(inode, 0, "No error, file close");
25 }
26 core_udp_close(socket);
27 }
28
29 /**
30 * Send an ERROR packet. This is used to terminate a connection.
31 *
32 * @inode: Inode structure
33 * @errnum: Error number (network byte order)
34 * @errstr: Error string (included in packet)
35 */
tftp_error(struct inode * inode,uint16_t errnum,const char * errstr)36 static void tftp_error(struct inode *inode, uint16_t errnum,
37 const char *errstr)
38 {
39 static struct {
40 uint16_t err_op;
41 uint16_t err_num;
42 char err_msg[64];
43 } __packed err_buf;
44 int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
45 struct pxe_pvt_inode *socket = PVT(inode);
46
47 err_buf.err_op = TFTP_ERROR;
48 err_buf.err_num = errnum;
49 memcpy(err_buf.err_msg, errstr, len);
50 err_buf.err_msg[len] = '\0';
51
52 core_udp_send(socket, &err_buf, 4 + len + 1);
53 }
54
55 /**
56 * Send ACK packet. This is a common operation and so is worth canning.
57 *
58 * @param: inode, Inode pointer
59 * @param: ack_num, Packet # to ack (host byte order)
60 *
61 */
ack_packet(struct inode * inode,uint16_t ack_num)62 static void ack_packet(struct inode *inode, uint16_t ack_num)
63 {
64 static uint16_t ack_packet_buf[2];
65 struct pxe_pvt_inode *socket = PVT(inode);
66
67 /* Packet number to ack */
68 ack_packet_buf[0] = TFTP_ACK;
69 ack_packet_buf[1] = htons(ack_num);
70
71 core_udp_send(socket, ack_packet_buf, 4);
72 }
73
74 /*
75 * Get a fresh packet if the buffer is drained, and we haven't hit
76 * EOF yet. The buffer should be filled immediately after draining!
77 */
tftp_get_packet(struct inode * inode)78 static void tftp_get_packet(struct inode *inode)
79 {
80 uint16_t last_pkt;
81 const uint8_t *timeout_ptr;
82 uint8_t timeout;
83 uint16_t buffersize;
84 uint16_t serial;
85 jiffies_t oldtime;
86 struct tftp_packet *pkt = NULL;
87 uint16_t buf_len;
88 struct pxe_pvt_inode *socket = PVT(inode);
89 uint16_t src_port;
90 uint32_t src_ip;
91 int err;
92
93 /*
94 * Start by ACKing the previous packet; this should cause
95 * the next packet to be sent.
96 */
97 timeout_ptr = TimeoutTable;
98 timeout = *timeout_ptr++;
99 oldtime = jiffies();
100
101 ack_again:
102 ack_packet(inode, socket->tftp_lastpkt);
103
104 while (timeout) {
105 buf_len = socket->tftp_blksize + 4;
106 err = core_udp_recv(socket, socket->tftp_pktbuf, &buf_len,
107 &src_ip, &src_port);
108 if (err) {
109 jiffies_t now = jiffies();
110
111 if (now-oldtime >= timeout) {
112 oldtime = now;
113 timeout = *timeout_ptr++;
114 if (!timeout)
115 break;
116 goto ack_again;
117 }
118 continue;
119 }
120
121 if (buf_len < 4) /* Bad size for a DATA packet */
122 continue;
123
124 pkt = (struct tftp_packet *)(socket->tftp_pktbuf);
125 if (pkt->opcode != TFTP_DATA) /* Not a data packet */
126 continue;
127
128 /* If goes here, recevie OK, break */
129 break;
130 }
131
132 /* time runs out */
133 if (timeout == 0)
134 kaboom();
135
136 last_pkt = socket->tftp_lastpkt;
137 last_pkt++;
138 serial = ntohs(pkt->serial);
139 if (serial != last_pkt) {
140 /*
141 * Wrong packet, ACK the packet and try again.
142 * This is presumably because the ACK got lost,
143 * so the server just resent the previous packet.
144 */
145 #if 0
146 printf("Wrong packet, wanted %04x, got %04x\n", \
147 htons(last_pkt), htons(*(uint16_t *)(data+2)));
148 #endif
149 goto ack_again;
150 }
151
152 /* It's the packet we want. We're also EOF if the size < blocksize */
153 socket->tftp_lastpkt = last_pkt; /* Update last packet number */
154 buffersize = buf_len - 4; /* Skip TFTP header */
155 socket->tftp_dataptr = socket->tftp_pktbuf + 4;
156 socket->tftp_filepos += buffersize;
157 socket->tftp_bytesleft = buffersize;
158 if (buffersize < socket->tftp_blksize) {
159 /* it's the last block, ACK packet immediately */
160 ack_packet(inode, serial);
161
162 /* Make sure we know we are at end of file */
163 inode->size = socket->tftp_filepos;
164 socket->tftp_goteof = 1;
165 tftp_close_file(inode);
166 }
167 }
168
169 const struct pxe_conn_ops tftp_conn_ops = {
170 .fill_buffer = tftp_get_packet,
171 .close = tftp_close_file,
172 };
173
174 /**
175 * Open a TFTP connection to the server
176 *
177 * @param:inode, the inode to store our state in
178 * @param:ip, the ip to contact to get the file
179 * @param:filename, the file we wanna open
180 *
181 * @out: open_file_t structure, stores in file->open_file
182 * @out: the lenght of this file, stores in file->file_len
183 *
184 */
tftp_open(struct url_info * url,int flags,struct inode * inode,const char ** redir)185 void tftp_open(struct url_info *url, int flags, struct inode *inode,
186 const char **redir)
187 {
188 struct pxe_pvt_inode *socket = PVT(inode);
189 char *buf;
190 uint16_t buf_len;
191 char *p;
192 char *options;
193 char *data;
194 static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
195 char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
196 char reply_packet_buf[PKTBUF_SIZE];
197 int err;
198 int buffersize;
199 int rrq_len;
200 const uint8_t *timeout_ptr;
201 jiffies_t timeout;
202 jiffies_t oldtime;
203 uint16_t opcode;
204 uint16_t blk_num;
205 uint64_t opdata;
206 uint16_t src_port;
207 uint32_t src_ip;
208
209 (void)redir; /* TFTP does not redirect */
210 (void)flags;
211
212 if (url->type != URL_OLD_TFTP) {
213 /*
214 * The TFTP URL specification allows the TFTP to end with a
215 * ;mode= which we just ignore.
216 */
217 url_unescape(url->path, ';');
218 }
219
220 if (!url->port)
221 url->port = TFTP_PORT;
222
223 socket->ops = &tftp_conn_ops;
224 if (core_udp_open(socket))
225 return;
226
227 buf = rrq_packet_buf;
228 *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
229 buf += 2;
230
231 buf = stpcpy(buf, url->path);
232
233 buf++; /* Point *past* the final NULL */
234 memcpy(buf, rrq_tail, sizeof rrq_tail);
235 buf += sizeof rrq_tail;
236
237 rrq_len = buf - rrq_packet_buf;
238
239 timeout_ptr = TimeoutTable; /* Reset timeout */
240 sendreq:
241 timeout = *timeout_ptr++;
242 if (!timeout)
243 return; /* No file available... */
244 oldtime = jiffies();
245
246 core_udp_sendto(socket, rrq_packet_buf, rrq_len, url->ip, url->port);
247
248 /* If the WRITE call fails, we let the timeout take care of it... */
249 wait_pkt:
250 for (;;) {
251 buf_len = sizeof(reply_packet_buf);
252
253 err = core_udp_recv(socket, reply_packet_buf, &buf_len,
254 &src_ip, &src_port);
255 if (err) {
256 jiffies_t now = jiffies();
257 if (now - oldtime >= timeout)
258 goto sendreq;
259 } else {
260 /* Make sure the packet actually came from the server and
261 is long enough for a TFTP opcode */
262 dprintf("tftp_open: got packet buflen=%d\n", buf_len);
263 if ((src_ip == url->ip) && (buf_len >= 2))
264 break;
265 }
266 }
267
268 core_udp_disconnect(socket);
269 core_udp_connect(socket, src_ip, src_port);
270
271 /* filesize <- -1 == unknown */
272 inode->size = -1;
273 socket->tftp_blksize = TFTP_BLOCKSIZE;
274 buffersize = buf_len - 2; /* bytes after opcode */
275
276 /*
277 * Get the opcode type, and parse it
278 */
279 opcode = *(uint16_t *)reply_packet_buf;
280 switch (opcode) {
281 case TFTP_ERROR:
282 inode->size = 0;
283 goto done; /* ERROR reply; don't try again */
284
285 case TFTP_DATA:
286 /*
287 * If the server doesn't support any options, we'll get a
288 * DATA reply instead of OACK. Stash the data in the file
289 * buffer and go with the default value for all options...
290 *
291 * We got a DATA packet, meaning no options are
292 * suported. Save the data away and consider the
293 * length undefined, *unless* this is the only
294 * data packet...
295 */
296 buffersize -= 2;
297 if (buffersize < 0)
298 goto wait_pkt;
299 data = reply_packet_buf + 2;
300 blk_num = ntohs(*(uint16_t *)data);
301 data += 2;
302 if (blk_num != 1)
303 goto wait_pkt;
304 socket->tftp_lastpkt = blk_num;
305 if (buffersize > TFTP_BLOCKSIZE)
306 goto err_reply; /* Corrupt */
307
308 socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE + 4);
309 if (!socket->tftp_pktbuf)
310 goto err_reply; /* Internal error */
311
312 if (buffersize < TFTP_BLOCKSIZE) {
313 /*
314 * This is the final EOF packet, already...
315 * We know the filesize, but we also want to
316 * ack the packet and set the EOF flag.
317 */
318 inode->size = buffersize;
319 socket->tftp_goteof = 1;
320 ack_packet(inode, blk_num);
321 }
322
323 socket->tftp_bytesleft = buffersize;
324 socket->tftp_dataptr = socket->tftp_pktbuf;
325 memcpy(socket->tftp_pktbuf, data, buffersize);
326 goto done;
327
328 case TFTP_OACK:
329 /*
330 * Now we need to parse the OACK packet to get the transfer
331 * and packet sizes.
332 */
333
334 options = reply_packet_buf + 2;
335 p = options;
336
337 while (buffersize) {
338 const char *opt = p;
339
340 /*
341 * If we find an option which starts with a NUL byte,
342 * (a null option), we're either seeing garbage that some
343 * TFTP servers add to the end of the packet, or we have
344 * no clue how to parse the rest of the packet (what is
345 * an option name and what is a value?) In either case,
346 * discard the rest.
347 */
348 if (!*opt)
349 goto done;
350
351 while (buffersize) {
352 if (!*p)
353 break; /* Found a final null */
354 *p++ |= 0x20;
355 buffersize--;
356 }
357 if (!buffersize)
358 break; /* Unterminated option */
359
360 /* Consume the terminal null */
361 p++;
362 buffersize--;
363
364 if (!buffersize)
365 break; /* No option data */
366
367 opdata = 0;
368
369 /* do convert a number-string to decimal number, just like atoi */
370 while (buffersize--) {
371 uint8_t d = *p++;
372 if (d == '\0')
373 break; /* found a final null */
374 d -= '0';
375 if (d > 9)
376 goto err_reply; /* Not a decimal digit */
377 opdata = opdata*10 + d;
378 }
379
380 if (!strcmp(opt, "tsize"))
381 inode->size = opdata;
382 else if (!strcmp(opt, "blksize"))
383 socket->tftp_blksize = opdata;
384 else
385 goto err_reply; /* Non-negotitated option returned,
386 no idea what it means ...*/
387
388
389 }
390
391 if (socket->tftp_blksize < 64 || socket->tftp_blksize > PKTBUF_SIZE)
392 goto err_reply;
393
394 /* Parsing successful, allocate buffer */
395 socket->tftp_pktbuf = malloc(socket->tftp_blksize + 4);
396 if (!socket->tftp_pktbuf)
397 goto err_reply;
398 else
399 goto done;
400
401 default:
402 printf("TFTP unknown opcode %d\n", ntohs(opcode));
403 goto err_reply;
404 }
405
406 err_reply:
407 /* Build the TFTP error packet */
408 tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
409 inode->size = 0;
410
411 done:
412 if (!inode->size)
413 core_udp_close(socket);
414
415 return;
416 }
417