File:  [Qemu by Fabrice Bellard] / qemu / slirp / tftp.c
Revision 1.1.1.8 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 19:02:49 2018 UTC (3 years, 1 month ago) by root
Branches: qemu, MAIN
CVS tags: qemu1000, qemu0151, HEAD
qemu 0.15.1

    1: /*
    2:  * tftp.c - a simple, read-only tftp server for qemu
    3:  *
    4:  * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
    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: #include "qemu-common.h"
   27: 
   28: static inline int tftp_session_in_use(struct tftp_session *spt)
   29: {
   30:     return (spt->slirp != NULL);
   31: }
   32: 
   33: static inline void tftp_session_update(struct tftp_session *spt)
   34: {
   35:     spt->timestamp = curtime;
   36: }
   37: 
   38: static void tftp_session_terminate(struct tftp_session *spt)
   39: {
   40:     qemu_free(spt->filename);
   41:     spt->slirp = NULL;
   42: }
   43: 
   44: static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
   45: {
   46:   struct tftp_session *spt;
   47:   int k;
   48: 
   49:   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
   50:     spt = &slirp->tftp_sessions[k];
   51: 
   52:     if (!tftp_session_in_use(spt))
   53:         goto found;
   54: 
   55:     /* sessions time out after 5 inactive seconds */
   56:     if ((int)(curtime - spt->timestamp) > 5000) {
   57:         qemu_free(spt->filename);
   58:         goto found;
   59:     }
   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;
   68:   spt->slirp = slirp;
   69: 
   70:   tftp_session_update(spt);
   71: 
   72:   return k;
   73: }
   74: 
   75: static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
   76: {
   77:   struct tftp_session *spt;
   78:   int k;
   79: 
   80:   for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
   81:     spt = &slirp->tftp_sessions[k];
   82: 
   83:     if (tftp_session_in_use(spt)) {
   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, uint16_t block_nr,
   96:                           uint8_t *buf, int len)
   97: {
   98:   int fd;
   99:   int bytes_read = 0;
  100: 
  101:   fd = open(spt->filename, O_RDONLY | O_BINARY);
  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: 
  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: 
  127:     m = m_get(spt->slirp);
  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);
  139:     n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
  140:                   key) + 1;
  141:     n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
  142:                   value) + 1;
  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: 
  157: static void tftp_send_error(struct tftp_session *spt,
  158:                             uint16_t errorcode, const char *msg,
  159:                             struct tftp_t *recv_tp)
  160: {
  161:   struct sockaddr_in saddr, daddr;
  162:   struct mbuf *m;
  163:   struct tftp_t *tp;
  164: 
  165:   m = m_get(spt->slirp);
  166: 
  167:   if (!m) {
  168:     goto out;
  169:   }
  170: 
  171:   memset(m->m_data, 0, m->m_size);
  172: 
  173:   m->m_data += IF_MAXLINKHDR;
  174:   tp = (void *)m->m_data;
  175:   m->m_data += sizeof(struct udpiphdr);
  176: 
  177:   tp->tp_op = htons(TFTP_ERROR);
  178:   tp->x.tp_error.tp_error_code = htons(errorcode);
  179:   pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
  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: 
  187:   m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
  188:         sizeof(struct ip) - sizeof(struct udphdr);
  189: 
  190:   udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
  191: 
  192: out:
  193:   tftp_session_terminate(spt);
  194: }
  195: 
  196: static int tftp_send_data(struct tftp_session *spt,
  197:                           uint16_t block_nr,
  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: 
  209:   m = m_get(spt->slirp);
  210: 
  211:   if (!m) {
  212:     return -1;
  213:   }
  214: 
  215:   memset(m->m_data, 0, m->m_size);
  216: 
  217:   m->m_data += IF_MAXLINKHDR;
  218:   tp = (void *)m->m_data;
  219:   m->m_data += sizeof(struct udpiphdr);
  220: 
  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: 
  242:   m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
  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: 
  257: static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
  258: {
  259:   struct tftp_session *spt;
  260:   int s, k;
  261:   size_t prefix_len;
  262:   char *req_fname;
  263: 
  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: 
  270:   s = tftp_session_allocate(slirp, tp);
  271: 
  272:   if (s < 0) {
  273:     return;
  274:   }
  275: 
  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:   }
  283: 
  284:   /* skip header fields */
  285:   k = 0;
  286:   pktlen -= offsetof(struct tftp_t, x.tp_buf);
  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] = '/';
  293: 
  294:   /* get name */
  295:   req_fname = spt->filename + prefix_len + 1;
  296: 
  297:   while (1) {
  298:     if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
  299:       tftp_send_error(spt, 2, "Access violation", tp);
  300:       return;
  301:     }
  302:     req_fname[k] = tp->x.tp_buf[k];
  303:     if (req_fname[k++] == '\0') {
  304:       break;
  305:     }
  306:   }
  307: 
  308:   /* check mode */
  309:   if ((pktlen - k) < 6) {
  310:     tftp_send_error(spt, 2, "Access violation", tp);
  311:     return;
  312:   }
  313: 
  314:   if (strcasecmp(&tp->x.tp_buf[k], "octet") != 0) {
  315:       tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
  316:       return;
  317:   }
  318: 
  319:   k += 6; /* skipping octet */
  320: 
  321:   /* do sanity checks on the filename */
  322:   if (!strncmp(req_fname, "../", 3) ||
  323:       req_fname[strlen(req_fname) - 1] == '/' ||
  324:       strstr(req_fname, "/../")) {
  325:       tftp_send_error(spt, 2, "Access violation", tp);
  326:       return;
  327:   }
  328: 
  329:   /* check if the file exists */
  330:   if (tftp_read_data(spt, 0, NULL, 0) < 0) {
  331:       tftp_send_error(spt, 1, "File not found", tp);
  332:       return;
  333:   }
  334: 
  335:   if (tp->x.tp_buf[pktlen - 1] != 0) {
  336:       tftp_send_error(spt, 2, "Access violation", tp);
  337:       return;
  338:   }
  339: 
  340:   while (k < pktlen) {
  341:       const char *key, *value;
  342: 
  343:       key = &tp->x.tp_buf[k];
  344:       k += strlen(key) + 1;
  345: 
  346:       if (k >= pktlen) {
  347: 	  tftp_send_error(spt, 2, "Access violation", tp);
  348: 	  return;
  349:       }
  350: 
  351:       value = &tp->x.tp_buf[k];
  352:       k += strlen(value) + 1;
  353: 
  354:       if (strcasecmp(key, "tsize") == 0) {
  355: 	  int tsize = atoi(value);
  356: 	  struct stat stat_p;
  357: 
  358: 	  if (tsize == 0) {
  359: 	      if (stat(spt->filename, &stat_p) == 0)
  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);
  368: 	  return;
  369:       }
  370:   }
  371: 
  372:   tftp_send_data(spt, 1, tp);
  373: }
  374: 
  375: static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
  376: {
  377:   int s;
  378: 
  379:   s = tftp_session_find(slirp, tp);
  380: 
  381:   if (s < 0) {
  382:     return;
  383:   }
  384: 
  385:   if (tftp_send_data(&slirp->tftp_sessions[s],
  386: 		     ntohs(tp->x.tp_data.tp_block_nr) + 1,
  387: 		     tp) < 0) {
  388:     return;
  389:   }
  390: }
  391: 
  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: 
  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:
  411:     tftp_handle_rrq(m->slirp, tp, m->m_len);
  412:     break;
  413: 
  414:   case TFTP_ACK:
  415:     tftp_handle_ack(m->slirp, tp, m->m_len);
  416:     break;
  417: 
  418:   case TFTP_ERROR:
  419:     tftp_handle_error(m->slirp, tp, m->m_len);
  420:     break;
  421:   }
  422: }

unix.superglobalmegacorp.com