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 #ifdef _WIN32
27 #include <io.h>
28 #endif
29 
30 struct tftp_session {
31     int in_use;
32     char filename[TFTP_FILENAME_MAX];
33 
34     struct in_addr client_ip;
35     u_int16_t client_port;
36 
37     int timestamp;
38 };
39 
40 static struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
41 
42 static const char *tftp_prefix;
43 
tftp_session_update(struct tftp_session * spt)44 static void tftp_session_update(struct tftp_session *spt)
45 {
46     spt->timestamp = curtime;
47     spt->in_use = 1;
48 }
49 
tftp_session_terminate(struct tftp_session * spt)50 static void tftp_session_terminate(struct tftp_session *spt)
51 {
52   spt->in_use = 0;
53 }
54 
tftp_session_allocate(struct tftp_t * tp)55 static int tftp_session_allocate(struct tftp_t *tp)
56 {
57   struct tftp_session *spt;
58   int k;
59 
60   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
61     spt = &tftp_sessions[k];
62 
63     if (!spt->in_use)
64         goto found;
65 
66     /* sessions time out after 5 inactive seconds */
67     if ((int)(curtime - spt->timestamp) > 5000)
68         goto found;
69   }
70 
71   return -1;
72 
73  found:
74   memset(spt, 0, sizeof(*spt));
75   memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
76   spt->client_port = tp->udp.uh_sport;
77 
78   tftp_session_update(spt);
79 
80   return k;
81 }
82 
tftp_session_find(struct tftp_t * tp)83 static int tftp_session_find(struct tftp_t *tp)
84 {
85   struct tftp_session *spt;
86   int k;
87 
88   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
89     spt = &tftp_sessions[k];
90 
91     if (spt->in_use) {
92       if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
93 	if (spt->client_port == tp->udp.uh_sport) {
94 	  return k;
95 	}
96       }
97     }
98   }
99 
100   return -1;
101 }
102 
tftp_read_data(struct tftp_session * spt,u_int16_t block_nr,u_int8_t * buf,int len)103 static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
104 			  u_int8_t *buf, int len)
105 {
106   int fd;
107   int bytes_read = 0;
108 
109   fd = open(spt->filename, O_RDONLY | O_BINARY);
110 
111   if (fd < 0) {
112     return -1;
113   }
114 
115   if (len) {
116     lseek(fd, block_nr * 512, SEEK_SET);
117 
118     bytes_read = read(fd, buf, len);
119   }
120 
121   close(fd);
122 
123   return bytes_read;
124 }
125 
tftp_send_error(struct tftp_session * spt,u_int16_t errorcode,const char * msg,struct tftp_t * recv_tp)126 static int tftp_send_error(struct tftp_session *spt,
127 			   u_int16_t errorcode, const char *msg,
128 			   struct tftp_t *recv_tp)
129 {
130   struct sockaddr_in saddr, daddr;
131   struct mbuf *m;
132   struct tftp_t *tp;
133   int nobytes;
134 
135   m = m_get();
136 
137   if (!m) {
138     return -1;
139   }
140 
141   memset(m->m_data, 0, m->m_size);
142 
143   m->m_data += if_maxlinkhdr;
144   tp = (struct tftp_t*)m->m_data;
145   m->m_data += sizeof(struct udpiphdr);
146 
147   tp->tp_op = htons(TFTP_ERROR);
148   tp->x.tp_error.tp_error_code = htons(errorcode);
149   strncpy((char *)tp->x.tp_error.tp_msg, msg, sizeof(tp->x.tp_error.tp_msg));
150   tp->x.tp_error.tp_msg[sizeof(tp->x.tp_error.tp_msg)-1] = 0;
151 
152   saddr.sin_addr = recv_tp->ip.ip_dst;
153   saddr.sin_port = recv_tp->udp.uh_dport;
154 
155   daddr.sin_addr = spt->client_ip;
156   daddr.sin_port = spt->client_port;
157 
158   nobytes = 2;
159 
160   m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
161         sizeof(struct ip) - sizeof(struct udphdr);
162 
163   udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
164 
165   tftp_session_terminate(spt);
166 
167   return 0;
168 }
169 
tftp_send_data(struct tftp_session * spt,u_int16_t block_nr,struct tftp_t * recv_tp)170 static int tftp_send_data(struct tftp_session *spt,
171 			  u_int16_t block_nr,
172 			  struct tftp_t *recv_tp)
173 {
174   struct sockaddr_in saddr, daddr;
175   struct mbuf *m;
176   struct tftp_t *tp;
177   int nobytes;
178 
179   if (block_nr < 1) {
180     return -1;
181   }
182 
183   m = m_get();
184 
185   if (!m) {
186     return -1;
187   }
188 
189   memset(m->m_data, 0, m->m_size);
190 
191   m->m_data += if_maxlinkhdr;
192   tp = (struct tftp_t *)m->m_data;
193   m->m_data += sizeof(struct udpiphdr);
194 
195   tp->tp_op = htons(TFTP_DATA);
196   tp->x.tp_data.tp_block_nr = htons(block_nr);
197 
198   saddr.sin_addr = recv_tp->ip.ip_dst;
199   saddr.sin_port = recv_tp->udp.uh_dport;
200 
201   daddr.sin_addr = spt->client_ip;
202   daddr.sin_port = spt->client_port;
203 
204   nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
205 
206   if (nobytes < 0) {
207     m_free(m);
208 
209     /* send "file not found" error back */
210 
211     tftp_send_error(spt, 1, "File not found", tp);
212 
213     return -1;
214   }
215 
216   m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
217         sizeof(struct ip) - sizeof(struct udphdr);
218 
219   udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
220 
221   if (nobytes == 512) {
222     tftp_session_update(spt);
223   }
224   else {
225     tftp_session_terminate(spt);
226   }
227 
228   return 0;
229 }
230 
tftp_handle_rrq(struct tftp_t * tp,int pktlen)231 static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
232 {
233   struct tftp_session *spt;
234   int s, k, n;
235   u_int8_t *src, *dst;
236 
237   s = tftp_session_allocate(tp);
238 
239   if (s < 0) {
240     return;
241   }
242 
243   spt = &tftp_sessions[s];
244 
245   src = tp->x.tp_buf;
246   dst = (u_int8_t *)spt->filename;
247   n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
248 
249   /* get name */
250 
251   for (k = 0; k < n; k++) {
252     if (k < TFTP_FILENAME_MAX) {
253       dst[k] = src[k];
254     }
255     else {
256       return;
257     }
258 
259     if (src[k] == '\0') {
260       break;
261     }
262   }
263 
264   if (k >= n) {
265     return;
266   }
267 
268   k++;
269 
270   /* check mode */
271   if ((n - k) < 6) {
272     return;
273   }
274 
275   if (memcmp(&src[k], "octet\0", 6) != 0) {
276       tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
277       return;
278   }
279 
280   /* do sanity checks on the filename */
281 
282   if ((spt->filename[0] != '/')
283       || (spt->filename[strlen(spt->filename) - 1] == '/')
284       ||  strstr(spt->filename, "/../")) {
285       tftp_send_error(spt, 2, "Access violation", tp);
286       return;
287   }
288 
289   /* only allow exported prefixes */
290 
291   if (!tftp_prefix
292       || (strncmp(spt->filename, tftp_prefix, strlen(tftp_prefix)) != 0)) {
293       tftp_send_error(spt, 2, "Access violation", tp);
294       return;
295   }
296 
297   /* check if the file exists */
298 
299   if (tftp_read_data(spt, 0, (u_int8_t *)spt->filename, 0) < 0) {
300       tftp_send_error(spt, 1, "File not found", tp);
301       return;
302   }
303 
304   tftp_send_data(spt, 1, tp);
305 }
306 
tftp_handle_ack(struct tftp_t * tp,int pktlen)307 static void tftp_handle_ack(struct tftp_t *tp, int pktlen)
308 {
309   int s;
310 
311   s = tftp_session_find(tp);
312 
313   if (s < 0) {
314     return;
315   }
316 
317   if (tftp_send_data(&tftp_sessions[s],
318 		     ntohs(tp->x.tp_data.tp_block_nr) + 1,
319 		     tp) < 0) {
320     return;
321   }
322 }
323 
tftp_input(struct mbuf * m)324 void tftp_input(struct mbuf *m)
325 {
326   struct tftp_t *tp = (struct tftp_t *)m->m_data;
327 
328   switch(ntohs(tp->tp_op)) {
329   case TFTP_RRQ:
330     tftp_handle_rrq(tp, m->m_len);
331     break;
332 
333   case TFTP_ACK:
334     tftp_handle_ack(tp, m->m_len);
335     break;
336   }
337 }
338