The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

#include "sockaddr.h"
#include <ostream>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <net/if.h>
#endif
#include "pton.impl"
#include <system_error>
#define _NULL_TERMINATE(what, to) \
auto to = (char*)alloca(what.length()+1); \
std::memcpy(to, what.data(), what.length()); \
to[what.length()] = 0;
namespace panda { namespace net {
using std::errc;
using std::error_code;
using std::system_error;
using std::make_error_code;
// this function is fix for clang build, see MEIACORE-723 for details
// htonl is statement in clang, but not exression, it cannot be used dirrectly in var initializers
template <typename T>
static in_addr to_inaddr(T src) {
auto ia = htonl(src);
return reinterpret_cast<const in_addr&>(ia);
}
const in_addr SockAddr::Inet4::addr_any = to_inaddr(INADDR_ANY);
const in_addr SockAddr::Inet4::addr_loopback = to_inaddr(INADDR_LOOPBACK);
const in_addr SockAddr::Inet4::addr_broadcast = to_inaddr(INADDR_BROADCAST);
const in_addr SockAddr::Inet4::addr_none = to_inaddr(INADDR_NONE);
const in6_addr SockAddr::Inet6::addr_any = IN6ADDR_ANY_INIT;
const in6_addr SockAddr::Inet6::addr_loopback = IN6ADDR_LOOPBACK_INIT;
const SockAddr::Inet4 SockAddr::Inet4::sockaddr_any (SockAddr::Inet4::addr_any, 0);
const SockAddr::Inet4 SockAddr::Inet4::sockaddr_loopback(SockAddr::Inet4::addr_loopback, 0);
const SockAddr::Inet6 SockAddr::Inet6::sockaddr_any (SockAddr::Inet6::addr_any, 0);
const SockAddr::Inet6 SockAddr::Inet6::sockaddr_loopback(SockAddr::Inet6::addr_loopback, 0);
static system_error _not_supported () { return system_error(make_error_code(errc::address_family_not_supported)); }
void SockAddr::validate (const sockaddr* sa, size_t length) {
if (length < BASE_LEN || length > sizeof(SockAddr)) throw system_error(make_error_code(errc::bad_address));
switch (sa->sa_family) {
#ifndef _WIN32
case AF_UNIX:
#endif
case AF_UNSPEC:
case AF_INET:
case AF_INET6:
break;
default: throw _not_supported();
}
}
SockAddr::SockAddr (const sockaddr* _sa, size_t length) {
validate(_sa, length);
switch (_sa->sa_family) {
case AF_UNSPEC : sa.sa_family = AF_UNSPEC; break;
case AF_INET : sa4 = *(const sockaddr_in*)_sa; break;
case AF_INET6 : sa6 = *(const sockaddr_in6*)_sa; break;
#ifndef _WIN32
case AF_UNIX : memcpy(&this->sa, _sa, length);
fix_unix_path(length);
break;
#endif
}
}
SockAddr& SockAddr::operator= (const SockAddr& oth) {
if (this != &oth) memcpy(&sa, &oth.sa, oth.length());
return *this;
}
bool SockAddr::operator== (const SockAddr& oth) const {
if (family() != oth.family()) return false;
switch (family()) {
case AF_UNSPEC : return true;
case AF_INET : return !memcmp(&sa4, &oth.sa4, sizeof(sockaddr_in));
case AF_INET6 : return !memcmp(&sa6, &oth.sa6, sizeof(sockaddr_in6));
#ifndef _WIN32
case AF_UNIX : return !strcmp(sau.sun_path, oth.sau.sun_path);
#endif
default : throw _not_supported();
}
}
string SockAddr::ip () const {
switch (sa.sa_family) {
case AF_UNSPEC: return {};
case AF_INET: return as_inet4().ip();
case AF_INET6: return as_inet6().ip();
default: throw _not_supported();
}
}
uint16_t SockAddr::port () const {
switch (sa.sa_family) {
case AF_UNSPEC: return 0;
case AF_INET: return as_inet4().port();
case AF_INET6: return as_inet6().port();
default: throw _not_supported();
}
}
size_t SockAddr::length () const {
switch (sa.sa_family) {
case AF_UNSPEC: return BASE_LEN;
case AF_INET: return sizeof(sockaddr_in);
case AF_INET6: return sizeof(sockaddr_in6);
#ifndef _WIN32
case AF_UNIX: return BASE_LEN + strlen(sau.sun_path) + 1; /* null-byte */
#endif
default: throw _not_supported();
}
}
#ifndef _WIN32
void SockAddr::fix_unix_path (size_t length) noexcept {
assert(sa.sa_family == AF_UNIX);
// write null-byte by force
if (length <= BASE_LEN) sau.sun_path[0] = 0;
else sau.sun_path[length - BASE_LEN - 1] = 0;
}
#endif
std::ostream& operator<< (std::ostream& os, const SockAddr& sa) {
switch (sa.family()) {
case AF_UNSPEC : os << "<empty>"; break;
case AF_INET : os << sa.as_inet4().ip() << ':' << sa.as_inet4().port(); break;
case AF_INET6 :
os << '[' << sa.as_inet6().ip();
if (sa.as_inet6().scope_id()) os << '%' << sa.as_inet6().scope_id();
os << "]:" << sa.as_inet6().port();
break;
#ifndef _WIN32
case AF_UNIX : os << sa.as_unix().path(); break;
#endif
default : throw _not_supported();
}
return os;
}
SockAddr::Inet4::Inet4 (const string_view& ip, uint16_t port) {
_NULL_TERMINATE(ip, ipstr);
memset(&sa4, 0, sizeof(sockaddr_in));
sa4.sin_family = AF_INET;
sa4.sin_port = htons(port);
auto err = inet_pton4(ipstr, (unsigned char*)&(sa4.sin_addr));
if (err) throw system_error(make_error_code((errc)err));
}
SockAddr::Inet4::Inet4 (const in_addr& addr, uint16_t port) {
memset(&sa4, 0, sizeof(sockaddr_in));
sa4.sin_family = AF_INET;
sa4.sin_port = htons(port);
sa4.sin_addr = addr;
}
string SockAddr::Inet4::ip () const {
string ret;
char* buf = ret.reserve(IP4_MAX_ADDRSTRLEN);
auto err = inet_ntop4((const unsigned char*)&sa4.sin_addr, buf, IP4_MAX_ADDRSTRLEN);
assert(!err);
ret.length(std::strlen(buf));
return ret;
}
SockAddr::Inet6::Inet6 (const string_view& ip, uint16_t port, uint32_t scope_id, uint32_t flowinfo) {
memset(&sa6, 0, sizeof(sockaddr_in6));
sa6.sin6_family = AF_INET6;
sa6.sin6_port = htons(port);
sa6.sin6_flowinfo = htonl(flowinfo);
int err;
auto idx = ip.find('%');
if (idx == string_view::npos) {
sa6.sin6_scope_id = scope_id;
_NULL_TERMINATE(ip, ipstr);
err = inet_pton6(ipstr, (unsigned char*)&sa6.sin6_addr);
} else {
auto iplen = ip.length();
char scope[iplen-idx];
std::memcpy(scope, &ip[idx+1], iplen-idx-1);
scope[iplen-idx]=0;
#ifdef _WIN32
sa6.sin6_scope_id = atoi(scope);
#else
sa6.sin6_scope_id = if_nametoindex(scope);
#endif
char ipstr[idx+1];
std::memcpy(ipstr, ip.data(), idx);
ipstr[idx] = 0;
err = inet_pton6(ipstr, (unsigned char*)&sa6.sin6_addr);
}
if (err) throw system_error(make_error_code((errc)err));
}
SockAddr::Inet6::Inet6 (const in6_addr& addr, uint16_t port, uint32_t scope_id, uint32_t flowinfo) {
memset(&sa6, 0, sizeof(sockaddr_in6));
sa6.sin6_family = AF_INET6;
sa6.sin6_port = htons(port);
sa6.sin6_addr = addr;
sa6.sin6_scope_id = scope_id;
sa6.sin6_flowinfo = htonl(flowinfo);
}
string SockAddr::Inet6::ip () const {
string ret;
char* buf = ret.reserve(IP6_MAX_ADDRSTRLEN);
auto err = inet_ntop6((const unsigned char*)&sa6.sin6_addr, buf, IP6_MAX_ADDRSTRLEN);
assert(!err);
ret.length(std::strlen(buf));
return ret;
}
#ifndef _WIN32
SockAddr::Unix::Unix (const string_view& path) {
if (path.length() >= sizeof(sau.sun_path)) throw system_error(make_error_code(errc::invalid_argument));
sau.sun_family = AF_UNIX;
memcpy(sau.sun_path, path.data(), path.length());
sau.sun_path[path.length()] = 0;
}
#endif
}}