int inet_pton()

in demo_example/asio/asio/detail/impl/socket_ops.ipp [2524:2778]


int inet_pton(int af, const char* src, void* dest,
    unsigned long* scope_id, asio::error_code& ec)
{
  clear_last_error();
#if defined(ASIO_WINDOWS_RUNTIME)
  using namespace std; // For sscanf.
  unsigned char* bytes = static_cast<unsigned char*>(dest);
  if (af == ASIO_OS_DEF(AF_INET))
  {
    unsigned int b0, b1, b2, b3;
    if (sscanf_s(src, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) != 4)
    {
      ec = asio::error::invalid_argument;
      return -1;
    }
    if (b0 > 255 || b1 > 255 || b2 > 255 || b3 > 255)
    {
      ec = asio::error::invalid_argument;
      return -1;
    }
    bytes[0] = static_cast<unsigned char>(b0);
    bytes[1] = static_cast<unsigned char>(b1);
    bytes[2] = static_cast<unsigned char>(b2);
    bytes[3] = static_cast<unsigned char>(b3);
    ec.assign(0, ec.category());
    return 1;
  }
  else if (af == ASIO_OS_DEF(AF_INET6))
  {
    unsigned char* bytes = static_cast<unsigned char*>(dest);
    std::memset(bytes, 0, 16);
    unsigned char back_bytes[16] = { 0 };
    int num_front_bytes = 0, num_back_bytes = 0;
    const char* p = src;

    enum { fword, fcolon, bword, scope, done } state = fword;
    unsigned long current_word = 0;
    while (state != done)
    {
      if (current_word > 0xFFFF)
      {
        ec = asio::error::invalid_argument;
        return -1;
      }

      switch (state)
      {
      case fword:
        if (*p >= '0' && *p <= '9')
          current_word = current_word * 16 + *p++ - '0';
        else if (*p >= 'a' && *p <= 'f')
          current_word = current_word * 16 + *p++ - 'a' + 10;
        else if (*p >= 'A' && *p <= 'F')
          current_word = current_word * 16 + *p++ - 'A' + 10;
        else
        {
          if (num_front_bytes == 16)
          {
            ec = asio::error::invalid_argument;
            return -1;
          }

          bytes[num_front_bytes++] = (current_word >> 8) & 0xFF;
          bytes[num_front_bytes++] = current_word & 0xFF;
          current_word = 0;

          if (*p == ':')
            state = fcolon, ++p;
          else if (*p == '%')
            state = scope, ++p;
          else if (*p == 0)
            state = done;
          else
          {
            ec = asio::error::invalid_argument;
            return -1;
          }
        }
        break;

      case fcolon:
        if (*p == ':')
          state = bword, ++p;
        else
          state = fword;
        break;

      case bword:
        if (*p >= '0' && *p <= '9')
          current_word = current_word * 16 + *p++ - '0';
        else if (*p >= 'a' && *p <= 'f')
          current_word = current_word * 16 + *p++ - 'a' + 10;
        else if (*p >= 'A' && *p <= 'F')
          current_word = current_word * 16 + *p++ - 'A' + 10;
        else
        {
          if (num_front_bytes + num_back_bytes == 16)
          {
            ec = asio::error::invalid_argument;
            return -1;
          }

          back_bytes[num_back_bytes++] = (current_word >> 8) & 0xFF;
          back_bytes[num_back_bytes++] = current_word & 0xFF;
          current_word = 0;

          if (*p == ':')
            state = bword, ++p;
          else if (*p == '%')
            state = scope, ++p;
          else if (*p == 0)
            state = done;
          else
          {
            ec = asio::error::invalid_argument;
            return -1;
          }
        }
        break;

      case scope:
        if (*p >= '0' && *p <= '9')
          current_word = current_word * 10 + *p++ - '0';
        else if (*p == 0)
          *scope_id = current_word, state = done;
        else
        {
          ec = asio::error::invalid_argument;
          return -1;
        }
        break;

      default:
        break;
      }
    }

    for (int i = 0; i < num_back_bytes; ++i)
      bytes[16 - num_back_bytes + i] = back_bytes[i];

    ec.assign(0, ec.category());
    return 1;
  }
  else
  {
    ec = asio::error::address_family_not_supported;
    return -1;
  }
#elif defined(ASIO_WINDOWS) || defined(__CYGWIN__)
  using namespace std; // For memcpy and strcmp.

  if (af != ASIO_OS_DEF(AF_INET) && af != ASIO_OS_DEF(AF_INET6))
  {
    ec = asio::error::address_family_not_supported;
    return -1;
  }

  union
  {
    socket_addr_type base;
    sockaddr_storage_type storage;
    sockaddr_in4_type v4;
    sockaddr_in6_type v6;
  } address;
  int address_length = sizeof(sockaddr_storage_type);
#if defined(BOOST_NO_ANSI_APIS) || (defined(_MSC_VER) && (_MSC_VER >= 1800))
  int num_wide_chars = static_cast<int>(strlen(src)) + 1;
  LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR));
  ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars);
  int result = ::WSAStringToAddressW(wide_buffer,
      af, 0, &address.base, &address_length);
  get_last_error(ec, true);
#else
  int result = ::WSAStringToAddressA(const_cast<char*>(src),
      af, 0, &address.base, &address_length);
  get_last_error(ec, true);
#endif

  if (af == ASIO_OS_DEF(AF_INET))
  {
    if (result != socket_error_retval)
    {
      memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type));
      ec.assign(0, ec.category());
    }
    else if (strcmp(src, "255.255.255.255") == 0)
    {
      static_cast<in4_addr_type*>(dest)->s_addr = INADDR_NONE;
      ec.assign(0, ec.category());
    }
  }
  else // AF_INET6
  {
    if (result != socket_error_retval)
    {
      memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type));
      if (scope_id)
        *scope_id = address.v6.sin6_scope_id;
      ec.assign(0, ec.category());
    }
  }

  // Windows may not set an error code on failure.
  if (result == socket_error_retval && !ec)
    ec = asio::error::invalid_argument;

  if (result != socket_error_retval)
    ec.assign(0, ec.category());

  return result == socket_error_retval ? -1 : 1;
#else // defined(ASIO_WINDOWS) || defined(__CYGWIN__)
  using namespace std; // For strchr, memcpy and atoi.

  // On some platforms, inet_pton fails if an address string contains a scope
  // id. Detect and remove the scope id before passing the string to inet_pton.
  const bool is_v6 = (af == ASIO_OS_DEF(AF_INET6));
  const char* if_name = is_v6 ? strchr(src, '%') : 0;
  char src_buf[max_addr_v6_str_len + 1];
  const char* src_ptr = src;
  if (if_name != 0)
  {
    if (if_name - src > max_addr_v6_str_len)
    {
      ec = asio::error::invalid_argument;
      return 0;
    }
    memcpy(src_buf, src, if_name - src);
    src_buf[if_name - src] = 0;
    src_ptr = src_buf;
  }

  int result = ::inet_pton(af, src_ptr, dest);
  get_last_error(ec, true);
  if (result <= 0 && !ec)
    ec = asio::error::invalid_argument;
  if (result > 0 && is_v6 && scope_id)
  {
    using namespace std; // For strchr and atoi.
    *scope_id = 0;
    if (if_name != 0)
    {
      in6_addr_type* ipv6_address = static_cast<in6_addr_type*>(dest);
      bool is_link_local = ((ipv6_address->s6_addr[0] == 0xfe)
          && ((ipv6_address->s6_addr[1] & 0xc0) == 0x80));
      bool is_multicast_link_local = ((ipv6_address->s6_addr[0] == 0xff)
          && ((ipv6_address->s6_addr[1] & 0x0f) == 0x02));
      if (is_link_local || is_multicast_link_local)
        *scope_id = if_nametoindex(if_name + 1);
      if (*scope_id == 0)
        *scope_id = atoi(if_name + 1);
    }
  }
  return result;
#endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__)
}