• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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