]> Git Repo - qemu.git/blob - slirp/tftp.c
slirp: Send window updates to guest after window was closed
[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 #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   memcpy(&spt->client_addr, srcsas, sockaddr_size(srcsas));
74   spt->fd = -1;
75   spt->block_size = 512;
76   spt->client_port = tp->udp.uh_sport;
77   spt->slirp = slirp;
78
79   tftp_session_update(spt);
80
81   return k;
82 }
83
84 static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas,
85                              struct tftp_t *tp)
86 {
87   struct tftp_session *spt;
88   int k;
89
90   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
91     spt = &slirp->tftp_sessions[k];
92
93     if (tftp_session_in_use(spt)) {
94       if (sockaddr_equal(&spt->client_addr, srcsas)) {
95         if (spt->client_port == tp->udp.uh_sport) {
96           return k;
97         }
98       }
99     }
100   }
101
102   return -1;
103 }
104
105 static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr,
106                           uint8_t *buf, int len)
107 {
108     int bytes_read = 0;
109
110     if (spt->fd < 0) {
111         spt->fd = open(spt->filename, O_RDONLY | O_BINARY);
112     }
113
114     if (spt->fd < 0) {
115         return -1;
116     }
117
118     if (len) {
119         lseek(spt->fd, block_nr * spt->block_size, SEEK_SET);
120
121         bytes_read = read(spt->fd, buf, len);
122     }
123
124     return bytes_read;
125 }
126
127 static struct tftp_t *tftp_prep_mbuf_data(struct tftp_session *spt,
128                                           struct mbuf *m)
129 {
130     struct tftp_t *tp;
131
132     memset(m->m_data, 0, m->m_size);
133
134     m->m_data += IF_MAXLINKHDR;
135     if (spt->client_addr.ss_family == AF_INET6) {
136         m->m_data += sizeof(struct ip6);
137     } else {
138         m->m_data += sizeof(struct ip);
139     }
140     tp = (void *)m->m_data;
141     m->m_data += sizeof(struct udphdr);
142
143     return tp;
144 }
145
146 static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m,
147                             struct tftp_t *recv_tp)
148 {
149     if (spt->client_addr.ss_family == AF_INET6) {
150         struct sockaddr_in6 sa6, da6;
151
152         sa6.sin6_addr = spt->slirp->vhost_addr6;
153         sa6.sin6_port = recv_tp->udp.uh_dport;
154         da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr;
155         da6.sin6_port = spt->client_port;
156
157         udp6_output(NULL, m, &sa6, &da6);
158     } else {
159         struct sockaddr_in sa4, da4;
160
161         sa4.sin_addr = spt->slirp->vhost_addr;
162         sa4.sin_port = recv_tp->udp.uh_dport;
163         da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr;
164         da4.sin_port = spt->client_port;
165
166         udp_output(NULL, m, &sa4, &da4, IPTOS_LOWDELAY);
167     }
168 }
169
170 static int tftp_send_oack(struct tftp_session *spt,
171                           const char *keys[], uint32_t values[], int nb,
172                           struct tftp_t *recv_tp)
173 {
174     struct mbuf *m;
175     struct tftp_t *tp;
176     int i, n = 0;
177
178     m = m_get(spt->slirp);
179
180     if (!m)
181         return -1;
182
183     tp = tftp_prep_mbuf_data(spt, m);
184
185     tp->tp_op = htons(TFTP_OACK);
186     for (i = 0; i < nb; i++) {
187         n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
188                       keys[i]) + 1;
189         n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
190                       values[i]) + 1;
191     }
192
193     m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX + 2) + n
194                - sizeof(struct udphdr);
195     tftp_udp_output(spt, m, recv_tp);
196
197     return 0;
198 }
199
200 static void tftp_send_error(struct tftp_session *spt,
201                             uint16_t errorcode, const char *msg,
202                             struct tftp_t *recv_tp)
203 {
204   struct mbuf *m;
205   struct tftp_t *tp;
206
207   m = m_get(spt->slirp);
208
209   if (!m) {
210     goto out;
211   }
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) - (TFTP_BLOCKSIZE_MAX + 2) + 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   tp = tftp_prep_mbuf_data(spt, m);
241
242   tp->tp_op = htons(TFTP_DATA);
243   tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
244
245   nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf,
246                            spt->block_size);
247
248   if (nobytes < 0) {
249     m_free(m);
250
251     /* send "file not found" error back */
252
253     tftp_send_error(spt, 1, "File not found", tp);
254
255     return;
256   }
257
258   m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX - nobytes)
259              - sizeof(struct udphdr);
260   tftp_udp_output(spt, m, recv_tp);
261
262   if (nobytes == spt->block_size) {
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           /* Accept blksize up to our maximum size */
393           if (blksize > 0) {
394               spt->block_size = MIN(blksize, TFTP_BLOCKSIZE_MAX);
395               option_name[nb_options] = "blksize";
396               option_value[nb_options] = spt->block_size;
397               nb_options++;
398           }
399       }
400   }
401
402   if (nb_options > 0) {
403       assert(nb_options <= ARRAY_SIZE(option_name));
404       tftp_send_oack(spt, option_name, option_value, nb_options, tp);
405       return;
406   }
407
408   spt->block_nr = 0;
409   tftp_send_next_block(spt, tp);
410 }
411
412 static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas,
413                             struct tftp_t *tp, int pktlen)
414 {
415   int s;
416
417   s = tftp_session_find(slirp, srcsas, tp);
418
419   if (s < 0) {
420     return;
421   }
422
423   tftp_send_next_block(&slirp->tftp_sessions[s], tp);
424 }
425
426 static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
427                               struct tftp_t *tp, int pktlen)
428 {
429   int s;
430
431   s = tftp_session_find(slirp, srcsas, tp);
432
433   if (s < 0) {
434     return;
435   }
436
437   tftp_session_terminate(&slirp->tftp_sessions[s]);
438 }
439
440 void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m)
441 {
442   struct tftp_t *tp = (struct tftp_t *)m->m_data;
443
444   switch(ntohs(tp->tp_op)) {
445   case TFTP_RRQ:
446     tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len);
447     break;
448
449   case TFTP_ACK:
450     tftp_handle_ack(m->slirp, srcsas, tp, m->m_len);
451     break;
452
453   case TFTP_ERROR:
454     tftp_handle_error(m->slirp, srcsas, tp, m->m_len);
455     break;
456   }
457 }
This page took 0.047237 seconds and 4 git commands to generate.