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