]> git.proxmox.com Git - mirror_qemu.git/blame - slirp/tftp.c
find -type f | xargs sed -i 's/[\t ]*$//g' # Yes, again. Note the star in the regex.
[mirror_qemu.git] / slirp / tftp.c
CommitLineData
c7f74643
FB
1/*
2 * tftp.c - a simple, read-only tftp server for qemu
5fafdf24 3 *
c7f74643 4 * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
5fafdf24 5 *
c7f74643
FB
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
27struct tftp_session {
a3504c87
FB
28 int in_use;
29 unsigned char filename[TFTP_FILENAME_MAX];
3b46e624 30
a3504c87
FB
31 struct in_addr client_ip;
32 u_int16_t client_port;
3b46e624 33
a3504c87 34 int timestamp;
c7f74643
FB
35};
36
37struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
38
9bf05444 39const char *tftp_prefix;
c7f74643
FB
40
41static void tftp_session_update(struct tftp_session *spt)
42{
a3504c87
FB
43 spt->timestamp = curtime;
44 spt->in_use = 1;
c7f74643
FB
45}
46
47static void tftp_session_terminate(struct tftp_session *spt)
48{
49 spt->in_use = 0;
50}
51
52static int tftp_session_allocate(struct tftp_t *tp)
53{
54 struct tftp_session *spt;
c7f74643
FB
55 int k;
56
c7f74643
FB
57 for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
58 spt = &tftp_sessions[k];
59
a3504c87
FB
60 if (!spt->in_use)
61 goto found;
c7f74643
FB
62
63 /* sessions time out after 5 inactive seconds */
a3504c87
FB
64 if ((int)(curtime - spt->timestamp) > 5000)
65 goto found;
c7f74643
FB
66 }
67
68 return -1;
69
70 found:
71 memset(spt, 0, sizeof(*spt));
72 memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
73 spt->client_port = tp->udp.uh_sport;
74
75 tftp_session_update(spt);
76
77 return k;
78}
79
80static int tftp_session_find(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 = &tftp_sessions[k];
87
88 if (spt->in_use) {
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
100static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
101 u_int8_t *buf, int len)
102{
103 int fd;
104 int bytes_read = 0;
0db1137d
TS
105 char buffer[1024];
106 int n;
c7f74643 107
0db1137d
TS
108 n = snprintf(buffer, sizeof(buffer), "%s/%s",
109 tftp_prefix, spt->filename);
110 if (n >= sizeof(buffer))
111 return -1;
112
113 fd = open(buffer, O_RDONLY | O_BINARY);
c7f74643
FB
114
115 if (fd < 0) {
116 return -1;
117 }
118
119 if (len) {
120 lseek(fd, block_nr * 512, SEEK_SET);
121
122 bytes_read = read(fd, buf, len);
123 }
124
125 close(fd);
126
127 return bytes_read;
128}
129
5fafdf24 130static int tftp_send_oack(struct tftp_session *spt,
1f697db9
TS
131 const char *key, uint32_t value,
132 struct tftp_t *recv_tp)
133{
134 struct sockaddr_in saddr, daddr;
135 struct mbuf *m;
136 struct tftp_t *tp;
137 int n = 0;
138
139 m = m_get();
140
141 if (!m)
142 return -1;
143
144 memset(m->m_data, 0, m->m_size);
145
146 m->m_data += if_maxlinkhdr;
147 tp = (void *)m->m_data;
148 m->m_data += sizeof(struct udpiphdr);
3b46e624 149
1f697db9
TS
150 tp->tp_op = htons(TFTP_OACK);
151 n += sprintf(tp->x.tp_buf + n, "%s", key) + 1;
152 n += sprintf(tp->x.tp_buf + n, "%u", value) + 1;
153
154 saddr.sin_addr = recv_tp->ip.ip_dst;
155 saddr.sin_port = recv_tp->udp.uh_dport;
3b46e624 156
1f697db9
TS
157 daddr.sin_addr = spt->client_ip;
158 daddr.sin_port = spt->client_port;
159
5fafdf24 160 m->m_len = sizeof(struct tftp_t) - 514 + n -
1f697db9
TS
161 sizeof(struct ip) - sizeof(struct udphdr);
162 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
163
164 return 0;
165}
166
167
168
5fafdf24 169static int tftp_send_error(struct tftp_session *spt,
c7f74643
FB
170 u_int16_t errorcode, const char *msg,
171 struct tftp_t *recv_tp)
172{
173 struct sockaddr_in saddr, daddr;
174 struct mbuf *m;
175 struct tftp_t *tp;
176 int nobytes;
177
178 m = m_get();
179
180 if (!m) {
181 return -1;
182 }
183
184 memset(m->m_data, 0, m->m_size);
185
186 m->m_data += if_maxlinkhdr;
187 tp = (void *)m->m_data;
188 m->m_data += sizeof(struct udpiphdr);
3b46e624 189
c7f74643
FB
190 tp->tp_op = htons(TFTP_ERROR);
191 tp->x.tp_error.tp_error_code = htons(errorcode);
192 strcpy(tp->x.tp_error.tp_msg, msg);
193
194 saddr.sin_addr = recv_tp->ip.ip_dst;
195 saddr.sin_port = recv_tp->udp.uh_dport;
196
197 daddr.sin_addr = spt->client_ip;
198 daddr.sin_port = spt->client_port;
199
200 nobytes = 2;
201
5fafdf24 202 m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
c7f74643
FB
203 sizeof(struct ip) - sizeof(struct udphdr);
204
205 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
206
207 tftp_session_terminate(spt);
208
209 return 0;
210}
211
5fafdf24 212static int tftp_send_data(struct tftp_session *spt,
c7f74643
FB
213 u_int16_t block_nr,
214 struct tftp_t *recv_tp)
215{
216 struct sockaddr_in saddr, daddr;
217 struct mbuf *m;
218 struct tftp_t *tp;
219 int nobytes;
220
221 if (block_nr < 1) {
222 return -1;
223 }
224
225 m = m_get();
226
227 if (!m) {
228 return -1;
229 }
230
231 memset(m->m_data, 0, m->m_size);
232
233 m->m_data += if_maxlinkhdr;
234 tp = (void *)m->m_data;
235 m->m_data += sizeof(struct udpiphdr);
3b46e624 236
c7f74643
FB
237 tp->tp_op = htons(TFTP_DATA);
238 tp->x.tp_data.tp_block_nr = htons(block_nr);
239
240 saddr.sin_addr = recv_tp->ip.ip_dst;
241 saddr.sin_port = recv_tp->udp.uh_dport;
242
243 daddr.sin_addr = spt->client_ip;
244 daddr.sin_port = spt->client_port;
245
246 nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
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 -1;
256 }
257
5fafdf24 258 m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
c7f74643
FB
259 sizeof(struct ip) - sizeof(struct udphdr);
260
261 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
262
263 if (nobytes == 512) {
264 tftp_session_update(spt);
265 }
266 else {
267 tftp_session_terminate(spt);
268 }
269
270 return 0;
271}
272
273static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
274{
275 struct tftp_session *spt;
276 int s, k, n;
277 u_int8_t *src, *dst;
278
279 s = tftp_session_allocate(tp);
280
281 if (s < 0) {
282 return;
283 }
284
285 spt = &tftp_sessions[s];
286
287 src = tp->x.tp_buf;
288 dst = spt->filename;
289 n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
290
291 /* get name */
292
293 for (k = 0; k < n; k++) {
294 if (k < TFTP_FILENAME_MAX) {
295 dst[k] = src[k];
296 }
297 else {
298 return;
299 }
3b46e624 300
c7f74643
FB
301 if (src[k] == '\0') {
302 break;
303 }
304 }
3b46e624 305
c7f74643
FB
306 if (k >= n) {
307 return;
308 }
3b46e624 309
c7f74643 310 k++;
3b46e624 311
c7f74643
FB
312 /* check mode */
313 if ((n - k) < 6) {
314 return;
315 }
3b46e624 316
c7f74643
FB
317 if (memcmp(&src[k], "octet\0", 6) != 0) {
318 tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
319 return;
320 }
321
1f697db9
TS
322 k += 6; /* skipping octet */
323
c7f74643
FB
324 /* do sanity checks on the filename */
325
326 if ((spt->filename[0] != '/')
327 || (spt->filename[strlen(spt->filename) - 1] == '/')
328 || strstr(spt->filename, "/../")) {
329 tftp_send_error(spt, 2, "Access violation", tp);
330 return;
331 }
332
333 /* only allow exported prefixes */
334
0db1137d 335 if (!tftp_prefix) {
c7f74643
FB
336 tftp_send_error(spt, 2, "Access violation", tp);
337 return;
338 }
339
340 /* check if the file exists */
3b46e624 341
c7f74643
FB
342 if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
343 tftp_send_error(spt, 1, "File not found", tp);
344 return;
345 }
346
1f697db9
TS
347 if (src[n - 1] != 0) {
348 tftp_send_error(spt, 2, "Access violation", tp);
349 return;
350 }
351
352 while (k < n) {
353 const char *key, *value;
354
355 key = src + k;
356 k += strlen(key) + 1;
357
358 if (k >= n) {
359 tftp_send_error(spt, 2, "Access violation", tp);
360 return;
361 }
362
363 value = src + k;
364 k += strlen(value) + 1;
365
366 if (strcmp(key, "tsize") == 0) {
367 int tsize = atoi(value);
368 struct stat stat_p;
369
370 if (tsize == 0 && tftp_prefix) {
371 char buffer[1024];
372 int len;
373
374 len = snprintf(buffer, sizeof(buffer), "%s/%s",
375 tftp_prefix, spt->filename);
376
377 if (stat(buffer, &stat_p) == 0)
378 tsize = stat_p.st_size;
379 else {
380 tftp_send_error(spt, 1, "File not found", tp);
381 return;
382 }
383 }
384
385 tftp_send_oack(spt, "tsize", tsize, tp);
386 }
387 }
388
c7f74643
FB
389 tftp_send_data(spt, 1, tp);
390}
391
392static void tftp_handle_ack(struct tftp_t *tp, int pktlen)
393{
394 int s;
395
396 s = tftp_session_find(tp);
397
398 if (s < 0) {
399 return;
400 }
401
5fafdf24
TS
402 if (tftp_send_data(&tftp_sessions[s],
403 ntohs(tp->x.tp_data.tp_block_nr) + 1,
c7f74643
FB
404 tp) < 0) {
405 return;
406 }
407}
408
409void tftp_input(struct mbuf *m)
410{
411 struct tftp_t *tp = (struct tftp_t *)m->m_data;
412
413 switch(ntohs(tp->tp_op)) {
414 case TFTP_RRQ:
415 tftp_handle_rrq(tp, m->m_len);
416 break;
417
418 case TFTP_ACK:
419 tftp_handle_ack(tp, m->m_len);
420 break;
421 }
422}