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