]> git.proxmox.com Git - qemu.git/blame - slirp/tftp.c
slirp: tftp: Clean up tftp_send_error
[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>
0d62c4cf 26#include "qemu-common.h"
c7f74643
FB
27
28struct tftp_session {
a3504c87
FB
29 int in_use;
30 unsigned char filename[TFTP_FILENAME_MAX];
3b46e624 31
a3504c87
FB
32 struct in_addr client_ip;
33 u_int16_t client_port;
3b46e624 34
a3504c87 35 int timestamp;
c7f74643
FB
36};
37
9634d903 38static struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
c7f74643 39
ad196a9d 40char *tftp_prefix;
c7f74643
FB
41
42static void tftp_session_update(struct tftp_session *spt)
43{
a3504c87
FB
44 spt->timestamp = curtime;
45 spt->in_use = 1;
c7f74643
FB
46}
47
48static void tftp_session_terminate(struct tftp_session *spt)
49{
50 spt->in_use = 0;
51}
52
53static int tftp_session_allocate(struct tftp_t *tp)
54{
55 struct tftp_session *spt;
c7f74643
FB
56 int k;
57
c7f74643
FB
58 for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
59 spt = &tftp_sessions[k];
60
a3504c87
FB
61 if (!spt->in_use)
62 goto found;
c7f74643
FB
63
64 /* sessions time out after 5 inactive seconds */
a3504c87
FB
65 if ((int)(curtime - spt->timestamp) > 5000)
66 goto found;
c7f74643
FB
67 }
68
69 return -1;
70
71 found:
72 memset(spt, 0, sizeof(*spt));
73 memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
74 spt->client_port = tp->udp.uh_sport;
75
76 tftp_session_update(spt);
77
78 return k;
79}
80
81static int tftp_session_find(struct tftp_t *tp)
82{
83 struct tftp_session *spt;
84 int k;
85
86 for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
87 spt = &tftp_sessions[k];
88
89 if (spt->in_use) {
90 if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
91 if (spt->client_port == tp->udp.uh_sport) {
92 return k;
93 }
94 }
95 }
96 }
97
98 return -1;
99}
100
101static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
102 u_int8_t *buf, int len)
103{
104 int fd;
105 int bytes_read = 0;
0db1137d
TS
106 char buffer[1024];
107 int n;
c7f74643 108
0db1137d
TS
109 n = snprintf(buffer, sizeof(buffer), "%s/%s",
110 tftp_prefix, spt->filename);
111 if (n >= sizeof(buffer))
112 return -1;
113
114 fd = open(buffer, O_RDONLY | O_BINARY);
c7f74643
FB
115
116 if (fd < 0) {
117 return -1;
118 }
119
120 if (len) {
121 lseek(fd, block_nr * 512, SEEK_SET);
122
123 bytes_read = read(fd, buf, len);
124 }
125
126 close(fd);
127
128 return bytes_read;
129}
130
5fafdf24 131static int tftp_send_oack(struct tftp_session *spt,
1f697db9
TS
132 const char *key, uint32_t value,
133 struct tftp_t *recv_tp)
134{
135 struct sockaddr_in saddr, daddr;
136 struct mbuf *m;
137 struct tftp_t *tp;
138 int n = 0;
139
140 m = m_get();
141
142 if (!m)
143 return -1;
144
145 memset(m->m_data, 0, m->m_size);
146
9634d903 147 m->m_data += IF_MAXLINKHDR;
1f697db9
TS
148 tp = (void *)m->m_data;
149 m->m_data += sizeof(struct udpiphdr);
3b46e624 150
1f697db9 151 tp->tp_op = htons(TFTP_OACK);
b55266b5
BS
152 n += snprintf((char *)tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
153 key) + 1;
154 n += snprintf((char *)tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
155 value) + 1;
1f697db9
TS
156
157 saddr.sin_addr = recv_tp->ip.ip_dst;
158 saddr.sin_port = recv_tp->udp.uh_dport;
3b46e624 159
1f697db9
TS
160 daddr.sin_addr = spt->client_ip;
161 daddr.sin_port = spt->client_port;
162
5fafdf24 163 m->m_len = sizeof(struct tftp_t) - 514 + n -
1f697db9
TS
164 sizeof(struct ip) - sizeof(struct udphdr);
165 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
166
167 return 0;
168}
169
ef2d54d8
JK
170static void tftp_send_error(struct tftp_session *spt,
171 u_int16_t errorcode, const char *msg,
172 struct tftp_t *recv_tp)
c7f74643
FB
173{
174 struct sockaddr_in saddr, daddr;
175 struct mbuf *m;
176 struct tftp_t *tp;
177 int nobytes;
178
179 m = m_get();
180
181 if (!m) {
ef2d54d8 182 goto out;
c7f74643
FB
183 }
184
185 memset(m->m_data, 0, m->m_size);
186
9634d903 187 m->m_data += IF_MAXLINKHDR;
c7f74643
FB
188 tp = (void *)m->m_data;
189 m->m_data += sizeof(struct udpiphdr);
3b46e624 190
c7f74643
FB
191 tp->tp_op = htons(TFTP_ERROR);
192 tp->x.tp_error.tp_error_code = htons(errorcode);
b55266b5 193 pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
c7f74643
FB
194
195 saddr.sin_addr = recv_tp->ip.ip_dst;
196 saddr.sin_port = recv_tp->udp.uh_dport;
197
198 daddr.sin_addr = spt->client_ip;
199 daddr.sin_port = spt->client_port;
200
201 nobytes = 2;
202
5fafdf24 203 m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
c7f74643
FB
204 sizeof(struct ip) - sizeof(struct udphdr);
205
206 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
207
ef2d54d8 208out:
c7f74643 209 tftp_session_terminate(spt);
c7f74643
FB
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
9634d903 233 m->m_data += IF_MAXLINKHDR;
c7f74643
FB
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
f8e3cbd3
JK
287 /* unspecifed prefix means service disabled */
288 if (!tftp_prefix) {
289 tftp_send_error(spt, 2, "Access violation", tp);
290 return;
291 }
292
c7f74643
FB
293 src = tp->x.tp_buf;
294 dst = spt->filename;
295 n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
296
297 /* get name */
298
299 for (k = 0; k < n; k++) {
300 if (k < TFTP_FILENAME_MAX) {
301 dst[k] = src[k];
302 }
303 else {
304 return;
305 }
3b46e624 306
c7f74643
FB
307 if (src[k] == '\0') {
308 break;
309 }
310 }
3b46e624 311
c7f74643
FB
312 if (k >= n) {
313 return;
314 }
3b46e624 315
c7f74643 316 k++;
3b46e624 317
c7f74643
FB
318 /* check mode */
319 if ((n - k) < 6) {
320 return;
321 }
3b46e624 322
c7f74643
FB
323 if (memcmp(&src[k], "octet\0", 6) != 0) {
324 tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
325 return;
326 }
327
1f697db9
TS
328 k += 6; /* skipping octet */
329
c7f74643
FB
330 /* do sanity checks on the filename */
331
332 if ((spt->filename[0] != '/')
b55266b5
BS
333 || (spt->filename[strlen((char *)spt->filename) - 1] == '/')
334 || strstr((char *)spt->filename, "/../")) {
c7f74643
FB
335 tftp_send_error(spt, 2, "Access violation", tp);
336 return;
337 }
338
c7f74643 339 /* check if the file exists */
3b46e624 340
c7f74643
FB
341 if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
342 tftp_send_error(spt, 1, "File not found", tp);
343 return;
344 }
345
1f697db9
TS
346 if (src[n - 1] != 0) {
347 tftp_send_error(spt, 2, "Access violation", tp);
348 return;
349 }
350
351 while (k < n) {
352 const char *key, *value;
353
b55266b5 354 key = (char *)src + k;
1f697db9
TS
355 k += strlen(key) + 1;
356
357 if (k >= n) {
358 tftp_send_error(spt, 2, "Access violation", tp);
359 return;
360 }
361
b55266b5 362 value = (char *)src + k;
1f697db9
TS
363 k += strlen(value) + 1;
364
365 if (strcmp(key, "tsize") == 0) {
366 int tsize = atoi(value);
367 struct stat stat_p;
368
f8e3cbd3 369 if (tsize == 0) {
1f697db9
TS
370 char buffer[1024];
371 int len;
372
373 len = snprintf(buffer, sizeof(buffer), "%s/%s",
374 tftp_prefix, spt->filename);
375
376 if (stat(buffer, &stat_p) == 0)
377 tsize = stat_p.st_size;
378 else {
379 tftp_send_error(spt, 1, "File not found", tp);
380 return;
381 }
382 }
383
384 tftp_send_oack(spt, "tsize", tsize, tp);
385 }
386 }
387
c7f74643
FB
388 tftp_send_data(spt, 1, tp);
389}
390
391static void tftp_handle_ack(struct tftp_t *tp, int pktlen)
392{
393 int s;
394
395 s = tftp_session_find(tp);
396
397 if (s < 0) {
398 return;
399 }
400
5fafdf24
TS
401 if (tftp_send_data(&tftp_sessions[s],
402 ntohs(tp->x.tp_data.tp_block_nr) + 1,
c7f74643
FB
403 tp) < 0) {
404 return;
405 }
406}
407
408void 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(tp, m->m_len);
415 break;
416
417 case TFTP_ACK:
418 tftp_handle_ack(tp, m->m_len);
419 break;
420 }
421}