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