1
#include "source/extensions/quic/server_preferred_address/fixed_server_preferred_address_config.h"
2

            
3
#include "source/common/network/utility.h"
4
#include "source/common/quic/envoy_quic_utils.h"
5
#include "source/extensions/quic/server_preferred_address/server_preferred_address.h"
6

            
7
namespace Envoy {
8
namespace Quic {
9

            
10
namespace {
11

            
12
quic::QuicIpAddress parseIp(const std::string& addr, absl::string_view address_family,
13
17
                            const Protobuf::Message& message) {
14
17
  quic::QuicIpAddress ip;
15
17
  if (!ip.FromString(addr)) {
16
5
    ProtoExceptionUtil::throwProtoValidationException(
17
5
        absl::StrCat("bad ", address_family, " server preferred address: ", addr), message);
18
5
  }
19
17
  return ip;
20
17
}
21

            
22
quic::QuicSocketAddress parseSocketAddress(const envoy::config::core::v3::SocketAddress& addr,
23
                                           Network::Address::IpVersion version,
24
                                           absl::string_view version_str,
25
28
                                           const Protobuf::Message& message) {
26
  // There's no utility to convert from a `SocketAddress`, so wrap it in an `Address` to make use of
27
  // existing helpers.
28
28
  envoy::config::core::v3::Address outer;
29
28
  *outer.mutable_socket_address() = addr;
30
28
  auto envoy_addr = Network::Utility::protobufAddressToAddressNoThrow(outer);
31
28
  if (!envoy_addr) {
32
1
    ProtoExceptionUtil::throwProtoValidationException(absl::StrCat("Invalid address ", outer),
33
1
                                                      message);
34
1
  }
35
28
  ASSERT(envoy_addr != nullptr,
36
28
         "Network::Utility::protobufAddressToAddress throws on failure so this can't be nullptr");
37
28
  if (envoy_addr->ip() == nullptr || envoy_addr->ip()->version() != version) {
38
2
    ProtoExceptionUtil::throwProtoValidationException(
39
2
        absl::StrCat("wrong address type for ", version_str, " server preferred address: ", addr),
40
2
        message);
41
2
  }
42

            
43
28
  return envoyIpAddressToQuicSocketAddress(envoy_addr->ip());
44
28
}
45

            
46
quic::QuicIpAddress
47
parseIpAddressFromSocketAddress(const envoy::config::core::v3::SocketAddress& addr,
48
                                Network::Address::IpVersion version, absl::string_view version_str,
49
11
                                const Protobuf::Message& message) {
50
11
  auto socket_addr = parseSocketAddress(addr, version, version_str, message);
51
11
  if (socket_addr.port() != 0) {
52
1
    ProtoExceptionUtil::throwProtoValidationException(
53
1
        fmt::format("port must be 0 in this version of Envoy in address '{}'",
54
1
                    socket_addr.ToString()),
55
1
        message);
56
1
  }
57

            
58
11
  return socket_addr.host();
59
11
}
60

            
61
ServerPreferredAddressConfig::IpVersionConfig
62
parseFamily(const std::string& addr_string,
63
            const envoy::extensions::quic::server_preferred_address::v3::
64
                FixedServerPreferredAddressConfig::AddressFamilyConfig* addresses,
65
            Network::Address::IpVersion version, absl::string_view address_family,
66
44
            const Protobuf::Message& message) {
67
44
  ServerPreferredAddressConfig::IpVersionConfig ret;
68
44
  if (addresses != nullptr) {
69
18
    if (addresses->has_dnat_address() && !addresses->has_address()) {
70
1
      ProtoExceptionUtil::throwProtoValidationException(
71
1
          absl::StrCat("'dnat_address' but not 'address' is set in server preferred address for ",
72
1
                       address_family),
73
1
          message);
74
1
    }
75

            
76
18
    if (addresses->has_address()) {
77
17
      ret.spa_ = parseSocketAddress(addresses->address(), version, address_family, message);
78

            
79
17
      if (!addresses->has_dnat_address() && ret.spa_.port() != 0) {
80
1
        ProtoExceptionUtil::throwProtoValidationException(
81
1
            fmt::format("'address' port must be zero unless 'dnat_address' is set in address {} "
82
1
                        "for address family {}",
83
1
                        ret.spa_.ToString(), address_family),
84
1
            message);
85
1
      }
86
17
    }
87

            
88
18
    if (addresses->has_dnat_address()) {
89
11
      ret.dnat_ = parseIpAddressFromSocketAddress(addresses->dnat_address(), version,
90
11
                                                  address_family, message);
91
11
    }
92
28
  } else {
93
26
    if (!addr_string.empty()) {
94
17
      ret.spa_ = quic::QuicSocketAddress(parseIp(addr_string, address_family, message), 0);
95
17
    }
96
26
  }
97

            
98
44
  return ret;
99
44
}
100

            
101
} // namespace
102

            
103
Quic::EnvoyQuicServerPreferredAddressConfigPtr
104
FixedServerPreferredAddressConfigFactory::createServerPreferredAddressConfig(
105
    const Protobuf::Message& message, ProtobufMessage::ValidationVisitor& validation_visitor,
106
26
    Server::Configuration::ServerFactoryContext& /*context*/) {
107
26
  auto& config =
108
26
      MessageUtil::downcastAndValidate<const envoy::extensions::quic::server_preferred_address::v3::
109
26
                                           FixedServerPreferredAddressConfig&>(message,
110
26
                                                                               validation_visitor);
111

            
112
26
  ServerPreferredAddressConfig::IpVersionConfig v4 =
113
26
      parseFamily(config.ipv4_address(), config.has_ipv4_config() ? &config.ipv4_config() : nullptr,
114
26
                  Network::Address::IpVersion::v4, "v4", message);
115
26
  ServerPreferredAddressConfig::IpVersionConfig v6 =
116
26
      parseFamily(config.ipv6_address(), config.has_ipv6_config() ? &config.ipv6_config() : nullptr,
117
26
                  Network::Address::IpVersion::v6, "v6", message);
118

            
119
26
  return std::make_unique<ServerPreferredAddressConfig>(v4, v6);
120
26
}
121

            
122
REGISTER_FACTORY(FixedServerPreferredAddressConfigFactory,
123
                 EnvoyQuicServerPreferredAddressConfigFactory);
124

            
125
} // namespace Quic
126
} // namespace Envoy