From 0a5880d6e3c0b292c238cf0835ab1f3a4f262d7c Mon Sep 17 00:00:00 2001 From: lihuiba Date: Sun, 6 Oct 2024 23:09:54 +0800 Subject: [PATCH] parse_address_list() --- net/kernel_socket.cpp | 50 ++++++++++++++++++++++++++++-------------- net/socket.h | 4 +++- net/test/test-ipv6.cpp | 3 +++ net/test/test.cpp | 15 ++++++++++++- net/utils.cpp | 14 ++++++++++++ net/utils.h | 4 ++++ 6 files changed, 72 insertions(+), 18 deletions(-) diff --git a/net/kernel_socket.cpp b/net/kernel_socket.cpp index 5abec733..bf64792a 100644 --- a/net/kernel_socket.cpp +++ b/net/kernel_socket.cpp @@ -1040,25 +1040,43 @@ extern "C" ISocketServer* new_fstack_dpdk_socket_server() { /* Implementations in socket.h */ -EndPoint::EndPoint(const char* _ep) { - estring_view ep(_ep); +static int parse_port(estring_view& ep) { auto pos = ep.find_last_of(':'); - if (pos == 0 || pos == estring::npos) - return; + if (pos == estring::npos) return -1; auto port_str = ep.substr(pos + 1); - if (!port_str.all_digits()) - return; - auto _port = port_str.to_uint64(); - if (_port > UINT16_MAX) - return; - port = (uint16_t)_port; - auto ipsv = (ep[0] == '[') ? ep.substr(1, pos - 2) : ep.substr(0, pos); - if (ipsv.length() >= INET6_ADDRSTRLEN - 1) - return; + if (!port_str.all_digits()) return -1; + auto port = port_str.to_uint64(); + if (port > UINT16_MAX) return -1; + ep = (pos > 2 && ep[0] == '[') ? ep.substr(1, pos - 2) + : ep.substr(0, pos); + return (int)port; +} + +static void parse_addr(std::string_view s, IPAddr* addr) { + if (s.length() >= INET6_ADDRSTRLEN - 1) return; char ip_str[INET6_ADDRSTRLEN]; - memcpy(ip_str, ipsv.data(), ipsv.length()); - ip_str[ipsv.length()] = '\0'; - addr = IPAddr(ip_str); + memcpy(ip_str, s.data(), s.length()); + ip_str[s.length()] = '\0'; + *addr = IPAddr(ip_str); +} + +EndPoint::EndPoint(const char* s_) { + estring_view s(s_); + if (s.empty()) return; + int port_ = parse_port(s); + if (port_ < 0) return; + port = (uint16_t)port_; + parse_addr(s, &addr); +} + +EndPoint EndPoint::parse(std::string_view s, uint16_t default_port) { + if (s.empty()) return {}; + int port = parse_port((estring_view&)s); + if (port < 0) port = default_port; + IPAddr addr; + parse_addr(s, &addr); + if (addr.undefined()) return {}; + return {addr, (uint16_t)port}; } LogBuffer& operator<<(LogBuffer& log, const IPAddr& addr) { diff --git a/net/socket.h b/net/socket.h index 8adb4cb3..90cca5b4 100644 --- a/net/socket.h +++ b/net/socket.h @@ -25,6 +25,7 @@ limitations under the License. #include #include #include +#include #ifdef __linux__ #define _in_addr_field s6_addr32 @@ -154,9 +155,10 @@ namespace net { IPAddr addr; uint16_t port = 0; EndPoint() = default; + explicit EndPoint(const char* ep); EndPoint(IPAddr ip, uint16_t port) : addr(ip), port(port) {} - explicit EndPoint(const char* ep); EndPoint(const char* ip, uint16_t port) : addr(ip), port(port) {} + static EndPoint parse(std::string_view ep, uint16_t default_port); bool is_ipv4() const { return addr.is_ipv4(); }; diff --git a/net/test/test-ipv6.cpp b/net/test/test-ipv6.cpp index b05d2f47..acb6f093 100644 --- a/net/test/test-ipv6.cpp +++ b/net/test/test-ipv6.cpp @@ -9,6 +9,9 @@ TEST(ipv6, endpoint) { auto c = photon::net::EndPoint("127.0.0.1"); EXPECT_TRUE(c.undefined()); // must have ':port' included + c = photon::net::EndPoint::parse("127.0.0.1", 1234); + EXPECT_FALSE(c.undefined()); + EXPECT_EQ(c.port, 1234); c = photon::net::EndPoint("127.0.0.1:8888"); EXPECT_FALSE(c.undefined()); c = photon::net::EndPoint("[::1]:8888"); diff --git a/net/test/test.cpp b/net/test/test.cpp index 713e896c..dec5c79b 100644 --- a/net/test/test.cpp +++ b/net/test/test.cpp @@ -187,7 +187,8 @@ TEST(Socket, endpoint) { photon::net::sockaddr_storage s(saddrin); ep = s.to_endpoint(); - EXPECT_TRUE(ep == EndPoint(IPAddr("12.34.56.78"), 4321)); + IPAddr addr12345678("12.34.56.78"); + EXPECT_TRUE(ep == EndPoint(addr12345678, 4321)); auto rsai = (sockaddr_in*) s.get_sockaddr(); EXPECT_EQ(saddrin.sin_addr.s_addr, rsai->sin_addr.s_addr); @@ -200,6 +201,18 @@ TEST(Socket, endpoint) { LOG_DEBUG(ep.addr); EXPECT_NE(nullptr, strstr(log_output_test._log_buf, "12.34.56.78")); log_output = log_output_stdout; + + EndPoint epfsv1("12.34.56.78:4321"), epfsv2("12.34.56.78", 4321); + EXPECT_EQ(epfsv1.addr, addr12345678); + EXPECT_EQ(epfsv1.port, 4321); + EXPECT_EQ(epfsv1, epfsv2); + + std::vector addrs; + parse_address_list("1.1.1.1:1,2.2.2.2:2,3.3.3.3:3,4.4.4.4", &addrs, 4); + EXPECT_EQ(addrs[0], EndPoint("1.1.1.1:1")); + EXPECT_EQ(addrs[1], EndPoint("2.2.2.2:2")); + EXPECT_EQ(addrs[2], EndPoint("3.3.3.3:3")); + EXPECT_EQ(addrs[3], EndPoint("4.4.4.4:4")); } TEST(Socket, timeout) { diff --git a/net/utils.cpp b/net/utils.cpp index 802aad47..d6c4ed7f 100644 --- a/net/utils.cpp +++ b/net/utils.cpp @@ -343,5 +343,19 @@ Resolver* new_default_resolver(uint64_t cache_ttl, uint64_t resolve_timeout) { return new DefaultResolver(cache_ttl, resolve_timeout); } +int parse_address_list(std::string_view list, std::vector* addresses, uint16_t default_port) { + addresses->clear(); + if (list.empty()) return 0; + for (auto p = list.begin(); p < list.end();) { + auto q = p + 1; + while (q < list.end() && *q != ',') ++q; + std::string_view epsv(p, q - p); + addresses->push_back(EndPoint::parse(epsv, default_port)); + p = q + 1; + } + return 0; +} + + } // namespace net } diff --git a/net/utils.h b/net/utils.h index f3a1f195..c413fbd2 100644 --- a/net/utils.h +++ b/net/utils.h @@ -174,5 +174,9 @@ class Resolver : public Object { */ Resolver* new_default_resolver(uint64_t cache_ttl = 3600UL * 1000000, uint64_t resolve_timeout = -1); +// parse a string list of endpoints into vector +// ip[:port],ip[:port],ip[:port],... +int parse_address_list(std::string_view list, std::vector* addresses, uint16_t default_port = 0); + } // namespace net }