Annotation of qemu/slirp/tftp.c, revision 1.1.1.5

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

unix.superglobalmegacorp.com