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