|
|
1.1 ! root 1: /* ! 2: * tftp.c - a simple, read-only tftp server for qemu ! 3: * ! 4: * Copyright (c) 2004 Magnus Damm <[email protected]> ! 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: 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: ! 106: fd = open(spt->filename, O_RDONLY | O_BINARY); ! 107: ! 108: if (fd < 0) { ! 109: return -1; ! 110: } ! 111: ! 112: if (len) { ! 113: lseek(fd, block_nr * 512, SEEK_SET); ! 114: ! 115: bytes_read = read(fd, buf, len); ! 116: } ! 117: ! 118: close(fd); ! 119: ! 120: return bytes_read; ! 121: } ! 122: ! 123: static int tftp_send_error(struct tftp_session *spt, ! 124: u_int16_t errorcode, const char *msg, ! 125: struct tftp_t *recv_tp) ! 126: { ! 127: struct sockaddr_in saddr, daddr; ! 128: struct mbuf *m; ! 129: struct tftp_t *tp; ! 130: ! 131: m = m_get(); ! 132: ! 133: if (!m) { ! 134: return -1; ! 135: } ! 136: ! 137: memset(m->m_data, 0, m->m_size); ! 138: ! 139: m->m_data += if_maxlinkhdr; ! 140: tp = (void *)m->m_data; ! 141: m->m_data += sizeof(struct udpiphdr); ! 142: ! 143: tp->tp_op = htons(TFTP_ERROR); ! 144: tp->x.tp_error.tp_error_code = htons(errorcode); ! 145: strncpy((char *)tp->x.tp_error.tp_msg, msg, sizeof(tp->x.tp_error.tp_msg)); ! 146: tp->x.tp_error.tp_msg[sizeof(tp->x.tp_error.tp_msg)-1] = 0; ! 147: ! 148: saddr.sin_addr = recv_tp->ip.ip_dst; ! 149: saddr.sin_port = recv_tp->udp.uh_dport; ! 150: ! 151: daddr.sin_addr = spt->client_ip; ! 152: daddr.sin_port = spt->client_port; ! 153: ! 154: m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) - ! 155: sizeof(struct ip) - sizeof(struct udphdr); ! 156: ! 157: udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); ! 158: ! 159: tftp_session_terminate(spt); ! 160: ! 161: return 0; ! 162: } ! 163: ! 164: static int tftp_send_data(struct tftp_session *spt, ! 165: u_int16_t block_nr, ! 166: struct tftp_t *recv_tp) ! 167: { ! 168: struct sockaddr_in saddr, daddr; ! 169: struct mbuf *m; ! 170: struct tftp_t *tp; ! 171: int nobytes; ! 172: ! 173: if (block_nr < 1) { ! 174: return -1; ! 175: } ! 176: ! 177: m = m_get(); ! 178: ! 179: if (!m) { ! 180: return -1; ! 181: } ! 182: ! 183: memset(m->m_data, 0, m->m_size); ! 184: ! 185: m->m_data += if_maxlinkhdr; ! 186: tp = (void *)m->m_data; ! 187: m->m_data += sizeof(struct udpiphdr); ! 188: ! 189: tp->tp_op = htons(TFTP_DATA); ! 190: tp->x.tp_data.tp_block_nr = htons(block_nr); ! 191: ! 192: saddr.sin_addr = recv_tp->ip.ip_dst; ! 193: saddr.sin_port = recv_tp->udp.uh_dport; ! 194: ! 195: daddr.sin_addr = spt->client_ip; ! 196: daddr.sin_port = spt->client_port; ! 197: ! 198: nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512); ! 199: ! 200: if (nobytes < 0) { ! 201: m_free(m); ! 202: ! 203: /* send "file not found" error back */ ! 204: ! 205: tftp_send_error(spt, 1, "File not found", tp); ! 206: ! 207: return -1; ! 208: } ! 209: ! 210: m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - ! 211: sizeof(struct ip) - sizeof(struct udphdr); ! 212: ! 213: udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); ! 214: ! 215: if (nobytes == 512) { ! 216: tftp_session_update(spt); ! 217: } ! 218: else { ! 219: tftp_session_terminate(spt); ! 220: } ! 221: ! 222: return 0; ! 223: } ! 224: ! 225: static void tftp_handle_rrq(struct tftp_t *tp, int pktlen) ! 226: { ! 227: struct tftp_session *spt; ! 228: int s, k, n; ! 229: u_int8_t *src, *dst; ! 230: ! 231: s = tftp_session_allocate(tp); ! 232: ! 233: if (s < 0) { ! 234: return; ! 235: } ! 236: ! 237: spt = &tftp_sessions[s]; ! 238: ! 239: src = tp->x.tp_buf; ! 240: dst = (u_int8_t *)spt->filename; ! 241: n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp); ! 242: ! 243: /* get name */ ! 244: ! 245: for (k = 0; k < n; k++) { ! 246: if (k < TFTP_FILENAME_MAX) { ! 247: dst[k] = src[k]; ! 248: } ! 249: else { ! 250: return; ! 251: } ! 252: ! 253: if (src[k] == '\0') { ! 254: break; ! 255: } ! 256: } ! 257: ! 258: if (k >= n) { ! 259: return; ! 260: } ! 261: ! 262: k++; ! 263: ! 264: /* check mode */ ! 265: if ((n - k) < 6) { ! 266: return; ! 267: } ! 268: ! 269: if (memcmp(&src[k], "octet\0", 6) != 0) { ! 270: tftp_send_error(spt, 4, "Unsupported transfer mode", tp); ! 271: return; ! 272: } ! 273: ! 274: /* do sanity checks on the filename */ ! 275: ! 276: if ((spt->filename[0] != '/') ! 277: || (spt->filename[strlen(spt->filename) - 1] == '/') ! 278: || strstr(spt->filename, "/../")) { ! 279: tftp_send_error(spt, 2, "Access violation", tp); ! 280: return; ! 281: } ! 282: ! 283: /* only allow exported prefixes */ ! 284: ! 285: if (!tftp_prefix ! 286: || (strncmp(spt->filename, tftp_prefix, strlen(tftp_prefix)) != 0)) { ! 287: tftp_send_error(spt, 2, "Access violation", tp); ! 288: return; ! 289: } ! 290: ! 291: /* check if the file exists */ ! 292: ! 293: if (tftp_read_data(spt, 0, (u_int8_t *)spt->filename, 0) < 0) { ! 294: tftp_send_error(spt, 1, "File not found", tp); ! 295: return; ! 296: } ! 297: ! 298: tftp_send_data(spt, 1, tp); ! 299: } ! 300: ! 301: static void tftp_handle_ack(struct tftp_t *tp, int pktlen) ! 302: { ! 303: int s; ! 304: ! 305: s = tftp_session_find(tp); ! 306: ! 307: if (s < 0) { ! 308: return; ! 309: } ! 310: ! 311: if (tftp_send_data(&tftp_sessions[s], ! 312: ntohs(tp->x.tp_data.tp_block_nr) + 1, ! 313: tp) < 0) { ! 314: return; ! 315: } ! 316: } ! 317: ! 318: void tftp_input(struct mbuf *m) ! 319: { ! 320: struct tftp_t *tp = (struct tftp_t *)m->m_data; ! 321: ! 322: switch(ntohs(tp->tp_op)) { ! 323: case TFTP_RRQ: ! 324: tftp_handle_rrq(tp, m->m_len); ! 325: break; ! 326: ! 327: case TFTP_ACK: ! 328: tftp_handle_ack(tp, m->m_len); ! 329: break; ! 330: } ! 331: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.