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

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: 
1.1.1.6   root       95: static int tftp_read_data(struct tftp_session *spt, uint16_t block_nr,
                     96:                           uint8_t *buf, int len)
1.1       root       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,
1.1.1.6   root      158:                             uint16_t errorcode, const char *msg,
1.1.1.4   root      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: 
1.1.1.4   root      165:   m = m_get(spt->slirp);
1.1       root      166: 
                    167:   if (!m) {
1.1.1.4   root      168:     goto out;
1.1       root      169:   }
                    170: 
                    171:   memset(m->m_data, 0, m->m_size);
                    172: 
1.1.1.2   root      173:   m->m_data += IF_MAXLINKHDR;
1.1       root      174:   tp = (void *)m->m_data;
                    175:   m->m_data += sizeof(struct udpiphdr);
1.1.1.2   root      176: 
1.1       root      177:   tp->tp_op = htons(TFTP_ERROR);
                    178:   tp->x.tp_error.tp_error_code = htons(errorcode);
1.1.1.3   root      179:   pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
1.1       root      180: 
                    181:   saddr.sin_addr = recv_tp->ip.ip_dst;
                    182:   saddr.sin_port = recv_tp->udp.uh_dport;
                    183: 
                    184:   daddr.sin_addr = spt->client_ip;
                    185:   daddr.sin_port = spt->client_port;
                    186: 
1.1.1.2   root      187:   m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
1.1       root      188:         sizeof(struct ip) - sizeof(struct udphdr);
                    189: 
                    190:   udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
                    191: 
1.1.1.4   root      192: out:
1.1       root      193:   tftp_session_terminate(spt);
                    194: }
                    195: 
1.1.1.2   root      196: static int tftp_send_data(struct tftp_session *spt,
1.1.1.6   root      197:                           uint16_t block_nr,
1.1       root      198:                          struct tftp_t *recv_tp)
                    199: {
                    200:   struct sockaddr_in saddr, daddr;
                    201:   struct mbuf *m;
                    202:   struct tftp_t *tp;
                    203:   int nobytes;
                    204: 
                    205:   if (block_nr < 1) {
                    206:     return -1;
                    207:   }
                    208: 
1.1.1.4   root      209:   m = m_get(spt->slirp);
1.1       root      210: 
                    211:   if (!m) {
                    212:     return -1;
                    213:   }
                    214: 
                    215:   memset(m->m_data, 0, m->m_size);
                    216: 
1.1.1.2   root      217:   m->m_data += IF_MAXLINKHDR;
1.1       root      218:   tp = (void *)m->m_data;
                    219:   m->m_data += sizeof(struct udpiphdr);
1.1.1.2   root      220: 
1.1       root      221:   tp->tp_op = htons(TFTP_DATA);
                    222:   tp->x.tp_data.tp_block_nr = htons(block_nr);
                    223: 
                    224:   saddr.sin_addr = recv_tp->ip.ip_dst;
                    225:   saddr.sin_port = recv_tp->udp.uh_dport;
                    226: 
                    227:   daddr.sin_addr = spt->client_ip;
                    228:   daddr.sin_port = spt->client_port;
                    229: 
                    230:   nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
                    231: 
                    232:   if (nobytes < 0) {
                    233:     m_free(m);
                    234: 
                    235:     /* send "file not found" error back */
                    236: 
                    237:     tftp_send_error(spt, 1, "File not found", tp);
                    238: 
                    239:     return -1;
                    240:   }
                    241: 
1.1.1.2   root      242:   m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
1.1       root      243:         sizeof(struct ip) - sizeof(struct udphdr);
                    244: 
                    245:   udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
                    246: 
                    247:   if (nobytes == 512) {
                    248:     tftp_session_update(spt);
                    249:   }
                    250:   else {
                    251:     tftp_session_terminate(spt);
                    252:   }
                    253: 
                    254:   return 0;
                    255: }
                    256: 
1.1.1.4   root      257: static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
1.1       root      258: {
                    259:   struct tftp_session *spt;
1.1.1.4   root      260:   int s, k;
                    261:   size_t prefix_len;
                    262:   char *req_fname;
1.1       root      263: 
1.1.1.5   root      264:   /* check if a session already exists and if so terminate it */
                    265:   s = tftp_session_find(slirp, tp);
                    266:   if (s >= 0) {
                    267:     tftp_session_terminate(&slirp->tftp_sessions[s]);
                    268:   }
                    269: 
1.1.1.4   root      270:   s = tftp_session_allocate(slirp, tp);
1.1       root      271: 
                    272:   if (s < 0) {
                    273:     return;
                    274:   }
                    275: 
1.1.1.4   root      276:   spt = &slirp->tftp_sessions[s];
                    277: 
                    278:   /* unspecifed prefix means service disabled */
                    279:   if (!slirp->tftp_prefix) {
                    280:       tftp_send_error(spt, 2, "Access violation", tp);
                    281:       return;
                    282:   }
1.1       root      283: 
1.1.1.4   root      284:   /* skip header fields */
                    285:   k = 0;
                    286:   pktlen -= ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
                    287: 
                    288:   /* prepend tftp_prefix */
                    289:   prefix_len = strlen(slirp->tftp_prefix);
                    290:   spt->filename = qemu_malloc(prefix_len + TFTP_FILENAME_MAX + 2);
                    291:   memcpy(spt->filename, slirp->tftp_prefix, prefix_len);
                    292:   spt->filename[prefix_len] = '/';
1.1       root      293: 
                    294:   /* get name */
1.1.1.4   root      295:   req_fname = spt->filename + prefix_len + 1;
1.1       root      296: 
1.1.1.4   root      297:   while (1) {
                    298:     if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
                    299:       tftp_send_error(spt, 2, "Access violation", tp);
1.1       root      300:       return;
                    301:     }
1.1.1.4   root      302:     req_fname[k] = (char)tp->x.tp_buf[k];
                    303:     if (req_fname[k++] == '\0') {
1.1       root      304:       break;
                    305:     }
                    306:   }
1.1.1.2   root      307: 
1.1       root      308:   /* check mode */
1.1.1.4   root      309:   if ((pktlen - k) < 6) {
                    310:     tftp_send_error(spt, 2, "Access violation", tp);
1.1       root      311:     return;
                    312:   }
1.1.1.2   root      313: 
1.1.1.7 ! root      314:   if (strcasecmp((const char *)&tp->x.tp_buf[k], "octet") != 0) {
1.1       root      315:       tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
                    316:       return;
                    317:   }
                    318: 
1.1.1.2   root      319:   k += 6; /* skipping octet */
                    320: 
1.1       root      321:   /* do sanity checks on the filename */
1.1.1.4   root      322:   if (!strncmp(req_fname, "../", 3) ||
                    323:       req_fname[strlen(req_fname) - 1] == '/' ||
                    324:       strstr(req_fname, "/../")) {
1.1       root      325:       tftp_send_error(spt, 2, "Access violation", tp);
                    326:       return;
                    327:   }
                    328: 
                    329:   /* check if the file exists */
1.1.1.4   root      330:   if (tftp_read_data(spt, 0, NULL, 0) < 0) {
1.1       root      331:       tftp_send_error(spt, 1, "File not found", tp);
                    332:       return;
                    333:   }
                    334: 
1.1.1.4   root      335:   if (tp->x.tp_buf[pktlen - 1] != 0) {
1.1.1.2   root      336:       tftp_send_error(spt, 2, "Access violation", tp);
                    337:       return;
                    338:   }
                    339: 
1.1.1.4   root      340:   while (k < pktlen) {
1.1.1.2   root      341:       const char *key, *value;
                    342: 
1.1.1.4   root      343:       key = (const char *)&tp->x.tp_buf[k];
1.1.1.2   root      344:       k += strlen(key) + 1;
                    345: 
1.1.1.4   root      346:       if (k >= pktlen) {
1.1.1.2   root      347:          tftp_send_error(spt, 2, "Access violation", tp);
                    348:          return;
                    349:       }
                    350: 
1.1.1.4   root      351:       value = (const char *)&tp->x.tp_buf[k];
1.1.1.2   root      352:       k += strlen(value) + 1;
                    353: 
1.1.1.7 ! root      354:       if (strcasecmp(key, "tsize") == 0) {
1.1.1.2   root      355:          int tsize = atoi(value);
                    356:          struct stat stat_p;
                    357: 
1.1.1.4   root      358:          if (tsize == 0) {
                    359:              if (stat(spt->filename, &stat_p) == 0)
1.1.1.2   root      360:                  tsize = stat_p.st_size;
                    361:              else {
                    362:                  tftp_send_error(spt, 1, "File not found", tp);
                    363:                  return;
                    364:              }
                    365:          }
                    366: 
                    367:          tftp_send_oack(spt, "tsize", tsize, tp);
1.1.1.5   root      368:          return;
1.1.1.2   root      369:       }
                    370:   }
                    371: 
1.1       root      372:   tftp_send_data(spt, 1, tp);
                    373: }
                    374: 
1.1.1.4   root      375: static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
1.1       root      376: {
                    377:   int s;
                    378: 
1.1.1.4   root      379:   s = tftp_session_find(slirp, tp);
1.1       root      380: 
                    381:   if (s < 0) {
                    382:     return;
                    383:   }
                    384: 
1.1.1.4   root      385:   if (tftp_send_data(&slirp->tftp_sessions[s],
1.1.1.2   root      386:                     ntohs(tp->x.tp_data.tp_block_nr) + 1,
1.1       root      387:                     tp) < 0) {
                    388:     return;
                    389:   }
                    390: }
                    391: 
1.1.1.5   root      392: static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
                    393: {
                    394:   int s;
                    395: 
                    396:   s = tftp_session_find(slirp, tp);
                    397: 
                    398:   if (s < 0) {
                    399:     return;
                    400:   }
                    401: 
                    402:   tftp_session_terminate(&slirp->tftp_sessions[s]);
                    403: }
                    404: 
1.1       root      405: void tftp_input(struct mbuf *m)
                    406: {
                    407:   struct tftp_t *tp = (struct tftp_t *)m->m_data;
                    408: 
                    409:   switch(ntohs(tp->tp_op)) {
                    410:   case TFTP_RRQ:
1.1.1.4   root      411:     tftp_handle_rrq(m->slirp, tp, m->m_len);
1.1       root      412:     break;
                    413: 
                    414:   case TFTP_ACK:
1.1.1.4   root      415:     tftp_handle_ack(m->slirp, tp, m->m_len);
1.1       root      416:     break;
1.1.1.5   root      417: 
                    418:   case TFTP_ERROR:
                    419:     tftp_handle_error(m->slirp, tp, m->m_len);
                    420:     break;
1.1       root      421:   }
                    422: }

unix.superglobalmegacorp.com