Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / net / tftp.c
blob9c70efbec867e1348560691bc20215c820548d42
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010,2011 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/misc.h>
20 #include <grub/net/udp.h>
21 #include <grub/net/ip.h>
22 #include <grub/net/ethernet.h>
23 #include <grub/net/netbuff.h>
24 #include <grub/net.h>
25 #include <grub/mm.h>
26 #include <grub/dl.h>
27 #include <grub/file.h>
28 #include <grub/priority_queue.h>
29 #include <grub/i18n.h>
31 GRUB_MOD_LICENSE ("GPLv3+");
33 /* IP port for the MTFTP server used for Intel's PXE */
34 enum
36 MTFTP_SERVER_PORT = 75,
37 MTFTP_CLIENT_PORT = 76,
38 /* IP port for the TFTP server */
39 TFTP_SERVER_PORT = 69
42 enum
44 TFTP_DEFAULTSIZE_PACKET = 512,
47 enum
49 TFTP_CODE_EOF = 1,
50 TFTP_CODE_MORE = 2,
51 TFTP_CODE_ERROR = 3,
52 TFTP_CODE_BOOT = 4,
53 TFTP_CODE_CFG = 5
56 enum
58 TFTP_RRQ = 1,
59 TFTP_WRQ = 2,
60 TFTP_DATA = 3,
61 TFTP_ACK = 4,
62 TFTP_ERROR = 5,
63 TFTP_OACK = 6
66 enum
68 TFTP_EUNDEF = 0, /* not defined */
69 TFTP_ENOTFOUND = 1, /* file not found */
70 TFTP_EACCESS = 2, /* access violation */
71 TFTP_ENOSPACE = 3, /* disk full or allocation exceeded */
72 TFTP_EBADOP = 4, /* illegal TFTP operation */
73 TFTP_EBADID = 5, /* unknown transfer ID */
74 TFTP_EEXISTS = 6, /* file already exists */
75 TFTP_ENOUSER = 7 /* no such user */
78 struct tftphdr {
79 grub_uint16_t opcode;
80 union {
81 grub_int8_t rrq[TFTP_DEFAULTSIZE_PACKET];
82 struct {
83 grub_uint16_t block;
84 grub_int8_t download[0];
85 } data;
86 struct {
87 grub_uint16_t block;
88 } ack;
89 struct {
90 grub_uint16_t errcode;
91 grub_int8_t errmsg[TFTP_DEFAULTSIZE_PACKET];
92 } err;
93 struct {
94 grub_int8_t data[TFTP_DEFAULTSIZE_PACKET+2];
95 } oack;
96 } u;
97 } __attribute__ ((packed)) ;
100 typedef struct tftp_data
102 grub_uint64_t file_size;
103 grub_uint64_t block;
104 grub_uint32_t block_size;
105 grub_uint32_t ack_sent;
106 int have_oack;
107 struct grub_error_saved save_err;
108 grub_net_udp_socket_t sock;
109 grub_priority_queue_t pq;
110 } *tftp_data_t;
112 static int
113 cmp (const void *a__, const void *b__)
115 struct grub_net_buff *a_ = *(struct grub_net_buff **) a__;
116 struct grub_net_buff *b_ = *(struct grub_net_buff **) b__;
117 struct tftphdr *a = (struct tftphdr *) a_->data;
118 struct tftphdr *b = (struct tftphdr *) b_->data;
119 /* We want the first elements to be on top. */
120 if (grub_be_to_cpu16 (a->u.data.block) < grub_be_to_cpu16 (b->u.data.block))
121 return +1;
122 if (grub_be_to_cpu16 (a->u.data.block) > grub_be_to_cpu16 (b->u.data.block))
123 return -1;
124 return 0;
127 static grub_err_t
128 ack (tftp_data_t data, grub_uint16_t block)
130 struct tftphdr *tftph_ack;
131 grub_uint8_t nbdata[512];
132 struct grub_net_buff nb_ack;
133 grub_err_t err;
135 nb_ack.head = nbdata;
136 nb_ack.end = nbdata + sizeof (nbdata);
137 grub_netbuff_clear (&nb_ack);
138 grub_netbuff_reserve (&nb_ack, 512);
139 err = grub_netbuff_push (&nb_ack, sizeof (tftph_ack->opcode)
140 + sizeof (tftph_ack->u.ack.block));
141 if (err)
142 return err;
144 tftph_ack = (struct tftphdr *) nb_ack.data;
145 tftph_ack->opcode = grub_cpu_to_be16 (TFTP_ACK);
146 tftph_ack->u.ack.block = block;
148 err = grub_net_send_udp_packet (data->sock, &nb_ack);
149 if (err)
150 return err;
151 data->ack_sent = block;
152 return GRUB_ERR_NONE;
155 static grub_err_t
156 tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)),
157 struct grub_net_buff *nb,
158 void *f)
160 grub_file_t file = f;
161 struct tftphdr *tftph = (void *) nb->data;
162 tftp_data_t data = file->data;
163 grub_err_t err;
164 grub_uint8_t *ptr;
166 if (nb->tail - nb->data < (grub_ssize_t) sizeof (tftph->opcode))
168 grub_dprintf ("tftp", "TFTP packet too small\n");
169 return GRUB_ERR_NONE;
172 tftph = (struct tftphdr *) nb->data;
173 switch (grub_be_to_cpu16 (tftph->opcode))
175 case TFTP_OACK:
176 data->block_size = TFTP_DEFAULTSIZE_PACKET;
177 data->have_oack = 1;
178 for (ptr = nb->data + sizeof (tftph->opcode); ptr < nb->tail;)
180 if (grub_memcmp (ptr, "tsize\0", sizeof ("tsize\0") - 1) == 0)
181 data->file_size = grub_strtoul ((char *) ptr + sizeof ("tsize\0")
182 - 1, 0, 0);
183 if (grub_memcmp (ptr, "blksize\0", sizeof ("blksize\0") - 1) == 0)
184 data->block_size = grub_strtoul ((char *) ptr + sizeof ("blksize\0")
185 - 1, 0, 0);
186 while (ptr < nb->tail && *ptr)
187 ptr++;
188 ptr++;
190 data->block = 0;
191 grub_netbuff_free (nb);
192 err = ack (data, 0);
193 grub_error_save (&data->save_err);
194 return GRUB_ERR_NONE;
195 case TFTP_DATA:
196 if (nb->tail - nb->data < (grub_ssize_t) (sizeof (tftph->opcode)
197 + sizeof (tftph->u.data.block)))
199 grub_dprintf ("tftp", "TFTP packet too small\n");
200 return GRUB_ERR_NONE;
203 err = grub_priority_queue_push (data->pq, &nb);
204 if (err)
205 return err;
208 struct grub_net_buff **nb_top_p, *nb_top;
209 while (1)
211 nb_top_p = grub_priority_queue_top (data->pq);
212 if (!nb_top_p)
213 return GRUB_ERR_NONE;
214 nb_top = *nb_top_p;
215 tftph = (struct tftphdr *) nb_top->data;
216 if (grub_be_to_cpu16 (tftph->u.data.block) >= data->block + 1)
217 break;
218 grub_netbuff_free (nb_top);
219 grub_priority_queue_pop (data->pq);
221 if (grub_be_to_cpu16 (tftph->u.data.block) == data->block + 1)
223 unsigned size;
225 grub_priority_queue_pop (data->pq);
227 if (file->device->net->packs.count < 50)
228 err = ack (data, tftph->u.data.block);
229 else
231 file->device->net->stall = 1;
232 err = 0;
234 if (err)
235 return err;
237 err = grub_netbuff_pull (nb_top, sizeof (tftph->opcode) +
238 sizeof (tftph->u.data.block));
239 if (err)
240 return err;
241 size = nb_top->tail - nb_top->data;
243 data->block++;
244 if (size < data->block_size)
246 file->device->net->eof = 1;
247 file->device->net->stall = 1;
248 grub_net_udp_close (data->sock);
249 data->sock = NULL;
251 /* Prevent garbage in broken cards. Is it still necessary
252 given that IP implementation has been fixed?
254 if (size > data->block_size)
256 err = grub_netbuff_unput (nb_top, size - data->block_size);
257 if (err)
258 return err;
260 /* If there is data, puts packet in socket list. */
261 if ((nb_top->tail - nb_top->data) > 0)
262 grub_net_put_packet (&file->device->net->packs, nb_top);
263 else
264 grub_netbuff_free (nb_top);
267 return GRUB_ERR_NONE;
268 case TFTP_ERROR:
269 data->have_oack = 1;
270 grub_netbuff_free (nb);
271 grub_error (GRUB_ERR_IO, (char *) tftph->u.err.errmsg);
272 grub_error_save (&data->save_err);
273 return GRUB_ERR_NONE;
274 default:
275 grub_netbuff_free (nb);
276 return GRUB_ERR_NONE;
280 static void
281 destroy_pq (tftp_data_t data)
283 struct grub_net_buff **nb_p;
284 while ((nb_p = grub_priority_queue_top (data->pq)))
286 grub_netbuff_free (*nb_p);
287 grub_priority_queue_pop (data->pq);
290 grub_priority_queue_destroy (data->pq);
293 static grub_err_t
294 tftp_open (struct grub_file *file, const char *filename)
296 struct tftphdr *tftph;
297 char *rrq;
298 int i;
299 int rrqlen;
300 int hdrlen;
301 grub_uint8_t open_data[1500];
302 struct grub_net_buff nb;
303 tftp_data_t data;
304 grub_err_t err;
305 grub_uint8_t *nbd;
306 grub_net_network_level_address_t addr;
308 data = grub_zalloc (sizeof (*data));
309 if (!data)
310 return grub_errno;
312 nb.head = open_data;
313 nb.end = open_data + sizeof (open_data);
314 grub_netbuff_clear (&nb);
316 grub_netbuff_reserve (&nb, 1500);
317 err = grub_netbuff_push (&nb, sizeof (*tftph));
318 if (err)
319 return err;
321 tftph = (struct tftphdr *) nb.data;
323 rrq = (char *) tftph->u.rrq;
324 rrqlen = 0;
326 tftph->opcode = grub_cpu_to_be16 (TFTP_RRQ);
327 grub_strcpy (rrq, filename);
328 rrqlen += grub_strlen (filename) + 1;
329 rrq += grub_strlen (filename) + 1;
331 grub_strcpy (rrq, "octet");
332 rrqlen += grub_strlen ("octet") + 1;
333 rrq += grub_strlen ("octet") + 1;
335 grub_strcpy (rrq, "blksize");
336 rrqlen += grub_strlen ("blksize") + 1;
337 rrq += grub_strlen ("blksize") + 1;
339 grub_strcpy (rrq, "1024");
340 rrqlen += grub_strlen ("1024") + 1;
341 rrq += grub_strlen ("1024") + 1;
343 grub_strcpy (rrq, "tsize");
344 rrqlen += grub_strlen ("tsize") + 1;
345 rrq += grub_strlen ("tsize") + 1;
347 grub_strcpy (rrq, "0");
348 rrqlen += grub_strlen ("0") + 1;
349 rrq += grub_strlen ("0") + 1;
350 hdrlen = sizeof (tftph->opcode) + rrqlen;
352 err = grub_netbuff_unput (&nb, nb.tail - (nb.data + hdrlen));
353 if (err)
354 return err;
356 file->not_easily_seekable = 1;
357 file->data = data;
359 data->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp);
360 if (!data->pq)
361 return grub_errno;
363 err = grub_net_resolve_address (file->device->net->server, &addr);
364 if (err)
366 destroy_pq (data);
367 return err;
370 data->sock = grub_net_udp_open (addr,
371 TFTP_SERVER_PORT, tftp_receive,
372 file);
373 if (!data->sock)
375 destroy_pq (data);
376 return grub_errno;
379 /* Receive OACK packet. */
380 nbd = nb.data;
381 for (i = 0; i < GRUB_NET_TRIES; i++)
383 nb.data = nbd;
384 err = grub_net_send_udp_packet (data->sock, &nb);
385 if (err)
387 grub_net_udp_close (data->sock);
388 destroy_pq (data);
389 return err;
391 grub_net_poll_cards (GRUB_NET_INTERVAL, &data->have_oack);
392 if (data->have_oack)
393 break;
396 if (!data->have_oack)
397 grub_error (GRUB_ERR_TIMEOUT, N_("time out opening `%s'"), filename);
398 else
399 grub_error_load (&data->save_err);
400 if (grub_errno)
402 grub_net_udp_close (data->sock);
403 destroy_pq (data);
404 return grub_errno;
407 file->size = data->file_size;
409 return GRUB_ERR_NONE;
412 static grub_err_t
413 tftp_close (struct grub_file *file)
415 tftp_data_t data = file->data;
417 if (data->sock)
419 grub_uint8_t nbdata[512];
420 grub_err_t err;
421 struct grub_net_buff nb_err;
422 struct tftphdr *tftph;
424 nb_err.head = nbdata;
425 nb_err.end = nbdata + sizeof (nbdata);
427 grub_netbuff_clear (&nb_err);
428 grub_netbuff_reserve (&nb_err, 512);
429 err = grub_netbuff_push (&nb_err, sizeof (tftph->opcode)
430 + sizeof (tftph->u.err.errcode)
431 + sizeof ("closed"));
432 if (!err)
434 tftph = (struct tftphdr *) nb_err.data;
435 tftph->opcode = grub_cpu_to_be16 (TFTP_ERROR);
436 tftph->u.err.errcode = grub_cpu_to_be16 (TFTP_EUNDEF);
437 grub_memcpy (tftph->u.err.errmsg, "closed", sizeof ("closed"));
439 err = grub_net_send_udp_packet (data->sock, &nb_err);
441 if (err)
442 grub_print_error ();
443 grub_net_udp_close (data->sock);
445 destroy_pq (data);
446 grub_free (data);
447 return GRUB_ERR_NONE;
450 static grub_err_t
451 tftp_packets_pulled (struct grub_file *file)
453 tftp_data_t data = file->data;
454 if (file->device->net->packs.count >= 50)
455 return 0;
457 if (!file->device->net->eof)
458 file->device->net->stall = 0;
459 if (data->ack_sent >= data->block)
460 return 0;
461 return ack (data, data->block);
464 static struct grub_net_app_protocol grub_tftp_protocol =
466 .name = "tftp",
467 .open = tftp_open,
468 .close = tftp_close,
469 .packets_pulled = tftp_packets_pulled
472 GRUB_MOD_INIT (tftp)
474 grub_net_app_level_register (&grub_tftp_protocol);
477 GRUB_MOD_FINI (tftp)
479 grub_net_app_level_unregister (&grub_tftp_protocol);