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