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