find -type f | xargs sed -i 's/[\t ]*$//g' # Yes, again. Note the star in the regex.
[qemu] / 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
27 struct tftp_session {
28     int in_use;
29     unsigned char filename[TFTP_FILENAME_MAX];
30
31     struct in_addr client_ip;
32     u_int16_t client_port;
33
34     int timestamp;
35 };
36
37 struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
38
39 const char *tftp_prefix;
40
41 static void tftp_session_update(struct tftp_session *spt)
42 {
43     spt->timestamp = curtime;
44     spt->in_use = 1;
45 }
46
47 static void tftp_session_terminate(struct tftp_session *spt)
48 {
49   spt->in_use = 0;
50 }
51
52 static int tftp_session_allocate(struct tftp_t *tp)
53 {
54   struct tftp_session *spt;
55   int k;
56
57   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
58     spt = &tftp_sessions[k];
59
60     if (!spt->in_use)
61         goto found;
62
63     /* sessions time out after 5 inactive seconds */
64     if ((int)(curtime - spt->timestamp) > 5000)
65         goto found;
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
80 static 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
100 static 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;
105   char buffer[1024];
106   int n;
107
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);
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
130 static int tftp_send_oack(struct tftp_session *spt,
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);
149
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;
156
157     daddr.sin_addr = spt->client_ip;
158     daddr.sin_port = spt->client_port;
159
160     m->m_len = sizeof(struct tftp_t) - 514 + n -
161         sizeof(struct ip) - sizeof(struct udphdr);
162     udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
163
164     return 0;
165 }
166
167
168
169 static int tftp_send_error(struct tftp_session *spt,
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);
189
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
202   m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
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
212 static int tftp_send_data(struct tftp_session *spt,
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);
236
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
258   m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
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
273 static 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     }
300
301     if (src[k] == '\0') {
302       break;
303     }
304   }
305
306   if (k >= n) {
307     return;
308   }
309
310   k++;
311
312   /* check mode */
313   if ((n - k) < 6) {
314     return;
315   }
316
317   if (memcmp(&src[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
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
335   if (!tftp_prefix) {
336       tftp_send_error(spt, 2, "Access violation", tp);
337       return;
338   }
339
340   /* check if the file exists */
341
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
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
389   tftp_send_data(spt, 1, tp);
390 }
391
392 static 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
402   if (tftp_send_data(&tftp_sessions[s],
403                      ntohs(tp->x.tp_data.tp_block_nr) + 1,
404                      tp) < 0) {
405     return;
406   }
407 }
408
409 void 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 }