]>
Commit | Line | Data |
---|---|---|
7bb47706 MA |
1 | #include <grub/misc.h> |
2 | #include <grub/net/tftp.h> | |
3 | #include <grub/net/udp.h> | |
4 | #include <grub/net/ip.h> | |
5 | #include <grub/net/ethernet.h> | |
6 | #include <grub/net/netbuff.h> | |
7bb47706 MA |
7 | #include <grub/net.h> |
8 | #include <grub/mm.h> | |
9 | #include <grub/dl.h> | |
10 | #include <grub/file.h> | |
11 | ||
48ac061a MA |
12 | GRUB_MOD_LICENSE ("GPLv3+"); |
13 | ||
7bb47706 MA |
14 | static grub_err_t |
15 | tftp_open (struct grub_file *file, const char *filename) | |
16 | { | |
17 | struct tftphdr *tftph; | |
18 | char *rrq; | |
25f1579b | 19 | int i; |
7bb47706 | 20 | int rrqlen; |
25f1579b MRA |
21 | int hdrlen; |
22 | char open_data[1500]; | |
23 | struct grub_net_buff nb; | |
eea84144 | 24 | tftp_data_t data; |
7bb47706 | 25 | grub_err_t err; |
eea84144 VS |
26 | |
27 | data = grub_malloc (sizeof *data); | |
28 | if (!data) | |
29 | return grub_errno; | |
25f1579b MRA |
30 | |
31 | file->device->net->socket->data = (void *) data; | |
32 | nb.head = open_data; | |
33 | nb.end = open_data + sizeof (open_data); | |
34 | grub_netbuff_clear (&nb); | |
7bb47706 | 35 | |
25f1579b MRA |
36 | grub_netbuff_reserve (&nb,1500); |
37 | grub_netbuff_push (&nb,sizeof (*tftph)); | |
04d22ddd | 38 | |
25f1579b | 39 | tftph = (struct tftphdr *) nb.data; |
7bb47706 MA |
40 | |
41 | rrq = (char *) tftph->u.rrq; | |
42 | rrqlen = 0; | |
43 | ||
04d22ddd | 44 | tftph->opcode = grub_cpu_to_be16 (TFTP_RRQ); |
7bb47706 MA |
45 | grub_strcpy (rrq, filename); |
46 | rrqlen += grub_strlen (filename) + 1; | |
47 | rrq += grub_strlen (filename) + 1; | |
7bb47706 MA |
48 | |
49 | grub_strcpy (rrq,"octet"); | |
50 | rrqlen += grub_strlen ("octet") + 1; | |
51 | rrq += grub_strlen ("octet") + 1; | |
52 | ||
7bb47706 MA |
53 | grub_strcpy (rrq,"blksize"); |
54 | rrqlen += grub_strlen("blksize") + 1; | |
55 | rrq += grub_strlen ("blksize") + 1; | |
56 | ||
57 | grub_strcpy (rrq,"1024"); | |
58 | rrqlen += grub_strlen ("1024") + 1; | |
59 | rrq += grub_strlen ("1024") + 1; | |
60 | ||
61 | grub_strcpy (rrq,"tsize"); | |
62 | rrqlen += grub_strlen ("tsize") + 1; | |
63 | rrq += grub_strlen ("tsize") + 1; | |
64 | ||
65 | grub_strcpy (rrq,"0"); | |
66 | rrqlen += grub_strlen ("0") + 1; | |
04d22ddd | 67 | rrq += grub_strlen ("0") + 1; |
7bb47706 MA |
68 | hdrlen = sizeof (tftph->opcode) + rrqlen; |
69 | ||
25f1579b MRA |
70 | grub_netbuff_unput (&nb, nb.tail - (nb.data + hdrlen)); |
71 | ||
72 | file->device->net->socket->out_port = TFTP_SERVER_PORT; | |
04d22ddd | 73 | |
25f1579b | 74 | err = grub_net_send_udp_packet (file->device->net->socket, &nb); |
04d22ddd VS |
75 | if (err) |
76 | return err; | |
77 | ||
25f1579b MRA |
78 | /* Receive OACK packet. */ |
79 | for ( i = 0; i < 3; i++) | |
04d22ddd | 80 | { |
eea84144 | 81 | grub_net_poll_cards (100); |
25f1579b MRA |
82 | if (grub_errno) |
83 | return grub_errno; | |
84 | if (file->device->net->socket->status != 0) | |
85 | break; | |
86 | /* Retry. */ | |
423a1849 MRA |
87 | /*err = grub_net_send_udp_packet (file->device->net->socket, &nb); |
88 | if (err) | |
89 | return err;*/ | |
04d22ddd | 90 | } |
7bb47706 | 91 | |
25f1579b MRA |
92 | if (file->device->net->socket->status == 0) |
93 | return grub_error (GRUB_ERR_TIMEOUT,"Time out opening tftp."); | |
94 | file->size = data->file_size; | |
7bb47706 | 95 | |
04d22ddd | 96 | return GRUB_ERR_NONE; |
7bb47706 MA |
97 | } |
98 | ||
25f1579b MRA |
99 | static grub_err_t |
100 | tftp_receive (grub_net_socket_t sock, struct grub_net_buff *nb) | |
7bb47706 MA |
101 | { |
102 | struct tftphdr *tftph; | |
25f1579b MRA |
103 | char nbdata[128]; |
104 | tftp_data_t data = sock->data; | |
7bb47706 | 105 | grub_err_t err; |
25f1579b MRA |
106 | char *ptr; |
107 | struct grub_net_buff nb_ack; | |
108 | ||
109 | nb_ack.head = nbdata; | |
110 | nb_ack.end = nbdata + sizeof (nbdata); | |
7bb47706 | 111 | |
7bb47706 | 112 | |
25f1579b | 113 | tftph = (struct tftphdr *) nb->data; |
04d22ddd | 114 | switch (grub_be_to_cpu16 (tftph->opcode)) |
7bb47706 | 115 | { |
25f1579b | 116 | case TFTP_OACK: |
eea84144 | 117 | for (ptr = nb->data + sizeof (tftph->opcode); ptr < nb->tail; ) |
25f1579b MRA |
118 | { |
119 | if (grub_memcmp (ptr, "tsize\0", sizeof ("tsize\0") - 1) == 0) | |
eea84144 VS |
120 | { |
121 | data->file_size = grub_strtoul (ptr + sizeof ("tsize\0") - 1, | |
122 | 0, 0); | |
123 | } | |
25f1579b MRA |
124 | while (ptr < nb->tail && *ptr) |
125 | ptr++; | |
126 | ptr++; | |
127 | } | |
128 | sock->status = 1; | |
129 | data->block = 0; | |
130 | grub_netbuff_clear(nb); | |
131 | break; | |
7bb47706 | 132 | case TFTP_DATA: |
25f1579b MRA |
133 | grub_netbuff_pull (nb,sizeof (tftph->opcode) + sizeof (tftph->u.data.block)); |
134 | ||
135 | if (grub_be_to_cpu16 (tftph->u.data.block) == data->block + 1) | |
136 | { | |
137 | data->block++; | |
0696f08f MRA |
138 | unsigned size = nb->tail - nb->data; |
139 | if (size < 1024) | |
25f1579b | 140 | sock->status = 2; |
0696f08f MRA |
141 | /* Prevent garbage in broken cards. */ |
142 | if (size > 1024) | |
143 | grub_netbuff_unput (nb, size - 1024); | |
25f1579b MRA |
144 | } |
145 | else | |
146 | grub_netbuff_clear(nb); | |
147 | ||
148 | break; | |
7bb47706 | 149 | case TFTP_ERROR: |
25f1579b | 150 | grub_netbuff_clear (nb); |
04d22ddd | 151 | return grub_error (GRUB_ERR_IO, (char *)tftph->u.err.errmsg); |
25f1579b | 152 | break; |
7bb47706 | 153 | } |
25f1579b MRA |
154 | grub_netbuff_clear (&nb_ack); |
155 | grub_netbuff_reserve (&nb_ack,128); | |
156 | grub_netbuff_push (&nb_ack,sizeof (tftph->opcode) + sizeof (tftph->u.ack.block)); | |
7bb47706 | 157 | |
25f1579b | 158 | tftph = (struct tftphdr *) nb_ack.data; |
04d22ddd | 159 | tftph->opcode = grub_cpu_to_be16 (TFTP_ACK); |
eea84144 | 160 | tftph->u.ack.block = grub_cpu_to_be16 (data->block); |
7bb47706 | 161 | |
25f1579b MRA |
162 | err = grub_net_send_udp_packet (sock, &nb_ack); |
163 | return err; | |
7bb47706 MA |
164 | } |
165 | ||
166 | static grub_err_t | |
167 | tftp_close (struct grub_file *file __attribute__ ((unused))) | |
168 | { | |
31b5172b MRA |
169 | grub_free (file->device->net->socket->data); |
170 | return GRUB_ERR_NONE; | |
7bb47706 MA |
171 | } |
172 | ||
25f1579b | 173 | static struct grub_net_app_protocol grub_tftp_protocol = |
7bb47706 MA |
174 | { |
175 | .name = "tftp", | |
176 | .open = tftp_open, | |
177 | .read = tftp_receive, | |
178 | .close = tftp_close | |
179 | }; | |
180 | ||
181 | GRUB_MOD_INIT (tftp) | |
182 | { | |
183 | grub_net_app_level_register (&grub_tftp_protocol); | |
184 | } | |
185 | ||
186 | GRUB_MOD_FINI (tftp) | |
187 | { | |
188 | grub_net_app_level_unregister (&grub_tftp_protocol); | |
189 | } |