Exemplo n.º 1
0
/*--------------------------------------------------------------------------
  Encodes an LCP protocol reject packet into the given buffer. Returns
  number of bytes encoded.
  --------------------------------------------------------------------------*/
static SshUInt32
ssh_wan_encode_lcp_protocol_reject(PUCHAR buffer, 
                                   ULONG length,
                                   SshUInt32 ppp_flags, 
                                   SshUInt32 lcp_id,
                                   SshUInt32 protocol,
                                   PUCHAR packet, 
                                   ULONG packet_size)
{
  PUCHAR pos = buffer, start;
  ULONG len = length;

  if (!ssh_wan_encode_ppp_header(&pos, &len, SSH_PPP_PROTOCOL_LCP, ppp_flags))
    return 0;

  start = pos;
  if (!ssh_wan_encode_cp_header(&pos, &len,
                                SSH_PPP_CODE_PROTOCOL_REJECT, lcp_id, 0))
    return 0;

  if (len < 2)
    return 0;

  SSH_PUT_16BIT(pos, protocol);
  pos += 2;
  len -= 2;

  if (packet_size > len)
    packet_size = len;

  ssh_wan_encode_data(&pos, &len, packet, packet_size);
  ssh_wan_set_cp_length(start, pos);
  return pos - buffer;
}
Exemplo n.º 2
0
/* Encode name. Return the number of bytes written to buffer. */
size_t ssh_dns_packet_encode_name(unsigned char *buffer,
				  size_t buffer_length,
				  size_t offset,
				  unsigned char *name, 
				  SshDNSCompressMap map)
{
  size_t len, len2;
  SshUInt16 compression_ptr;
  Boolean added;

  len = 0;
  added = FALSE;
  while (*name != 0)
    {
      compression_ptr = ssh_dns_compress_map_find(map, name);
      if (compression_ptr != 0 && compression_ptr != offset)
	{
	  /* We can compress rest away, we need 2 bytes for it. */
	  if (buffer_length < offset + 2)
	    return 0;
	  SSH_PUT_16BIT(buffer + offset, compression_ptr | 0xc000);
	  return len + 2;
	}

      /* Add entry to the compression map, if we haven't yet added it there. */
      if (!added)
	{
	  ssh_dns_compress_map_add(map, name, offset);
	  added = TRUE;
	}

      /* Ok, check the length and copy the label. */
      len2 = (*name) + 1;
      if (buffer_length < offset + len2)
	return 0;

      /* Copy data. */
      memcpy(buffer + offset, name, len2);

      /* Move to the next label. */
      name += len2;
      offset += len2;
      len += len2;
    }
  /* Mark the end. */
  buffer[offset] = '\0';
  return len + 1;
}
Exemplo n.º 3
0
/*
 * Make socks reply packet that can be sent to client and store it to buffer.
 * If connection is granted set command_code to SSH_SOCKS_REPLY_GRANTED,
 * otherwise set it to some error code (SSH_SOCKS_REPLY_FAILED_*).
 * The port and ip from the socksinfo are sent along with reply and if
 * the request that was granted was bind they should indicate the port and ip
 * address of the other end of the socket.
 * Does NOT free the SocksInfo structure.
 */
SocksError ssh_socks4_server_generate_reply(SshBuffer buffer,
                                            SocksInfo socksinfo)
{
  unsigned char *data;
  int port;
  SshIpAddrStruct ip_addr;

  port = ssh_inet_get_port_by_service(socksinfo->port, ssh_custr("tcp"));
  if (port >= 65536 || port <= 0)
    return SSH_SOCKS_ERROR_INVALID_ARGUMENT;

  if (!ssh_ipaddr_parse(&ip_addr, socksinfo->ip))
    {
      SSH_DEBUG(2, ("Couldn't parse IP-address `%s'.", socksinfo->ip));
      return SSH_SOCKS_ERROR_INVALID_ARGUMENT;
    }

  /* nowadays the ip-addresses returned by ssh functions is more and more
     often in ipv6 format (ipv4 addresses are in ipv6 mapped ipv4 format). */
  ssh_inet_convert_ip6_mapped_ip4_to_ip4(&ip_addr);

  if (!SSH_IP_IS4(&ip_addr))
    {
      SSH_DEBUG(2, ("IP-address `%s' isn't an IPv4 numerical address.",
                    socksinfo->ip));
      return SSH_SOCKS_ERROR_INVALID_ARGUMENT;
    }

  if (ssh_buffer_append_space(buffer, &data, SOCKS4_REPLY_SIZE)
      != SSH_BUFFER_OK)
    {
      SSH_DEBUG(2, ("Failed to allocate reply buffer."));
      return SSH_SOCKS_ERROR_INVALID_ARGUMENT;
    }

  *data++ = 0; /* SOCKS4 replys must have version number '0'. */
  *data++ = socksinfo->command_code;
  SSH_PUT_16BIT(data, port);
  data += 2;
  SSH_IP4_ENCODE(&ip_addr, data);
  return SSH_SOCKS_SUCCESS;
}
Exemplo n.º 4
0
/*--------------------------------------------------------------------------
  Encodes a PPP control protocol header into the given buffer. Returns
  TRUE if the header was completely encoded, FALSE otherwise.
  --------------------------------------------------------------------------*/
static Boolean
ssh_wan_encode_cp_header(PUCHAR *pos, 
                         ULONG *len, 
                         SshUInt32
                         code, SshUInt32 id, 
                         SshUInt32 datalen)
{
  if (*len < 4) return FALSE;

  *(*pos)++ = (UCHAR)code;
  *(*pos)++ = (UCHAR)id;

  SSH_PUT_16BIT(*pos, datalen + 4);
  *pos += 2;

  *len -= 4;

  if (datalen > *len)
    return FALSE;

  return TRUE;
}
Exemplo n.º 5
0
/*--------------------------------------------------------------------------
  Encodes a PPP header into the given buffer. Return TRUE if the
  header was completely encoded, FALSE otherwise.
  --------------------------------------------------------------------------*/
static Boolean
ssh_wan_encode_ppp_header(PUCHAR *pos, 
                          ULONG *len,
                          SshUInt32 protocol, 
                          SshUInt32 flags)
{
  PUCHAR start = *pos;

  /* Ensure minimal length for non-compressed PPP address, control and
     protocol fields. */
  if (*len < 4)
    return FALSE;

  /* Add address and control bytes unless they are omitted by
     Address-and-Control-Field-Compression (ACFC). */
  if (!(flags & SSH_PPP_FLAG_ACFC))
    {
      *(*pos)++ = 0xff;
      *(*pos)++ = 0x03;
    }

  /* Add protocol number and compress if Protocol-Field-Compression
     (PFC) is on and compression is possible. */
  if (!(flags & SSH_PPP_FLAG_PFC) || protocol > 0xff)
    {
      SSH_PUT_16BIT(*pos, protocol);
      *pos += 2;
    }
  else
    {
      *(*pos)++ = protocol;
    }

  *len -= *pos - start;
  return TRUE;
}
Exemplo n.º 6
0
/*--------------------------------------------------------------------------
  Sets the length field of a PPP control protocol frame.
  --------------------------------------------------------------------------*/
static void
ssh_wan_set_cp_length(PUCHAR start, 
                      PUCHAR end)
{
  SSH_PUT_16BIT(start + 2, end - start);
}
void
ssh_virtual_adapter_send(SshInterceptor interceptor,
			 SshInterceptorPacket pp)
{
  SshVirtualAdapter adapter;
  SshInterceptorInternalPacket ipp = (SshInterceptorInternalPacket) pp;
  struct net_device_stats *stats;
  struct sk_buff *skb;

  local_bh_disable();
  ssh_kernel_mutex_lock(interceptor->interceptor_lock);
  adapter = ssh_virtual_adapter_ifnum_to_adapter(interceptor, pp->ifnum_out);
  if (adapter == NULL)
    {
      ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
      local_bh_enable();
      SSH_DEBUG(SSH_D_ERROR,
                ("Virtual adapter %d does not exist", (int)pp->ifnum_out));
      goto error;
    }

  /* Check the type of the source packet. */
  if (pp->protocol == SSH_PROTOCOL_ETHERNET)
    {
      /* We can send this directly. */
    }
  else if (pp->protocol == SSH_PROTOCOL_IP4
#ifdef SSH_LINUX_INTERCEPTOR_IPV6
           || pp->protocol == SSH_PROTOCOL_IP6
#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */
	   )
    {
      unsigned char ether_hdr[SSH_ETHERH_HDRLEN];
      SshIpAddrStruct src;
      SshUInt16 ethertype = SSH_ETHERTYPE_IP;
      unsigned char *cp = NULL;
      size_t packet_len;

      /* Add ethernet framing. */

      /* Destination is virtual adapter's ethernet address. */
      memcpy(ether_hdr + SSH_ETHERH_OFS_DST, adapter->dev->dev_addr,
             SSH_ETHERH_ADDRLEN);

      /* Resolve packet's source and the ethernet type to use. */
      packet_len = ssh_interceptor_packet_len(pp);

      /* IPv4 */
      if (pp->protocol == SSH_PROTOCOL_IP4)
        {
          if (packet_len < SSH_IPH4_HDRLEN)
            {
	      ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
	      local_bh_enable();
              SSH_DEBUG(SSH_D_ERROR,
                        ("Packet is too short to contain IPv4 header"));
              goto error;
            }

	  /* Pullup requests data from the header of a writable skb. */
	  if (likely(skb_headlen(ipp->skb) >= SSH_IPH4_HDRLEN
		     && !skb_shared(ipp->skb) && 
		     SSH_SKB_WRITABLE(ipp->skb, SSH_IPH4_HDRLEN)))
	    cp = ipp->skb->data;

          if (cp == NULL)
	    {
	      ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
	      local_bh_enable();
	      goto error_already_freed;
	    }

          SSH_IPH4_SRC(&src, cp);
        }

#ifdef SSH_LINUX_INTERCEPTOR_IPV6
      /* IPv6 */
      else
        {
          if (packet_len < SSH_IPH6_HDRLEN)
            {
	      ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
	      local_bh_enable();
              SSH_DEBUG(SSH_D_ERROR,
                        ("Packet too short to contain IPv6 header"));
              goto error;
            }

	  if (likely(skb_headlen(ipp->skb) >= SSH_IPH6_HDRLEN
		     && !skb_shared(ipp->skb) && 
		     SSH_SKB_WRITABLE(ipp->skb, SSH_IPH6_HDRLEN)))
	    cp = ipp->skb->data;
	    
          if (cp == NULL)
	    {
	      ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
	      local_bh_enable();
	      goto error_already_freed;
	    }

          SSH_IPH6_SRC(&src, cp);
          ethertype = SSH_ETHERTYPE_IPv6;
        }
#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */

      /* Finalize ethernet header. */
      ssh_virtual_adapter_ip_ether_address(&src,
                                           ether_hdr + SSH_ETHERH_OFS_SRC);
      SSH_PUT_16BIT(ether_hdr + SSH_ETHERH_OFS_TYPE, ethertype);

      /* Insert header to the packet. */
      cp = NULL;
      if (likely((skb_headroom(ipp->skb) >= 
		  (SSH_ETHERH_HDRLEN + SSH_INTERCEPTOR_PACKET_HARD_HEAD_ROOM))
		 && !skb_shared(ipp->skb) && SSH_SKB_WRITABLE(ipp->skb, 0)))
	cp = skb_push(ipp->skb, SSH_ETHERH_HDRLEN);

      if (cp == NULL)
	{
	  ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
	  goto error_already_freed;
	}
      memcpy(cp, ether_hdr, SSH_ETHERH_HDRLEN);

      /* Just to be pedantic. */
      pp->protocol = SSH_PROTOCOL_ETHERNET;
    }
  else
    {
      ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
      local_bh_enable();
      SSH_DEBUG(SSH_D_ERROR, ("Can not handle protocol %d", pp->protocol));
      goto error;
    }

  /* Tear off the internal packet from the generic SshInterceptorPacket. */
  skb = ipp->skb;
  ipp->skb = NULL;

  /* (re-)receive the packet via the interface; this should
     make the packet go back up the stack */
  skb->protocol = eth_type_trans(skb, adapter->dev);
  skb->dev = adapter->dev;

  /* Update per virtual adapter statistics. */ 
  stats = &adapter->low_level_stats;
  stats->rx_packets++;
  stats->rx_bytes += skb->len;

  ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
  local_bh_enable();

  /* Send the skb up towards stack. If it is IP (or ARP), it will be 
     intercepted by ssh_interceptor_packet_in. */
  netif_rx(skb);
  
  /* Put the packet header on freelist. */
  ssh_interceptor_packet_free((SshInterceptorPacket) ipp);
  return;
  
 error:
  ssh_interceptor_packet_free(pp);

 error_already_freed:
  return;
}
Exemplo n.º 8
0
SshTlsTransStatus ssh_tls_trans_write_client_hello(SshTlsProtocolState s)
{
  SshTlsCipherSuite tab[SSH_TLS_NUM_CIPHERSUITES];
  SshTlsCipherSuite *suites_to_send;
  SshTlsCipherSuiteDetailsStruct details;
  int number_suites;
  int contents_len;
  unsigned char tempbuf[2];
  int i;

  s->kex.flags |= SSH_TLS_KEX_NEW_SESSION; /* Initially. */

  if (s->conf.preferred_suites != NULL)
    {

      /* If a preference list has been given, check that the
         preferences are sound w.r.t. the protocol flags given in the
         configuration. */

      SshTlsCipherSuite *tmp;

      SSH_DEBUG(6, ("Got a preference list."));

      number_suites = 0;
      tmp = s->conf.preferred_suites;
      while (*tmp != SSH_TLS_NO_CIPHERSUITE)
        {
          ssh_tls_get_ciphersuite_details(*tmp, &details);
          if (details.crippled && (!(s->conf.flags & SSH_TLS_WEAKCIPHERS)))
            {
	      return SSH_TLS_TRANS_FAILED;
            }
          if (details.cipher == SSH_TLS_CIPH_NULL &&
              (!(s->conf.flags & SSH_TLS_NULLCIPHER)))
            {
	      return SSH_TLS_TRANS_FAILED;
            }
          tmp++; number_suites++;
        }
      suites_to_send = s->conf.preferred_suites;
    }
  else
    {
      /* Otherwise construct a list containing all those ciphersuites
         that are supported by our implementation and that can be used
         according to the protocol configuration flags. */

      number_suites = 0;

      for (i = SSH_TLS_RSA_WITH_NULL_MD5; i < SSH_TLS_MAX_CIPHERSUITE; i++)
        {
          ssh_tls_get_ciphersuite_details(i, &details);

          SSH_DEBUG(7, ("Check if suite %d can be supported.", i));

	  if ((details.kex_method == SSH_TLS_UNKNOWN_SUITE)
	       || !(ssh_tls_supported_suite(s->conf.flags, 
					    (SshTlsCipherSuite) i)))
	     continue;

          SSH_DEBUG(7, ("Null? %d  Crippled? %d  RC2? %d  RSA? %d  "
                        "Nosign? %d",
                        (details.cipher == SSH_TLS_CIPH_NULL),
                        (details.crippled),
                        (details.cipher == SSH_TLS_CIPH_RC2),
                        (details.kex_method == SSH_TLS_KEX_RSA),
                        (details.signature_method == SSH_TLS_SIGN_NONE)));

          if ((details.cipher == SSH_TLS_CIPH_NULL &&
               (!(s->conf.flags & SSH_TLS_NULLCIPHER)))
              || (details.crippled && (!(s->conf.flags & SSH_TLS_WEAKCIPHERS)))
              || (details.cipher == SSH_TLS_CIPH_RC2)
              || (details.kex_method != SSH_TLS_KEX_RSA)
              || (details.signature_method != SSH_TLS_SIGN_NONE))
            continue;

          SSH_DEBUG(7, ("Adding the cipher suite %d.", i));

          tab[number_suites++] = i;
        }
      suites_to_send = tab;
    }

  /* Now we can calculate the length of the packet. */

  /* Protocol version 2 bytes, random value 32 bytes,
     session ID 1 + N bytes, ciphersuites list 2 bytes (length)
     plus number_suites * 2 bytes, and 2 bytes for the single
     compression method (no compression) we support. */

  contents_len = 2 + 32 + 1 + s->kex.id_len + 2 + number_suites * 2 + 2;

  /* Initialize the handshake history buffer now. */
  SSH_ASSERT(s->kex.handshake_history == NULL);
  s->kex.handshake_history = ssh_buffer_allocate();

  if (s->kex.handshake_history == NULL)
    return SSH_TLS_TRANS_FAILED;

  ssh_tls_make_hs_header(s, SSH_TLS_HS_CLIENT_HELLO, contents_len);

  /* Write the highest protocol version or that of a hopefully-resumed
     session. */

  tempbuf[0] = s->kex.client_version.major;
  tempbuf[1] = s->kex.client_version.minor;

  ssh_tls_add_to_kex_packet(s, tempbuf, 2);

  /* Write unix time. */
  SSH_PUT_32BIT(s->kex.client_random, (SshUInt32)ssh_time());

  /* Write 28 random bytes. */
  for (i = 4; i < 32; i++)
    {
      s->kex.client_random[i]
        = ssh_random_get_byte();
    }
  ssh_tls_add_to_kex_packet(s, s->kex.client_random, 32);

  /* Write the requested session identifier. */
  tempbuf[0] = s->kex.id_len;
  ssh_tls_add_to_kex_packet(s, &tempbuf[0], 1);
  if (s->kex.id_len > 0)
    {
      ssh_tls_add_to_kex_packet(s, s->kex.session_id, s->kex.id_len);
    }

  /* Write the cipher suites. */
  SSH_PUT_16BIT(tempbuf, number_suites * 2);
  ssh_tls_add_to_kex_packet(s, tempbuf, 2);

  for (i = 0; i < number_suites; i++)
    {
      SSH_PUT_16BIT(tempbuf, suites_to_send[i]);
      ssh_tls_add_to_kex_packet(s, tempbuf, 2);
    }

  /* And the compression method. */
  tempbuf[0] = 1;
  tempbuf[1] = 0; /* the null compression */
  ssh_tls_add_to_kex_packet(s, tempbuf, 2);

  s->kex.state = SSH_TLS_KEX_WAIT_S_HELLO;

  return SSH_TLS_TRANS_OK;
}
Exemplo n.º 9
0
/*--------------------------------------------------------------------------
  ssh_interceptor_send()
  
  Sends packet either down to the network or up to the protocol.
  --------------------------------------------------------------------------*/
void
ssh_interceptor_send(SshInterceptor interceptor,
                     SshInterceptorPacket ip,
                     size_t media_header_len)
{
  SshNdisPacket packet;
  ULONG first_buf_len = SSH_ETHERH_HDRLEN;
  SshNdisIMAdapter adapter;
  SshCpuContext cpu_ctx;
  Boolean use_one_buffer = FALSE;
  ULONG new_value;

  /* Sanity checks for arguments */
  SSH_ASSERT(interceptor != NULL);
  SSH_ASSERT(ip != NULL);
  SSH_ASSERT((ip->flags & SSH_PACKET_FROMADAPTER) !=
              (ip->flags & SSH_PACKET_FROMPROTOCOL));
#ifndef _WIN32_WCE
  SSH_ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
#endif /* _WIN32_WCE */

  cpu_ctx = &interceptor->cpu_ctx[ssh_kernel_get_cpu()];

  packet = CONTAINING_RECORD(ip, SshNdisPacketStruct, ip);

#ifdef DEBUG_LIGHT
  packet->f.flags.in_engine = 0;
#endif /* DEBUG_LIGHT */

  adapter = (SshNdisIMAdapter)packet->adapter_in;

  /* Check if adapter where the packet should be sent is
     different where the packet originated from */
  if (adapter && (adapter->ifnum == ip->ifnum_out))
    {
      new_value = InterlockedIncrement(&adapter->ref_count);
      packet->adapter_out = (SshAdapter)adapter;
    }
  else
    {
      SshAdapter gen_adapter = NULL;

      if (ip->ifnum_out < SSH_INTERCEPTOR_MAX_ADAPTERS)
        {
          ssh_kernel_rw_mutex_lock_read(&interceptor->adapter_lock);
          gen_adapter = interceptor->adapter_table[ip->ifnum_out];
          if (gen_adapter)
            {
              new_value = InterlockedIncrement(&gen_adapter->ref_count);
              packet->adapter_out = gen_adapter;
            }
          ssh_kernel_rw_mutex_unlock_read(&interceptor->adapter_lock);
        }

      if (gen_adapter == NULL)
        goto free_packet;

      adapter = (SshNdisIMAdapter)gen_adapter;
    }
  SSH_ASSERT(new_value > 0);

  /* Check that active adapter found and it supports the protocol */
  if (!ssh_adapter_is_enabled((SshNdisIMAdapter)adapter))
    {
      SSH_DEBUG(SSH_D_FAIL, ("active network connection not found"));
      goto free_packet;
    }

#ifndef _WIN32_WCE
  /* Check if packet is plain IPv4 (IPv6) and then add ethernet framing */
  if (ip->protocol == SSH_PROTOCOL_IP4 || ip->protocol == SSH_PROTOCOL_IP6)
    {
      if (!ssh_wan_packet_encapsulate((SshAdapter)adapter, ip))
        {
          SSH_DEBUG(SSH_D_FAIL, ("packet framing failed"));
          goto free_packet;
        }

      /* Some dial-up drivers seem to expect to receive whole packet in one
         NDIS buffer. */
      if (ip->flags & SSH_PACKET_FROMADAPTER)
        use_one_buffer = TRUE;
    }
#endif /* _WIN32_WCE */

  /* Add the VLAN tagging, if any */
  if (packet->vlan_tag_count > 0)
    {
      if (adapter != (SshNdisIMAdapter)packet->adapter_in)
        {
          /* Engine forwards this packet to different interface. Check 
             whether this is VLAN/QoS enabled interface and reconstruct
             tagging accordingly. */

          switch (adapter->options 
                  & (NDIS_MAC_OPTION_8021Q_VLAN
                     | NDIS_MAC_OPTION_8021P_PRIORITY))
            {
            case 0:
              /* Adapter doesn't support IEEE 802.1q/p; drop VLAN tag(s). */
              packet->vlan_tag_count = 0;
              break;

            case NDIS_MAC_OPTION_8021P_PRIORITY:
              /* Adapter supports only priority (QoS) tagging. */
              packet->vlan_tags[0].vlan_id = 0;
              packet->vlan_tag_count = 1;
              break;

            default:
              /* Adapter supports also VLAN. Change the VLAN ID of the
                 first tag to the one configued to this NIC driver. */
              packet->vlan_tags[0].vlan_id = adapter->vlan_id;
              break;
            }
        }

      if (packet->vlan_tag_count)
        {
          unsigned char *vlan_tags;
          SshUInt16 i;

          vlan_tags = 
            ssh_interceptor_packet_insert(ip, SSH_ETHERH_OFS_TYPE,
                                          packet->vlan_tag_count * 4);
          if (vlan_tags == NULL)
            {
              SSH_DEBUG(SSH_D_FAIL, ("Failed to add VLAN tags"));
              return;
            }

          for (i = 0; i < packet->vlan_tag_count; i++)
            {
              unsigned char *tag = vlan_tags + (i * 4);

              SSH_PUT_16BIT(tag, SSH_ETHERTYPE_VLAN);
              SSH_PUT_16BIT((tag + 2), 
                            (packet->vlan_tags[i].vlan_id << 4
                            | packet->vlan_tags[i].qos));
            }
        }
    }

  NDIS_SET_PACKET_HEADER_SIZE(packet->np, SSH_ETHERH_HDRLEN);
  NDIS_SET_PACKET_STATUS(packet->np, NDIS_STATUS_SUCCESS);

#ifdef _WIN32_WCE
  if (adapter->media == NdisMediumWan)
    {
      if (ip->flags & SSH_PACKET_FROMPROTOCOL)
        {
          if (!ssh_wan_send_to_adapter(adapter, packet, ip->protocol))
            SSH_DEBUG(SSH_D_FAIL, ("Cannot send packet to WAN adapter"));
        }
      else if (ip->flags & SSH_PACKET_FROMADAPTER)
        {
          if (!ssh_wan_send_to_protocol(adapter, packet, ip->protocol))
            SSH_DEBUG(SSH_D_FAIL, ("Cannot send packet to WAN protocol"));
        }
      else
        {
          SSH_DEBUG(SSH_D_ERROR, ("Dropping WAN packet without direction"));
        }
      ssh_interceptor_packet_free(&packet->ip);
      return;
    }
#endif /* _WIN32_WCE */

  if (ip->flags & SSH_PACKET_FROMPROTOCOL)
    {
      NDIS_STATUS status;

      /* Send packet to network */
      NdisSetPacketFlags(packet->np, NDIS_FLAGS_DONT_LOOPBACK);

      if (cpu_ctx->in_packet_cb 
	  || cpu_ctx->in_route_cb
	  || cpu_ctx->in_timeout_cb)
        {
          SSH_DEBUG(SSH_D_NICETOKNOW, 
                    ("Risk for recursive call; enqueueing packet 0x%p",
                     packet));

#ifdef DEBUG_LIGHT
          packet->f.flags.in_send_queue = 1;
#endif /* DEBUG_LIGHT */
	  if (cpu_ctx->in_packet_cb)
	    {
	      ssh_net_packet_enqueue(&cpu_ctx->send_queue[adapter->ifnum],
				     (SshNetDataPacket)packet);
	      cpu_ctx->packets_in_send_queue = 1;
	    }
	  else if (cpu_ctx->in_route_cb)
	    {
	      ssh_net_packet_enqueue(&cpu_ctx->route_send_queue[adapter->ifnum],
				     (SshNetDataPacket)packet);
	      cpu_ctx->packets_in_route_send_queue = 1;
	    }
	  else if (cpu_ctx->in_timeout_cb)
	    {
	      ssh_net_packet_enqueue(
                             &cpu_ctx->timeout_send_queue[adapter->ifnum],
			     (SshNetDataPacket)packet);
	      cpu_ctx->packets_in_timeout_send_queue = 1;
	    }
        }
      else
        {
#ifdef DEBUG_LIGHT
          SSH_DEBUG(SSH_D_NICETOKNOW, 
                    ("Sending packet 0x%p to underlying driver",
                     packet));

          packet->f.flags.in_miniport = 1;
#endif /* DEBUG_LIGHT */
          NdisSend(&status, adapter->binding_handle, packet->np);

          if (status != NDIS_STATUS_PENDING)
            {
              SSH_DEBUG(SSH_D_NICETOKNOW,
                        ("Send operation completed synchronously; "
                         "packet=0x%p, status=%@",
                         ssh_ndis_status_render, status));
              ssh_interceptor_packet_free(&packet->ip);
            }
        }
    }
  else if (ip->flags & SSH_PACKET_FROMADAPTER)
    {
      /* Packet is ready now so check packet consistency */
      if (use_one_buffer)
        first_buf_len = packet->packet_len;
      else
        first_buf_len += adapter->lookahead_size;
      first_buf_len = MIN(first_buf_len, packet->packet_len);
        
      if (!ssh_packet_get_contiguous_data((SshNetDataPacket)packet, 
                                          0, first_buf_len, FALSE))
        {
          SSH_DEBUG(SSH_D_FAIL, ("Invalid packet"));
          goto free_packet;
        }

      if (cpu_ctx->in_packet_cb
	  || cpu_ctx->in_route_cb
	  || cpu_ctx->in_timeout_cb)
        {
          SSH_DEBUG(SSH_D_NICETOKNOW, 
                    ("Risk for recursive call; enqueueing packet 0x%p",
                     packet));

#ifdef DEBUG_LIGHT
          packet->f.flags.in_recv_queue = 1;
#endif /* DEBUG_LIGHT */
	  if (cpu_ctx->in_packet_cb)
	    {
	      ssh_net_packet_enqueue(&cpu_ctx->recv_queue[adapter->ifnum],
				     (SshNetDataPacket)packet);
	      cpu_ctx->packets_in_recv_queue = 1;
	    }
	  else if (cpu_ctx->in_route_cb)
	    {
	      ssh_net_packet_enqueue(&cpu_ctx->route_recv_queue[adapter->ifnum],
				     (SshNetDataPacket)packet);
	      cpu_ctx->packets_in_route_recv_queue = 1;
	    }
	  else if (cpu_ctx->in_timeout_cb)
	    {
	      ssh_net_packet_enqueue(
			     &cpu_ctx->timeout_recv_queue[adapter->ifnum],
			     (SshNetDataPacket)packet);
	      cpu_ctx->packets_in_timeout_recv_queue = 1;
	    }
        }
      else
        {
          SSH_DEBUG(SSH_D_NICETOKNOW, 
                    ("Indicating packet 0x%p to upper layers",
                     packet));

#ifdef DEBUG_LIGHT
          packet->f.flags.in_protocol = 1;
#endif /* DEBUG_LIGHT */
          NdisMIndicateReceivePacket(adapter->handle, &packet->np, 1);
        }
    }
  else
    {
      SSH_NOTREACHED;
    }

  return;

 free_packet:
  /* Otherwise just drop the packet */
  SSH_DEBUG(SSH_D_FAIL, ("ssh_interceptor_send(): dropping packet"));
  ssh_interceptor_packet_free(&packet->ip);
}
Exemplo n.º 10
0
/* For SOCKS5. */
SocksError ssh_socks5_client_generate_open(SshBuffer buffer,
                                           SocksInfo socksinfo)
{
  unsigned char *data;
  unsigned long port;
  size_t bytes = 0L, bytes_needed = 0;
  SshIpAddrStruct ip_addr;
  unsigned int address_type;

  port = ssh_inet_get_port_by_service(socksinfo->port, ssh_custr("tcp"));
  if (port >= 65536 || port <= 0)
    return SSH_SOCKS_ERROR_INVALID_ARGUMENT;

  if (ssh_ipaddr_parse(&ip_addr, socksinfo->ip))
    {
      if (SSH_IP_IS4(&ip_addr))
        address_type = SSH_SOCKS5_ATYP_IPV4;
      else
        address_type = SSH_SOCKS5_ATYP_IPV6;
    }
  else
    {
      SSH_DEBUG(2, ("IP `%s' could not be parsed, assuming it is a hostname.",
                    socksinfo->ip));
      address_type = SSH_SOCKS5_ATYP_FQDN;
    }

  bytes = ssh_encode_buffer(buffer,
                            SSH_ENCODE_CHAR(socksinfo->socks_version_number),
                            SSH_ENCODE_CHAR(socksinfo->command_code),
                            /* RSV. */
                            SSH_ENCODE_CHAR(0),
                            SSH_ENCODE_CHAR(address_type),
                            SSH_FORMAT_END);
  if (bytes == 0)
    {
      SSH_DEBUG(2, ("Encoding command buffer failed."));
      return SSH_SOCKS_ERROR_INVALID_ARGUMENT;
    }

  if (address_type == SSH_SOCKS5_ATYP_IPV4)
    bytes_needed = 4;
  else if (address_type == SSH_SOCKS5_ATYP_IPV6)
    bytes_needed = 16;
  else if (address_type == SSH_SOCKS5_ATYP_FQDN)
    /* length field + address length */
    bytes_needed = 1 + ssh_ustrlen(socksinfo->ip);

  /* port */
  bytes_needed += 2;

  /* Allocate space for the IP-address*/
  if (ssh_buffer_append_space(buffer, &data, bytes_needed) != SSH_BUFFER_OK)
    {
      SSH_DEBUG(2, ("Allocating space for the IP-address failed."));
      ssh_buffer_consume_end(buffer, bytes);
      return SSH_SOCKS_ERROR_INVALID_ARGUMENT;
    }

  if (address_type == SSH_SOCKS5_ATYP_IPV4)
    {
      SSH_IP4_ENCODE(&ip_addr, data);
    }
  else if (address_type == SSH_SOCKS5_ATYP_IPV6)
    {
      SSH_IP6_ENCODE(&ip_addr, data);
    }
  else if (address_type == SSH_SOCKS5_ATYP_FQDN)
    {
      *data = ssh_ustrlen(socksinfo->ip);
      ssh_ustrcpy(data + 1, socksinfo->ip);
    }
  bytes += bytes_needed - 2;
  data += bytes_needed - 2;
  SSH_PUT_16BIT(data, port);
  SSH_DEBUG(4, ("Command buffer size %zd.", bytes + bytes_needed));
  return SSH_SOCKS_SUCCESS;
}
Exemplo n.º 11
0
SocksError ssh_socks5_server_generate_reply(SshBuffer buffer,
                                            SocksInfo socksinfo)
{
  unsigned char *data;
  int port, ip_addr_len;
  unsigned int atyp;
  size_t len;
  SshIpAddrStruct ip_addr;

  port = ssh_inet_get_port_by_service(socksinfo->port, ssh_custr("tcp"));
  if (port >= 65536 || port <= 0)
    return SSH_SOCKS_ERROR_INVALID_ARGUMENT;

  if (!ssh_ipaddr_parse(&ip_addr, socksinfo->ip))
    {
      atyp = SSH_SOCKS5_ATYP_FQDN;
      ip_addr_len = ssh_ustrlen(socksinfo->ip);
    }
  else if (SSH_IP_IS4(&ip_addr))
    {
      atyp = SSH_SOCKS5_ATYP_IPV4;
      ip_addr_len = 4;
    }
  else if (SSH_IP_IS6(&ip_addr))
    {
      atyp = SSH_SOCKS5_ATYP_IPV6;
      ip_addr_len = 16;
    }
  else
    {
      SSH_DEBUG(2, ("IP-address is of unknown type."));
      return SSH_SOCKS_ERROR_INVALID_ARGUMENT;
    }

  len = 6 + ip_addr_len;
  if (atyp == SSH_SOCKS5_ATYP_FQDN)
    /* Length field. */
    len += 1;

  if (ssh_buffer_append_space(buffer, &data, len) != SSH_BUFFER_OK)
    {
      SSH_DEBUG(2, ("Failed to allocate reply buffer."));
      return SSH_SOCKS_ERROR_INVALID_ARGUMENT;
    }

  *data++ = socksinfo->socks_version_number;
  *data++ = socksinfo->command_code;
  *data++ = 0; /* RSV. */
  *data++ = atyp;
  if (atyp == SSH_SOCKS5_ATYP_FQDN)
    {
      *data++ = ip_addr_len;
      memmove(data, socksinfo->ip, ip_addr_len);
      data += ip_addr_len;
    }
  else
    {
      int len;
      SSH_IP_ENCODE(&ip_addr, data, len);
      SSH_ASSERT(ip_addr_len == len);
      data += len;
    }
  SSH_PUT_16BIT(data, port);
  return SSH_SOCKS_SUCCESS;
}
Exemplo n.º 12
0
void
ikev2_fb_spd_select_qm_sa_cb(SshIkev2Error error_code,
			     int ikev2_proposal_index,
			     SshIkev2PayloadTransform
			     selected_transforms[SSH_IKEV2_TRANSFORM_TYPE_MAX],
			     void *context)
{
  SshIkev2FbNegotiation neg = context;
  SshIkeIpsecSelectedSAIndexes selected = NULL;
  SshIkePayloadSA sa;
  int ikev1_proposal_index, ikev1_ipsec_transform_index;
  int ikev1_ipcomp_transform_index, i, iproto;

  SSH_IKEV2_FB_V2_COMPLETE_CALL(neg);

  sa = &neg->sa_table_in[0]->pl.sa;

  if (error_code != SSH_IKEV2_ERROR_OK)
    {
      SSH_DEBUG(SSH_D_FAIL, ("IKEv2 SA select failed with error %s",
			     ssh_ikev2_error_to_string(error_code)));
      goto error;
    }







  /* Check if ISAKMP library has freed the qm negotiation. */
  if (neg->qm_info == NULL)
    {
      SSH_DEBUG(SSH_D_FAIL, ("QM negotiation has disappeared"));
      neg->ike_error = SSH_IKEV2_ERROR_INVALID_ARGUMENT;
      goto error;
    }

  if (selected_transforms == NULL)
    {
      SSH_DEBUG(SSH_D_FAIL, ("No IPSec SA transforms selected"));
      goto error;
    }

  /* Store information on the selected SA to the IPSec exchange data. */
  for (i = 0; i < SSH_IKEV2_TRANSFORM_TYPE_MAX; i++)
    {
      neg->ed->ipsec_ed->ipsec_sa_transforms[i] = selected_transforms[i];
    }

  neg->ed->ipsec_ed->ipsec_sa_protocol =
    neg->sav2->protocol_id[ikev2_proposal_index];

  /* Set the inbound SPI to the IPSec exchange data */
  neg->ed->ipsec_ed->spi_inbound = neg->inbound_spi;

  SSH_DEBUG(SSH_D_LOWOK, ("Inbound SPI %lx",
			  (unsigned long) neg->inbound_spi));

  SSH_ASSERT(neg->number_of_sas_in == 1);
  selected = ssh_calloc(neg->number_of_sas_in, sizeof(*selected));
  if (selected == NULL)
    goto error;

  /* Check to see which proposal and ESP/AH/IPComp transform index 
     was selected. */
  if (!ikev2_fb_select_ipsec_transform_index(selected_transforms,
					     neg->qm_info->negotiation,
					     sa,
					     neg->ipcomp_proposals,
					     neg->ipcomp_algs[0],
					     &ikev1_proposal_index,
					     &ikev1_ipsec_transform_index,
					     &ikev1_ipcomp_transform_index))
    {
    error:
      SSH_DEBUG(SSH_D_FAIL,
		("SA selection failed, no matching proposal (neg %p)", neg));
      ikev2_fb_free_sa_indexes(selected, neg->number_of_sas_in);
      neg->selected = NULL;
      SSH_FSM_CONTINUE_AFTER_CALLBACK(neg->sub_thread);
      return;
    }
  selected[0].proposal_index = ikev1_proposal_index;

  SSH_DEBUG(SSH_D_LOWOK, 
	    ("Selected proposal indices are v2=%d, v1=%d num protocols is %d",
	     ikev2_proposal_index, ikev1_proposal_index,  
	     sa->proposals[ikev1_proposal_index].number_of_protocols));
  
  selected[0].number_of_protocols
    = sa->proposals[ikev1_proposal_index].number_of_protocols;
  selected[0].transform_indexes
    = ssh_calloc(selected[0].number_of_protocols, sizeof(int));
  selected[0].spi_sizes
    = ssh_calloc(selected[0].number_of_protocols, sizeof(size_t));
  selected[0].spis
    = ssh_calloc(selected[0].number_of_protocols,
		 sizeof(unsigned char *));

  if (selected[0].transform_indexes == NULL ||
      selected[0].spi_sizes == NULL ||
      selected[0].spis == NULL)
    goto error;

  for (iproto = 0; iproto < selected[0].number_of_protocols; iproto++)
    {
      SshIkePayloadPProtocol proto = 
	&sa->proposals[ikev1_proposal_index].protocols[iproto];
      
      if ((selected->spis[iproto] = ssh_malloc(4)) == NULL)
	goto error;

      switch (proto->protocol_id)
	{
	case SSH_IKE_PROTOCOL_IPCOMP:
	  selected->spi_sizes[iproto] = 2;
	  SSH_PUT_16BIT(selected->spis[iproto], neg->ipcomp_cpi_in);

	  selected[0].transform_indexes[iproto] = ikev1_ipcomp_transform_index;
	  break;
	default:
	  selected->spi_sizes[iproto] = 4;
	  SSH_PUT_32BIT(selected->spis[iproto], neg->inbound_spi);

	  /* Check the proposed lifetimes against that of our policy to see
	     whether we should send a responder lifetime notification */
	  if (ikev2_fb_check_ipsec_responder_lifetimes(neg->ed,
						       neg->sa_life_seconds,
						       neg->sa_life_kbytes))
	    {
	      /* Let's send a responder lifetime notify. */
	      SSH_DEBUG(SSH_D_NICETOKNOW,
			("Sending a responder lifetime notification: "
			 "life_sec=%lu, life_kb=%lu",
			 neg->ed->ipsec_ed->sa_life_seconds,
			 neg->ed->ipsec_ed->sa_life_kbytes));
	      selected[0].expire_secs = neg->ed->ipsec_ed->sa_life_seconds;
	      selected[0].expire_kb = neg->ed->ipsec_ed->sa_life_kbytes;
	    }
	  selected[0].transform_indexes[iproto] = ikev1_ipsec_transform_index;
	}
    }

  neg->selected = selected;

  SSH_FSM_CONTINUE_AFTER_CALLBACK(neg->sub_thread);
  return;
}
Exemplo n.º 13
0
void
ikev2_fb_sa_handler(SshIkeNegotiation negotiation,
		    SshIkePMPhaseQm pm_info,
		    int number_of_sas, SshIkeIpsecSelectedSA sas,
		    SshIkeIpsecKeymat keymat,
		    void *sa_callback_context)
{
  SshIkev2FbNegotiation neg;

  neg = SSH_IKEV2_FB_QM_GET_P1_NEGOTIATION(pm_info);
  if (neg == NULL)
    return;

  neg->ike_sa->last_input_stamp = ssh_time();

  SSH_DEBUG(SSH_D_LOWOK, ("SA handler entered, IKE SA %p (neg %p)",
			  pm_info->phase_i->policy_manager_data, neg));

  if (number_of_sas != 1)
    {
      SSH_DEBUG(SSH_D_FAIL, ("Quick-Mode does not result in one bundle"));
      goto error;
    }

  if (neg->ed->callback)
    {
      int i, iproto;
      SshIkev2PayloadTransform *t;
      SshIkeIpsecSelectedProtocol p;
      SshIkeIpsecAttributeEncapsulationModeValues encap;

      /* Assert that `neg->qm_info' is set correctly. */
      SSH_ASSERT(neg->qm_info == pm_info);

      for (i = 0; i < SSH_IKEV2_TRANSFORM_TYPE_MAX; i++)
	neg->ed->ipsec_ed->ipsec_sa_transforms[i] = &neg->transforms[i];

      /* Fill in the selected transforms into ipsec_ed */

      t = neg->ed->ipsec_ed->ipsec_sa_transforms;

      for (iproto = 0; iproto < sas[0].number_of_protocols; iproto++)
	{
	  p = &sas[0].protocols[iproto];
	  if (p->protocol_id == SSH_IKE_PROTOCOL_IPSEC_ESP)
	    {
	      t[SSH_IKEV2_TRANSFORM_TYPE_ENCR]->id =
		ikev2_fb_v1_esp_id_to_v2_id(p->transform_id.generic);
	      if (p->attributes.key_length)
		t[SSH_IKEV2_TRANSFORM_TYPE_ENCR]->transform_attribute =
		  (0x800e << 16) | p->attributes.key_length;

	      if (p->attributes.auth_algorithm)
		t[SSH_IKEV2_TRANSFORM_TYPE_INTEG]->id =
		  ikev2_fb_v1_auth_id_to_v2_id(p->attributes.auth_algorithm);
	      else
		t[SSH_IKEV2_TRANSFORM_TYPE_INTEG] = NULL;
	    }
	  else if (p->protocol_id == SSH_IKE_PROTOCOL_IPSEC_AH)
	    {
	      t[SSH_IKEV2_TRANSFORM_TYPE_INTEG]->id =
		ikev2_fb_v1_ah_id_to_v2_id(p->transform_id.generic);
	      t[SSH_IKEV2_TRANSFORM_TYPE_ENCR] = NULL;
	    }
	  else if (p->protocol_id == SSH_IKE_PROTOCOL_IPCOMP)
	    {
	      if (p->spi_size_out == 2)
		{
		  int j;

		  for (j = 0; j < neg->ipcomp_num; j++)
		    {
		      if (neg->ipcomp_algs[j] == p->transform_id.ipcomp)
			{
			  neg->ipcomp_num = 1;
			  neg->ipcomp_algs[0] = p->transform_id.ipcomp;
			  neg->ipcomp_cpi_out[0] = SSH_GET_16BIT(p->spi_out);
			  break;
			}
		    }
		}
	    }

	  if (p->attributes.group_desc)
	    t[SSH_IKEV2_TRANSFORM_TYPE_D_H]->id = p->attributes.group_desc;
	  else
	    t[SSH_IKEV2_TRANSFORM_TYPE_D_H] = NULL;

	  t[SSH_IKEV2_TRANSFORM_TYPE_ESN]->id = (p->attributes.longseq_size)
	    ? SSH_IKEV2_TRANSFORM_ESN_ESN
	    : SSH_IKEV2_TRANSFORM_ESN_NO_ESN;

	  /* For initiator, notify policymanager about transport mode */
	  encap = p->attributes.encapsulation_mode;
	  if (encap == IPSEC_VALUES_ENCAPSULATION_MODE_TRANSPORT ||
	      encap == IPSEC_VALUES_ENCAPSULATION_MODE_UDP_TRANSPORT ||
	      encap == IPSEC_VALUES_ENCAPSULATION_MODE_UDP_DRAFT_TRANSPORT)
	    {
	      (void)
		ikev2_fb_construct_notify(neg,
					  0,
					  SSH_IKEV2_NOTIFY_USE_TRANSPORT_MODE,
					  TRUE,
					  0, NULL, 0, NULL);
	    }
	}

      if (neg->ipcomp_num > 0)
	{
	  for (i = 0; i < neg->ipcomp_num; i++)
	    {
	      unsigned char data[3];

	      SSH_PUT_16BIT(data, neg->ipcomp_cpi_out[i]);
	      data[2] = neg->ipcomp_algs[i];

	      (void)
		ikev2_fb_construct_notify(neg,
					  0,
					  SSH_IKEV2_NOTIFY_IPCOMP_SUPPORTED,
					  TRUE,
					  0, NULL, sizeof(data), data);
	    }
	}
    }

  /* Set the outbound SPI to the IPSec exchange data */
  if (sas->protocols[0].spi_size_out != 4)
    goto error;
  neg->ed->ipsec_ed->spi_outbound = SSH_GET_32BIT(sas->protocols[0].spi_out);

  SSH_DEBUG(SSH_D_LOWOK, ("Outbound SPI %lx",
			  (unsigned long) neg->ed->ipsec_ed->spi_outbound));

  if (!ikev2_fb_fill_keymat(neg->ed, negotiation, sas, keymat))
    {
      SSH_DEBUG(SSH_D_FAIL, ("Cannot generate IKEv2 keying material"));
      goto error;
    }

  SSH_IKEV2_FB_V2_CALL(neg, ipsec_sa_install)
    (neg->server->sad_handle, neg->ed, ikev2_fb_ipsec_sa_install_done, neg);
  return;

 error:

  /* Even in the case of error, we call the IKEv2 SA installation
     policy call. The key material is cleared to ensure the installation
     will fail at the policy manager. */
  ssh_free(neg->ed->ipsec_ed->ikev1_keymat);
  neg->ed->ipsec_ed->ikev1_keymat = NULL;
  neg->ed->ipsec_ed->ikev1_keymat_len = 0;

  SSH_IKEV2_FB_V2_CALL(neg, ipsec_sa_install)
    (neg->server->sad_handle, neg->ed, ikev2_fb_ipsec_sa_install_done, neg);
}
Exemplo n.º 14
0
/* Encode record. */
size_t ssh_dns_packet_encode_record(unsigned char *buffer, 
				    size_t buffer_length,
				    size_t offset,
				    SshDNSRecord record,
				    SshDNSCompressMap map)
{
  size_t len, len2, len3, rdlength;

  len = ssh_dns_packet_encode_name(buffer, buffer_length, offset,
				   record->name, map);
  if (len == 0)
    return 0;

  len2 = ssh_encode_array(buffer + offset + len,
			  buffer_length - offset - len,
			  SSH_ENCODE_UINT16(record->type),
			  SSH_ENCODE_UINT16(record->dns_class),
			  SSH_ENCODE_UINT32(record->ttl),
			  SSH_ENCODE_UINT16(0),
			  SSH_FORMAT_END);
  if (len2 == 0)
    return 0;

  /* Offset points to the beginning of rdata. */
  offset += len + len2;
  len += len2;

  switch (record->type)
    {
      /* One compressed name. Copy rest. */
    case SSH_DNS_RESOURCE_NS:
    case SSH_DNS_RESOURCE_MD:
    case SSH_DNS_RESOURCE_MF:
    case SSH_DNS_RESOURCE_CNAME:
    case SSH_DNS_RESOURCE_MB:
    case SSH_DNS_RESOURCE_MG:
    case SSH_DNS_RESOURCE_MR:
    case SSH_DNS_RESOURCE_PTR:
      rdlength = ssh_dns_packet_encode_name(buffer, buffer_length,
					    offset, record->rdata, map);
      if (rdlength == 0)
	return 0;
      len2 = ssh_ustrnlen(record->rdata, record->rdlength) + 1;
      goto copy_rest;
      break;

      /* Two domain name addresses, copy rest. */
    case SSH_DNS_RESOURCE_SOA:
    case SSH_DNS_RESOURCE_MINFO:
      rdlength = ssh_dns_packet_encode_name(buffer, buffer_length,
					    offset, record->rdata, map);
      if (rdlength == 0)
	return 0;
      len3 = ssh_ustrnlen(record->rdata, record->rdlength) + 1;
      if (len3 >= record->rdlength)
	return 0;
      len2 = ssh_dns_packet_encode_name(buffer, buffer_length,
					offset + rdlength,
					record->rdata + len3, map);
      if (len2 == 0)
	return 0;
      rdlength += len2;
      len2 = len3 + ssh_ustrnlen(record->rdata + len3,
				record->rdlength - len3) + 1;
      goto copy_rest;
      break;

      /* 16-bit number and dns name. */
    case SSH_DNS_RESOURCE_MX:
      len2 = 2;
      if (buffer_length < offset + len2)
	return 0;
      memcpy(buffer + offset, record->rdata, len2);

      rdlength = ssh_dns_packet_encode_name(buffer, buffer_length,
					    offset + len2,
					    record->rdata + len2,
					    map);
      if (rdlength == 0)
	return 0;
      rdlength += len2;
      len2 += ssh_ustrnlen(record->rdata + len2, record->rdlength - len2) + 1;
      goto copy_rest;
      break;

      /* Just copy data. */
    case SSH_DNS_RESOURCE_A:
    case SSH_DNS_RESOURCE_NULL:
    case SSH_DNS_RESOURCE_WKS:
    case SSH_DNS_RESOURCE_HINFO:
    case SSH_DNS_RESOURCE_TXT:
    case SSH_DNS_RESOURCE_X25:
    case SSH_DNS_RESOURCE_ISDN:
    case SSH_DNS_RESOURCE_NSAP:
    case SSH_DNS_RESOURCE_KEY:
    case SSH_DNS_RESOURCE_GPOS:
    case SSH_DNS_RESOURCE_AAAA:
    case SSH_DNS_RESOURCE_LOC:
    case SSH_DNS_RESOURCE_NAPTR: /* Contains name, and rfc2168 says it is
				    compressed, but rfc2915 says that will not
				    be compressed. */
    case SSH_DNS_RESOURCE_CERT:
    case SSH_DNS_RESOURCE_A6:	/* Contains name, but MUST NOT be compr. */
    case SSH_DNS_RESOURCE_APL:
    case SSH_DNS_RESOURCE_DS:
    case SSH_DNS_RESOURCE_SSHFP:
    case SSH_DNS_RESOURCE_DNSKEY:
    case SSH_DNS_QUERY_TKEY:
    case SSH_DNS_QUERY_TSIG:
      /* Contains names, but do not compress. See RFC 3597 for details */
    case SSH_DNS_RESOURCE_DNAME: /* Do not compress, but do decompress. */
    case SSH_DNS_RESOURCE_NSEC: /* Do not compress, but do decompress. */
    case SSH_DNS_RESOURCE_KX: /* Do not compress, but do decompress. */
    case SSH_DNS_RESOURCE_RRSIG: /* Docompress, must not compress */
    case SSH_DNS_RESOURCE_SRV:	/* Do not compress, but do decompress */
    case SSH_DNS_RESOURCE_NSAP_PTR: /* Do not compress */
    case SSH_DNS_RESOURCE_NXT:	/* Do not compress */
    case SSH_DNS_RESOURCE_RP:	/* Do not compress */
    case SSH_DNS_RESOURCE_AFSDB: /* Do not compress */
    case SSH_DNS_RESOURCE_RT:	/* Do not compress */
    case SSH_DNS_RESOURCE_SIG:	/* Do not compress */
    case SSH_DNS_RESOURCE_PX:	/* Do not compress */
      /* Special handling, we just copy it, we assume that if the sender wants
	 to use the extended flags etc, he will add OPT record himself, i.e. we
	 do not try to parse the response codes etc to see if there is
	 something which would need OPT record. . */
    case SSH_DNS_RESOURCE_OPT:
      /* Unknown, just copy. */
    case SSH_DNS_RESOURCE_EID:
    case SSH_DNS_RESOURCE_NIMLOC:
    case SSH_DNS_RESOURCE_ATMA:
    case SSH_DNS_RESOURCE_SINK:
    case SSH_DNS_RESOURCE_UINFO:
    case SSH_DNS_RESOURCE_UID:
    case SSH_DNS_RESOURCE_GID:
    case SSH_DNS_RESOURCE_UNSPEC:
      /* Query typedef values which do not appear in resource records, copy */
    case SSH_DNS_QUERY_IXFR:
    case SSH_DNS_QUERY_AXFR:
    case SSH_DNS_QUERY_MAILB:
    case SSH_DNS_QUERY_MAILA:
    case SSH_DNS_QUERY_ANY:
    default:
      rdlength = 0;
      len2 = 0;
    copy_rest: 
      /* Len2 is the number of bytes consumed from the record->rdata. */
      if (buffer_length < offset + rdlength + record->rdlength - len2)
	return 0;
      memcpy(buffer + offset + rdlength, record->rdata + len2,
	     record->rdlength - len2);
      rdlength += record->rdlength - len2;
    }

  /* Fix the length. It is stored just before the rdata. */
  SSH_PUT_16BIT(buffer + offset - 2, rdlength);
  return len + rdlength;
}