1
#include "source/common/network/ip_address_parsing.h"
2

            
3
#include <cstring>
4

            
5
#include "source/common/api/os_sys_calls_impl.h"
6

            
7
namespace Envoy {
8
namespace Network {
9
namespace IpAddressParsing {
10

            
11
350449
StatusOr<sockaddr_in> parseIPv4(const std::string& ip_address, uint16_t port) {
12
  // Use inet_pton() for IPv4 as it's simpler, faster, and already enforces
13
  // strict dotted-quad format while rejecting non-standard notations.
14
350449
  sockaddr_in sa4;
15
350449
  memset(&sa4, 0, sizeof(sa4));
16
350449
  if (inet_pton(AF_INET, ip_address.c_str(), &sa4.sin_addr) != 1) {
17
48069
    return absl::FailedPreconditionError("failed parsing ipv4");
18
48069
  }
19
302380
  sa4.sin_family = AF_INET;
20
302380
  sa4.sin_port = htons(port);
21
302380
  return sa4;
22
350449
}
23

            
24
48502
StatusOr<sockaddr_in6> parseIPv6(const std::string& ip_address, uint16_t port) {
25
  // Parse IPv6 with optional scope using getaddrinfo().
26
  // While inet_pton() would be faster and simpler, it does not support IPv6
27
  // addresses that specify a scope, e.g. `::%eth0` to listen on only one interface.
28
48502
  struct addrinfo hints;
29
48502
  memset(&hints, 0, sizeof(hints));
30
48502
  struct addrinfo* res = nullptr;
31
  // Suppresses any potentially lengthy network host address lookups and inhibit the
32
  // invocation of a name resolution service.
33
48502
  hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
34
48502
  hints.ai_family = AF_INET6;
35
  // Given that we do not specify a service but we use getaddrinfo() to only parse the node
36
  // address, specifying the socket type allows to hint the getaddrinfo() to return only an
37
  // element with the below socket type. The behavior though remains platform dependent and
38
  // anyway we consume only the first element if the call succeeds.
39
48502
  hints.ai_socktype = SOCK_DGRAM;
40
48502
  hints.ai_protocol = IPPROTO_UDP;
41

            
42
  // We want to use the interface of OsSysCalls for this for the platform-independence, but
43
  // we do not want to use the common OsSysCallsSingleton.
44
  //
45
  // The problem with using OsSysCallsSingleton is that we likely want to override getaddrinfo()
46
  // for DNS lookups in tests, but typically that override would resolve a name to e.g. the
47
  // address from resolveUrl("tcp://[::1]:80"). But resolveUrl calls ``parseIPv6``, which calls
48
  // getaddrinfo(), so if we use the mock here then mocking DNS causes infinite recursion.
49
  //
50
  // We do not ever need to mock this getaddrinfo() call, because it is only used to parse
51
  // numeric IP addresses, per ``ai_flags``, so it should be deterministic resolution. There
52
  // is no need to mock it to test failure cases.
53
48502
  static Api::OsSysCallsImpl os_sys_calls;
54
48502
  const Api::SysCallIntResult rc =
55
48502
      os_sys_calls.getaddrinfo(ip_address.c_str(), /*service=*/nullptr, &hints, &res);
56
48502
  if (rc.return_value_ != 0) {
57
2732
    return absl::FailedPreconditionError(absl::StrCat("getaddrinfo error: ", rc.return_value_));
58
2732
  }
59
45770
  sockaddr_in6 sa6 = *reinterpret_cast<sockaddr_in6*>(res->ai_addr);
60
45770
  os_sys_calls.freeaddrinfo(res);
61
45770
  sa6.sin6_port = htons(port);
62
45770
  return sa6;
63
48502
}
64

            
65
} // namespace IpAddressParsing
66
} // namespace Network
67
} // namespace Envoy