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