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