]> Git Repo - qemu.git/blob - slirp/tftp.c
cris: Add CRISv10 gdbstub support.
[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 <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, u_int16_t block_nr,
96                           u_int8_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                             u_int16_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   int nobytes;
165
166   m = m_get(spt->slirp);
167
168   if (!m) {
169     goto out;
170   }
171
172   memset(m->m_data, 0, m->m_size);
173
174   m->m_data += IF_MAXLINKHDR;
175   tp = (void *)m->m_data;
176   m->m_data += sizeof(struct udpiphdr);
177
178   tp->tp_op = htons(TFTP_ERROR);
179   tp->x.tp_error.tp_error_code = htons(errorcode);
180   pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
181
182   saddr.sin_addr = recv_tp->ip.ip_dst;
183   saddr.sin_port = recv_tp->udp.uh_dport;
184
185   daddr.sin_addr = spt->client_ip;
186   daddr.sin_port = spt->client_port;
187
188   nobytes = 2;
189
190   m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
191         sizeof(struct ip) - sizeof(struct udphdr);
192
193   udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
194
195 out:
196   tftp_session_terminate(spt);
197 }
198
199 static int tftp_send_data(struct tftp_session *spt,
200                           u_int16_t block_nr,
201                           struct tftp_t *recv_tp)
202 {
203   struct sockaddr_in saddr, daddr;
204   struct mbuf *m;
205   struct tftp_t *tp;
206   int nobytes;
207
208   if (block_nr < 1) {
209     return -1;
210   }
211
212   m = m_get(spt->slirp);
213
214   if (!m) {
215     return -1;
216   }
217
218   memset(m->m_data, 0, m->m_size);
219
220   m->m_data += IF_MAXLINKHDR;
221   tp = (void *)m->m_data;
222   m->m_data += sizeof(struct udpiphdr);
223
224   tp->tp_op = htons(TFTP_DATA);
225   tp->x.tp_data.tp_block_nr = htons(block_nr);
226
227   saddr.sin_addr = recv_tp->ip.ip_dst;
228   saddr.sin_port = recv_tp->udp.uh_dport;
229
230   daddr.sin_addr = spt->client_ip;
231   daddr.sin_port = spt->client_port;
232
233   nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
234
235   if (nobytes < 0) {
236     m_free(m);
237
238     /* send "file not found" error back */
239
240     tftp_send_error(spt, 1, "File not found", tp);
241
242     return -1;
243   }
244
245   m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
246         sizeof(struct ip) - sizeof(struct udphdr);
247
248   udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
249
250   if (nobytes == 512) {
251     tftp_session_update(spt);
252   }
253   else {
254     tftp_session_terminate(spt);
255   }
256
257   return 0;
258 }
259
260 static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
261 {
262   struct tftp_session *spt;
263   int s, k;
264   size_t prefix_len;
265   char *req_fname;
266
267   /* check if a session already exists and if so terminate it */
268   s = tftp_session_find(slirp, tp);
269   if (s >= 0) {
270     tftp_session_terminate(&slirp->tftp_sessions[s]);
271   }
272
273   s = tftp_session_allocate(slirp, tp);
274
275   if (s < 0) {
276     return;
277   }
278
279   spt = &slirp->tftp_sessions[s];
280
281   /* unspecifed prefix means service disabled */
282   if (!slirp->tftp_prefix) {
283       tftp_send_error(spt, 2, "Access violation", tp);
284       return;
285   }
286
287   /* skip header fields */
288   k = 0;
289   pktlen -= ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
290
291   /* prepend tftp_prefix */
292   prefix_len = strlen(slirp->tftp_prefix);
293   spt->filename = qemu_malloc(prefix_len + TFTP_FILENAME_MAX + 2);
294   memcpy(spt->filename, slirp->tftp_prefix, prefix_len);
295   spt->filename[prefix_len] = '/';
296
297   /* get name */
298   req_fname = spt->filename + prefix_len + 1;
299
300   while (1) {
301     if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
302       tftp_send_error(spt, 2, "Access violation", tp);
303       return;
304     }
305     req_fname[k] = (char)tp->x.tp_buf[k];
306     if (req_fname[k++] == '\0') {
307       break;
308     }
309   }
310
311   /* check mode */
312   if ((pktlen - k) < 6) {
313     tftp_send_error(spt, 2, "Access violation", tp);
314     return;
315   }
316
317   if (memcmp(&tp->x.tp_buf[k], "octet\0", 6) != 0) {
318       tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
319       return;
320   }
321
322   k += 6; /* skipping octet */
323
324   /* do sanity checks on the filename */
325   if (!strncmp(req_fname, "../", 3) ||
326       req_fname[strlen(req_fname) - 1] == '/' ||
327       strstr(req_fname, "/../")) {
328       tftp_send_error(spt, 2, "Access violation", tp);
329       return;
330   }
331
332   /* check if the file exists */
333   if (tftp_read_data(spt, 0, NULL, 0) < 0) {
334       tftp_send_error(spt, 1, "File not found", tp);
335       return;
336   }
337
338   if (tp->x.tp_buf[pktlen - 1] != 0) {
339       tftp_send_error(spt, 2, "Access violation", tp);
340       return;
341   }
342
343   while (k < pktlen) {
344       const char *key, *value;
345
346       key = (const char *)&tp->x.tp_buf[k];
347       k += strlen(key) + 1;
348
349       if (k >= pktlen) {
350           tftp_send_error(spt, 2, "Access violation", tp);
351           return;
352       }
353
354       value = (const char *)&tp->x.tp_buf[k];
355       k += strlen(value) + 1;
356
357       if (strcmp(key, "tsize") == 0) {
358           int tsize = atoi(value);
359           struct stat stat_p;
360
361           if (tsize == 0) {
362               if (stat(spt->filename, &stat_p) == 0)
363                   tsize = stat_p.st_size;
364               else {
365                   tftp_send_error(spt, 1, "File not found", tp);
366                   return;
367               }
368           }
369
370           tftp_send_oack(spt, "tsize", tsize, tp);
371           return;
372       }
373   }
374
375   tftp_send_data(spt, 1, tp);
376 }
377
378 static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
379 {
380   int s;
381
382   s = tftp_session_find(slirp, tp);
383
384   if (s < 0) {
385     return;
386   }
387
388   if (tftp_send_data(&slirp->tftp_sessions[s],
389                      ntohs(tp->x.tp_data.tp_block_nr) + 1,
390                      tp) < 0) {
391     return;
392   }
393 }
394
395 static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
396 {
397   int s;
398
399   s = tftp_session_find(slirp, tp);
400
401   if (s < 0) {
402     return;
403   }
404
405   tftp_session_terminate(&slirp->tftp_sessions[s]);
406 }
407
408 void tftp_input(struct mbuf *m)
409 {
410   struct tftp_t *tp = (struct tftp_t *)m->m_data;
411
412   switch(ntohs(tp->tp_op)) {
413   case TFTP_RRQ:
414     tftp_handle_rrq(m->slirp, tp, m->m_len);
415     break;
416
417   case TFTP_ACK:
418     tftp_handle_ack(m->slirp, tp, m->m_len);
419     break;
420
421   case TFTP_ERROR:
422     tftp_handle_error(m->slirp, tp, m->m_len);
423     break;
424   }
425 }
This page took 0.045682 seconds and 4 git commands to generate.