]> Git Repo - qemu.git/blob - slirp/tftp.c
fw_cfg: document ACPI device node information
[qemu.git] / slirp / tftp.c
1 /*
2  * tftp.c - a simple, read-only tftp server for qemu
3  *
4  * Copyright (c) 2004 Magnus Damm <[email protected]>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24
25 #include "qemu/osdep.h"
26 #include <slirp.h>
27 #include "qemu-common.h"
28
29 static inline int tftp_session_in_use(struct tftp_session *spt)
30 {
31     return (spt->slirp != NULL);
32 }
33
34 static inline void tftp_session_update(struct tftp_session *spt)
35 {
36     spt->timestamp = curtime;
37 }
38
39 static void tftp_session_terminate(struct tftp_session *spt)
40 {
41     if (spt->fd >= 0) {
42         close(spt->fd);
43         spt->fd = -1;
44     }
45     g_free(spt->filename);
46     spt->slirp = NULL;
47 }
48
49 static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
50 {
51   struct tftp_session *spt;
52   int k;
53
54   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
55     spt = &slirp->tftp_sessions[k];
56
57     if (!tftp_session_in_use(spt))
58         goto found;
59
60     /* sessions time out after 5 inactive seconds */
61     if ((int)(curtime - spt->timestamp) > 5000) {
62         tftp_session_terminate(spt);
63         goto found;
64     }
65   }
66
67   return -1;
68
69  found:
70   memset(spt, 0, sizeof(*spt));
71   memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
72   spt->fd = -1;
73   spt->client_port = tp->udp.uh_sport;
74   spt->slirp = slirp;
75
76   tftp_session_update(spt);
77
78   return k;
79 }
80
81 static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
82 {
83   struct tftp_session *spt;
84   int k;
85
86   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
87     spt = &slirp->tftp_sessions[k];
88
89     if (tftp_session_in_use(spt)) {
90       if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
91         if (spt->client_port == tp->udp.uh_sport) {
92           return k;
93         }
94       }
95     }
96   }
97
98   return -1;
99 }
100
101 static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr,
102                           uint8_t *buf, int len)
103 {
104     int bytes_read = 0;
105
106     if (spt->fd < 0) {
107         spt->fd = open(spt->filename, O_RDONLY | O_BINARY);
108     }
109
110     if (spt->fd < 0) {
111         return -1;
112     }
113
114     if (len) {
115         lseek(spt->fd, block_nr * 512, SEEK_SET);
116
117         bytes_read = read(spt->fd, buf, len);
118     }
119
120     return bytes_read;
121 }
122
123 static int tftp_send_oack(struct tftp_session *spt,
124                           const char *keys[], uint32_t values[], int nb,
125                           struct tftp_t *recv_tp)
126 {
127     struct sockaddr_in saddr, daddr;
128     struct mbuf *m;
129     struct tftp_t *tp;
130     int i, n = 0;
131
132     m = m_get(spt->slirp);
133
134     if (!m)
135         return -1;
136
137     memset(m->m_data, 0, m->m_size);
138
139     m->m_data += IF_MAXLINKHDR;
140     tp = (void *)m->m_data;
141     m->m_data += sizeof(struct udpiphdr);
142
143     tp->tp_op = htons(TFTP_OACK);
144     for (i = 0; i < nb; i++) {
145         n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
146                       keys[i]) + 1;
147         n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
148                       values[i]) + 1;
149     }
150
151     saddr.sin_addr = recv_tp->ip.ip_dst;
152     saddr.sin_port = recv_tp->udp.uh_dport;
153
154     daddr.sin_addr = spt->client_ip;
155     daddr.sin_port = spt->client_port;
156
157     m->m_len = sizeof(struct tftp_t) - 514 + n -
158         sizeof(struct ip) - sizeof(struct udphdr);
159     udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
160
161     return 0;
162 }
163
164 static void tftp_send_error(struct tftp_session *spt,
165                             uint16_t errorcode, const char *msg,
166                             struct tftp_t *recv_tp)
167 {
168   struct sockaddr_in saddr, daddr;
169   struct mbuf *m;
170   struct tftp_t *tp;
171
172   m = m_get(spt->slirp);
173
174   if (!m) {
175     goto out;
176   }
177
178   memset(m->m_data, 0, m->m_size);
179
180   m->m_data += IF_MAXLINKHDR;
181   tp = (void *)m->m_data;
182   m->m_data += sizeof(struct udpiphdr);
183
184   tp->tp_op = htons(TFTP_ERROR);
185   tp->x.tp_error.tp_error_code = htons(errorcode);
186   pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
187
188   saddr.sin_addr = recv_tp->ip.ip_dst;
189   saddr.sin_port = recv_tp->udp.uh_dport;
190
191   daddr.sin_addr = spt->client_ip;
192   daddr.sin_port = spt->client_port;
193
194   m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
195         sizeof(struct ip) - sizeof(struct udphdr);
196
197   udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
198
199 out:
200   tftp_session_terminate(spt);
201 }
202
203 static void tftp_send_next_block(struct tftp_session *spt,
204                                  struct tftp_t *recv_tp)
205 {
206   struct sockaddr_in saddr, daddr;
207   struct mbuf *m;
208   struct tftp_t *tp;
209   int nobytes;
210
211   m = m_get(spt->slirp);
212
213   if (!m) {
214     return;
215   }
216
217   memset(m->m_data, 0, m->m_size);
218
219   m->m_data += IF_MAXLINKHDR;
220   tp = (void *)m->m_data;
221   m->m_data += sizeof(struct udpiphdr);
222
223   tp->tp_op = htons(TFTP_DATA);
224   tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
225
226   saddr.sin_addr = recv_tp->ip.ip_dst;
227   saddr.sin_port = recv_tp->udp.uh_dport;
228
229   daddr.sin_addr = spt->client_ip;
230   daddr.sin_port = spt->client_port;
231
232   nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, 512);
233
234   if (nobytes < 0) {
235     m_free(m);
236
237     /* send "file not found" error back */
238
239     tftp_send_error(spt, 1, "File not found", tp);
240
241     return;
242   }
243
244   m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
245         sizeof(struct ip) - sizeof(struct udphdr);
246
247   udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
248
249   if (nobytes == 512) {
250     tftp_session_update(spt);
251   }
252   else {
253     tftp_session_terminate(spt);
254   }
255
256   spt->block_nr++;
257 }
258
259 static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
260 {
261   struct tftp_session *spt;
262   int s, k;
263   size_t prefix_len;
264   char *req_fname;
265   const char *option_name[2];
266   uint32_t option_value[2];
267   int nb_options = 0;
268
269   /* check if a session already exists and if so terminate it */
270   s = tftp_session_find(slirp, tp);
271   if (s >= 0) {
272     tftp_session_terminate(&slirp->tftp_sessions[s]);
273   }
274
275   s = tftp_session_allocate(slirp, tp);
276
277   if (s < 0) {
278     return;
279   }
280
281   spt = &slirp->tftp_sessions[s];
282
283   /* unspecified prefix means service disabled */
284   if (!slirp->tftp_prefix) {
285       tftp_send_error(spt, 2, "Access violation", tp);
286       return;
287   }
288
289   /* skip header fields */
290   k = 0;
291   pktlen -= offsetof(struct tftp_t, x.tp_buf);
292
293   /* prepend tftp_prefix */
294   prefix_len = strlen(slirp->tftp_prefix);
295   spt->filename = g_malloc(prefix_len + TFTP_FILENAME_MAX + 2);
296   memcpy(spt->filename, slirp->tftp_prefix, prefix_len);
297   spt->filename[prefix_len] = '/';
298
299   /* get name */
300   req_fname = spt->filename + prefix_len + 1;
301
302   while (1) {
303     if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
304       tftp_send_error(spt, 2, "Access violation", tp);
305       return;
306     }
307     req_fname[k] = tp->x.tp_buf[k];
308     if (req_fname[k++] == '\0') {
309       break;
310     }
311   }
312
313   /* check mode */
314   if ((pktlen - k) < 6) {
315     tftp_send_error(spt, 2, "Access violation", tp);
316     return;
317   }
318
319   if (strcasecmp(&tp->x.tp_buf[k], "octet") != 0) {
320       tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
321       return;
322   }
323
324   k += 6; /* skipping octet */
325
326   /* do sanity checks on the filename */
327   if (!strncmp(req_fname, "../", 3) ||
328       req_fname[strlen(req_fname) - 1] == '/' ||
329       strstr(req_fname, "/../")) {
330       tftp_send_error(spt, 2, "Access violation", tp);
331       return;
332   }
333
334   /* check if the file exists */
335   if (tftp_read_data(spt, 0, NULL, 0) < 0) {
336       tftp_send_error(spt, 1, "File not found", tp);
337       return;
338   }
339
340   if (tp->x.tp_buf[pktlen - 1] != 0) {
341       tftp_send_error(spt, 2, "Access violation", tp);
342       return;
343   }
344
345   while (k < pktlen && nb_options < ARRAY_SIZE(option_name)) {
346       const char *key, *value;
347
348       key = &tp->x.tp_buf[k];
349       k += strlen(key) + 1;
350
351       if (k >= pktlen) {
352           tftp_send_error(spt, 2, "Access violation", tp);
353           return;
354       }
355
356       value = &tp->x.tp_buf[k];
357       k += strlen(value) + 1;
358
359       if (strcasecmp(key, "tsize") == 0) {
360           int tsize = atoi(value);
361           struct stat stat_p;
362
363           if (tsize == 0) {
364               if (stat(spt->filename, &stat_p) == 0)
365                   tsize = stat_p.st_size;
366               else {
367                   tftp_send_error(spt, 1, "File not found", tp);
368                   return;
369               }
370           }
371
372           option_name[nb_options] = "tsize";
373           option_value[nb_options] = tsize;
374           nb_options++;
375       } else if (strcasecmp(key, "blksize") == 0) {
376           int blksize = atoi(value);
377
378           /* If blksize option is bigger than what we will
379            * emit, accept the option with our packet size.
380            * Otherwise, simply do as we didn't see the option.
381            */
382           if (blksize >= 512) {
383               option_name[nb_options] = "blksize";
384               option_value[nb_options] = 512;
385               nb_options++;
386           }
387       }
388   }
389
390   if (nb_options > 0) {
391       assert(nb_options <= ARRAY_SIZE(option_name));
392       tftp_send_oack(spt, option_name, option_value, nb_options, tp);
393       return;
394   }
395
396   spt->block_nr = 0;
397   tftp_send_next_block(spt, tp);
398 }
399
400 static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
401 {
402   int s;
403
404   s = tftp_session_find(slirp, tp);
405
406   if (s < 0) {
407     return;
408   }
409
410   tftp_send_next_block(&slirp->tftp_sessions[s], tp);
411 }
412
413 static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
414 {
415   int s;
416
417   s = tftp_session_find(slirp, tp);
418
419   if (s < 0) {
420     return;
421   }
422
423   tftp_session_terminate(&slirp->tftp_sessions[s]);
424 }
425
426 void tftp_input(struct mbuf *m)
427 {
428   struct tftp_t *tp = (struct tftp_t *)m->m_data;
429
430   switch(ntohs(tp->tp_op)) {
431   case TFTP_RRQ:
432     tftp_handle_rrq(m->slirp, tp, m->m_len);
433     break;
434
435   case TFTP_ACK:
436     tftp_handle_ack(m->slirp, tp, m->m_len);
437     break;
438
439   case TFTP_ERROR:
440     tftp_handle_error(m->slirp, tp, m->m_len);
441     break;
442   }
443 }
This page took 0.048033 seconds and 4 git commands to generate.