These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / slirp / tftp.c
1 /*
2  * tftp.c - a simple, read-only tftp server for qemu
3  *
4  * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
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 #include "qemu/cutils.h"
29
30 static inline int tftp_session_in_use(struct tftp_session *spt)
31 {
32     return (spt->slirp != NULL);
33 }
34
35 static inline void tftp_session_update(struct tftp_session *spt)
36 {
37     spt->timestamp = curtime;
38 }
39
40 static void tftp_session_terminate(struct tftp_session *spt)
41 {
42     if (spt->fd >= 0) {
43         close(spt->fd);
44         spt->fd = -1;
45     }
46     g_free(spt->filename);
47     spt->slirp = NULL;
48 }
49
50 static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas,
51                                  struct tftp_t *tp)
52 {
53   struct tftp_session *spt;
54   int k;
55
56   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
57     spt = &slirp->tftp_sessions[k];
58
59     if (!tftp_session_in_use(spt))
60         goto found;
61
62     /* sessions time out after 5 inactive seconds */
63     if ((int)(curtime - spt->timestamp) > 5000) {
64         tftp_session_terminate(spt);
65         goto found;
66     }
67   }
68
69   return -1;
70
71  found:
72   memset(spt, 0, sizeof(*spt));
73   spt->client_addr = *srcsas;
74   spt->fd = -1;
75   spt->client_port = tp->udp.uh_sport;
76   spt->slirp = slirp;
77
78   tftp_session_update(spt);
79
80   return k;
81 }
82
83 static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas,
84                              struct tftp_t *tp)
85 {
86   struct tftp_session *spt;
87   int k;
88
89   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
90     spt = &slirp->tftp_sessions[k];
91
92     if (tftp_session_in_use(spt)) {
93       if (sockaddr_equal(&spt->client_addr, srcsas)) {
94         if (spt->client_port == tp->udp.uh_sport) {
95           return k;
96         }
97       }
98     }
99   }
100
101   return -1;
102 }
103
104 static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr,
105                           uint8_t *buf, int len)
106 {
107     int bytes_read = 0;
108
109     if (spt->fd < 0) {
110         spt->fd = open(spt->filename, O_RDONLY | O_BINARY);
111     }
112
113     if (spt->fd < 0) {
114         return -1;
115     }
116
117     if (len) {
118         lseek(spt->fd, block_nr * 512, SEEK_SET);
119
120         bytes_read = read(spt->fd, buf, len);
121     }
122
123     return bytes_read;
124 }
125
126 static struct tftp_t *tftp_prep_mbuf_data(struct tftp_session *spt,
127                                           struct mbuf *m)
128 {
129     struct tftp_t *tp;
130
131     memset(m->m_data, 0, m->m_size);
132
133     m->m_data += IF_MAXLINKHDR;
134     if (spt->client_addr.ss_family == AF_INET6) {
135         m->m_data += sizeof(struct ip6);
136     } else {
137         m->m_data += sizeof(struct ip);
138     }
139     tp = (void *)m->m_data;
140     m->m_data += sizeof(struct udphdr);
141
142     return tp;
143 }
144
145 static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m,
146                             struct tftp_t *recv_tp)
147 {
148     if (spt->client_addr.ss_family == AF_INET6) {
149         struct sockaddr_in6 sa6, da6;
150
151         sa6.sin6_addr = spt->slirp->vhost_addr6;
152         sa6.sin6_port = recv_tp->udp.uh_dport;
153         da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr;
154         da6.sin6_port = spt->client_port;
155
156         udp6_output(NULL, m, &sa6, &da6);
157     } else {
158         struct sockaddr_in sa4, da4;
159
160         sa4.sin_addr = spt->slirp->vhost_addr;
161         sa4.sin_port = recv_tp->udp.uh_dport;
162         da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr;
163         da4.sin_port = spt->client_port;
164
165         udp_output(NULL, m, &sa4, &da4, IPTOS_LOWDELAY);
166     }
167 }
168
169 static int tftp_send_oack(struct tftp_session *spt,
170                           const char *keys[], uint32_t values[], int nb,
171                           struct tftp_t *recv_tp)
172 {
173     struct mbuf *m;
174     struct tftp_t *tp;
175     int i, n = 0;
176
177     m = m_get(spt->slirp);
178
179     if (!m)
180         return -1;
181
182     tp = tftp_prep_mbuf_data(spt, m);
183
184     tp->tp_op = htons(TFTP_OACK);
185     for (i = 0; i < nb; i++) {
186         n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
187                       keys[i]) + 1;
188         n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
189                       values[i]) + 1;
190     }
191
192     m->m_len = sizeof(struct tftp_t) - 514 + n - sizeof(struct udphdr);
193     tftp_udp_output(spt, m, recv_tp);
194
195     return 0;
196 }
197
198 static void tftp_send_error(struct tftp_session *spt,
199                             uint16_t errorcode, const char *msg,
200                             struct tftp_t *recv_tp)
201 {
202   struct mbuf *m;
203   struct tftp_t *tp;
204
205   m = m_get(spt->slirp);
206
207   if (!m) {
208     goto out;
209   }
210
211   memset(m->m_data, 0, m->m_size);
212
213   tp = tftp_prep_mbuf_data(spt, m);
214
215   tp->tp_op = htons(TFTP_ERROR);
216   tp->x.tp_error.tp_error_code = htons(errorcode);
217   pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
218
219   m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg)
220              - sizeof(struct udphdr);
221   tftp_udp_output(spt, m, recv_tp);
222
223 out:
224   tftp_session_terminate(spt);
225 }
226
227 static void tftp_send_next_block(struct tftp_session *spt,
228                                  struct tftp_t *recv_tp)
229 {
230   struct mbuf *m;
231   struct tftp_t *tp;
232   int nobytes;
233
234   m = m_get(spt->slirp);
235
236   if (!m) {
237     return;
238   }
239
240   memset(m->m_data, 0, m->m_size);
241
242   tp = tftp_prep_mbuf_data(spt, m);
243
244   tp->tp_op = htons(TFTP_DATA);
245   tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
246
247   nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, 512);
248
249   if (nobytes < 0) {
250     m_free(m);
251
252     /* send "file not found" error back */
253
254     tftp_send_error(spt, 1, "File not found", tp);
255
256     return;
257   }
258
259   m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - sizeof(struct udphdr);
260   tftp_udp_output(spt, m, recv_tp);
261
262   if (nobytes == 512) {
263     tftp_session_update(spt);
264   }
265   else {
266     tftp_session_terminate(spt);
267   }
268
269   spt->block_nr++;
270 }
271
272 static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas,
273                             struct tftp_t *tp, int pktlen)
274 {
275   struct tftp_session *spt;
276   int s, k;
277   size_t prefix_len;
278   char *req_fname;
279   const char *option_name[2];
280   uint32_t option_value[2];
281   int nb_options = 0;
282
283   /* check if a session already exists and if so terminate it */
284   s = tftp_session_find(slirp, srcsas, tp);
285   if (s >= 0) {
286     tftp_session_terminate(&slirp->tftp_sessions[s]);
287   }
288
289   s = tftp_session_allocate(slirp, srcsas, tp);
290
291   if (s < 0) {
292     return;
293   }
294
295   spt = &slirp->tftp_sessions[s];
296
297   /* unspecified prefix means service disabled */
298   if (!slirp->tftp_prefix) {
299       tftp_send_error(spt, 2, "Access violation", tp);
300       return;
301   }
302
303   /* skip header fields */
304   k = 0;
305   pktlen -= offsetof(struct tftp_t, x.tp_buf);
306
307   /* prepend tftp_prefix */
308   prefix_len = strlen(slirp->tftp_prefix);
309   spt->filename = g_malloc(prefix_len + TFTP_FILENAME_MAX + 2);
310   memcpy(spt->filename, slirp->tftp_prefix, prefix_len);
311   spt->filename[prefix_len] = '/';
312
313   /* get name */
314   req_fname = spt->filename + prefix_len + 1;
315
316   while (1) {
317     if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
318       tftp_send_error(spt, 2, "Access violation", tp);
319       return;
320     }
321     req_fname[k] = tp->x.tp_buf[k];
322     if (req_fname[k++] == '\0') {
323       break;
324     }
325   }
326
327   /* check mode */
328   if ((pktlen - k) < 6) {
329     tftp_send_error(spt, 2, "Access violation", tp);
330     return;
331   }
332
333   if (strcasecmp(&tp->x.tp_buf[k], "octet") != 0) {
334       tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
335       return;
336   }
337
338   k += 6; /* skipping octet */
339
340   /* do sanity checks on the filename */
341   if (!strncmp(req_fname, "../", 3) ||
342       req_fname[strlen(req_fname) - 1] == '/' ||
343       strstr(req_fname, "/../")) {
344       tftp_send_error(spt, 2, "Access violation", tp);
345       return;
346   }
347
348   /* check if the file exists */
349   if (tftp_read_data(spt, 0, NULL, 0) < 0) {
350       tftp_send_error(spt, 1, "File not found", tp);
351       return;
352   }
353
354   if (tp->x.tp_buf[pktlen - 1] != 0) {
355       tftp_send_error(spt, 2, "Access violation", tp);
356       return;
357   }
358
359   while (k < pktlen && nb_options < ARRAY_SIZE(option_name)) {
360       const char *key, *value;
361
362       key = &tp->x.tp_buf[k];
363       k += strlen(key) + 1;
364
365       if (k >= pktlen) {
366           tftp_send_error(spt, 2, "Access violation", tp);
367           return;
368       }
369
370       value = &tp->x.tp_buf[k];
371       k += strlen(value) + 1;
372
373       if (strcasecmp(key, "tsize") == 0) {
374           int tsize = atoi(value);
375           struct stat stat_p;
376
377           if (tsize == 0) {
378               if (stat(spt->filename, &stat_p) == 0)
379                   tsize = stat_p.st_size;
380               else {
381                   tftp_send_error(spt, 1, "File not found", tp);
382                   return;
383               }
384           }
385
386           option_name[nb_options] = "tsize";
387           option_value[nb_options] = tsize;
388           nb_options++;
389       } else if (strcasecmp(key, "blksize") == 0) {
390           int blksize = atoi(value);
391
392           /* If blksize option is bigger than what we will
393            * emit, accept the option with our packet size.
394            * Otherwise, simply do as we didn't see the option.
395            */
396           if (blksize >= 512) {
397               option_name[nb_options] = "blksize";
398               option_value[nb_options] = 512;
399               nb_options++;
400           }
401       }
402   }
403
404   if (nb_options > 0) {
405       assert(nb_options <= ARRAY_SIZE(option_name));
406       tftp_send_oack(spt, option_name, option_value, nb_options, tp);
407       return;
408   }
409
410   spt->block_nr = 0;
411   tftp_send_next_block(spt, tp);
412 }
413
414 static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas,
415                             struct tftp_t *tp, int pktlen)
416 {
417   int s;
418
419   s = tftp_session_find(slirp, srcsas, tp);
420
421   if (s < 0) {
422     return;
423   }
424
425   tftp_send_next_block(&slirp->tftp_sessions[s], tp);
426 }
427
428 static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
429                               struct tftp_t *tp, int pktlen)
430 {
431   int s;
432
433   s = tftp_session_find(slirp, srcsas, tp);
434
435   if (s < 0) {
436     return;
437   }
438
439   tftp_session_terminate(&slirp->tftp_sessions[s]);
440 }
441
442 void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m)
443 {
444   struct tftp_t *tp = (struct tftp_t *)m->m_data;
445
446   switch(ntohs(tp->tp_op)) {
447   case TFTP_RRQ:
448     tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len);
449     break;
450
451   case TFTP_ACK:
452     tftp_handle_ack(m->slirp, srcsas, tp, m->m_len);
453     break;
454
455   case TFTP_ERROR:
456     tftp_handle_error(m->slirp, srcsas, tp, m->m_len);
457     break;
458   }
459 }