From 5b529ad5359dec66a430f97fe1278632086b5ca8 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Wed, 17 Oct 2018 14:20:32 +0000 Subject: [PATCH] added Socket::getIfceIndex(), Socket::getIfceAddress(), etc --- qse/include/qse/si/Socket.hpp | 18 +++ qse/include/qse/si/SocketAddress.hpp | 4 +- qse/lib/si/Socket.cpp | 224 ++++++++++++++++++++++++++- qse/lib/si/SocketAddress.cpp | 43 ++++- qse/samples/si/sck01.cpp | 36 +++++ 5 files changed, 316 insertions(+), 9 deletions(-) diff --git a/qse/include/qse/si/Socket.hpp b/qse/include/qse/si/Socket.hpp index d09ccdf7..51d0070b 100644 --- a/qse/include/qse/si/Socket.hpp +++ b/qse/include/qse/si/Socket.hpp @@ -96,11 +96,29 @@ public: /* TODO: sendmsg, recvmsg */ + // utility functions to retrieve network configuration information. + int getIfceIndex (const qse_mchar_t* name); + int getIfceIndex (const qse_wchar_t* name); + + // the following 6 functions are provided for backward compatibility. + // it is limited to a single address and they may suffer race condition. + // for example, you call getIfceAddress() followed by getIfceNetmask(). + // the network configuration information may change in between. + // the address/netmask pair may not be the valid fixed combination. + int getIfceAddress (const qse_mchar_t* name, SocketAddress* addr); + int getIfceAddress (const qse_wchar_t* name, SocketAddress* addr); + int getIfceNetmask (const qse_mchar_t* name, SocketAddress* addr); + int getIfceNetmask (const qse_wchar_t* name, SocketAddress* addr); + int getIfceBroadcast (const qse_mchar_t* name, SocketAddress* addr); + int getIfceBroadcast (const qse_wchar_t* name, SocketAddress* addr); + protected: qse_sck_hnd_t handle; + int domain; ErrorCode errcode; void set_errcode_with_syserr (int syserr); + int get_ifce_address (int cmd, const void* name, bool wchar, SocketAddress* addr); }; diff --git a/qse/include/qse/si/SocketAddress.hpp b/qse/include/qse/si/SocketAddress.hpp index 1e767a16..fef92c28 100644 --- a/qse/include/qse/si/SocketAddress.hpp +++ b/qse/include/qse/si/SocketAddress.hpp @@ -70,6 +70,9 @@ public: qse_uint16_t getPort() const QSE_CPP_NOEXCEPT; // in network-byte order void setPort (qse_uint16_t port) QSE_CPP_NOEXCEPT; // in network-byte order + qse_uint32_t getScopeId () QSE_CPP_NOEXCEPT; // in network-byte order + void setScopeId (qse_uint32_t scope_id) QSE_CPP_NOEXCEPT; // in network-byte order + int set (const qse_skad_t* skad) QSE_CPP_NOEXCEPT; int set (const qse_nwad_t* nwad) QSE_CPP_NOEXCEPT; int set (const qse_mchar_t* str) QSE_CPP_NOEXCEPT; @@ -77,7 +80,6 @@ public: int set (const qse_mchar_t* str, qse_size_t len) QSE_CPP_NOEXCEPT; int set (const qse_wchar_t* str, qse_size_t len) QSE_CPP_NOEXCEPT; - qse_mchar_t* toStrBuf (qse_mchar_t* buf, qse_size_t len) const QSE_CPP_NOEXCEPT; qse_wchar_t* toStrBuf (qse_wchar_t* buf, qse_size_t len) const QSE_CPP_NOEXCEPT; diff --git a/qse/lib/si/Socket.cpp b/qse/lib/si/Socket.cpp index c3cf7a7d..32c424c7 100644 --- a/qse/lib/si/Socket.cpp +++ b/qse/lib/si/Socket.cpp @@ -26,6 +26,7 @@ #include #include +#include #include "../cmn/mem-prv.h" #include @@ -35,6 +36,19 @@ #include #include +#if defined(HAVE_NET_IF_H) +# include +#endif + +#if defined(HAVE_SYS_IOCTL_H) +# include +#endif + +#if defined(HAVE_IFADDRS_H) +# include +#endif + +#include ///////////////////////////////// QSE_BEGIN_NAMESPACE(QSE) ///////////////////////////////// @@ -42,7 +56,7 @@ QSE_BEGIN_NAMESPACE(QSE) #include "../cmn/syserr.h" IMPLEMENT_SYSERR_TO_ERRNUM (Socket::ErrorCode, Socket::) -Socket::Socket () QSE_CPP_NOEXCEPT: handle(QSE_INVALID_SCKHND), errcode(E_ENOERR) +Socket::Socket () QSE_CPP_NOEXCEPT: handle(QSE_INVALID_SCKHND), domain(-1), errcode(E_ENOERR) { } @@ -68,7 +82,7 @@ int Socket::open (int domain, int type, int protocol, int traits) QSE_CPP_NOEXCE if (traits & Socket::T_CLOEXEC) type |= SOCK_CLOEXEC; open_socket: #endif - x = ::socket (domain, type, protocol); + x = ::socket(domain, type, protocol); if (x == -1) { #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) @@ -113,6 +127,18 @@ open_socket: done: this->close (); // close the existing handle if open. this->handle = x; + + // while it seems to be possible to get the domain value from a socket + // descriptor with getsockopt(SO_DOMAIN), the method doesn't seem universal. + // + // SO_DOMAIN (since Linux 2.6.32) + // Retrieves the socket domain as an integer, returning a value + // such as AF_INET6. See socket(2) for details. This socket + // option is read-only. + // + // let me just store the information in the class + + this->domain = domain; return 0; } @@ -122,6 +148,7 @@ void Socket::close () QSE_CPP_NOEXCEPT { qse_closesckhnd (this->handle); this->handle = QSE_INVALID_SCKHND; + this->domain = -1; } } @@ -564,6 +591,199 @@ qse_ssize_t Socket::receive (void* buf, qse_size_t len, SocketAddress& srcaddr) return n; } + +int Socket::getIfceIndex (const qse_mchar_t* name) +{ +#if defined(SIOCGIFINDEX) + struct ifreq ifr; + + QSE_MEMSET (&ifr, 0, QSE_SIZEOF(ifr)); + qse_mbsxcpy (ifr.ifr_name, QSE_COUNTOF(ifr.ifr_name), name); + + if (::ioctl(this->handle, SIOCGIFINDEX, &ifr) == -1) + { + this->setErrorCode (syserr_to_errnum(errno)); + return -1; + } + + #if defined(ifr_ifindex) + return ifr.ifr_ifindex; + #else + return ifr.ifr_index; + #endif +#else + this->setErrorCode (E_ENOIMPL); + return -1; +#endif +} + +int Socket::getIfceIndex (const qse_wchar_t* name) +{ +#if defined(SIOCGIFINDEX) + struct ifreq ifr; + + QSE_MEMSET (&ifr, 0, QSE_SIZEOF(ifr)); + + qse_size_t wlen, mlen = QSE_COUNTOF(ifr.ifr_name); + if (qse_wcstombs(name, &wlen, ifr.ifr_name, &mlen) <= -1 || wlen < qse_wcslen(name)) + { + this->setErrorCode (E_EINVAL); + return -1; + } + + if (::ioctl(this->handle, SIOCGIFINDEX, &ifr) == -1) + { + this->setErrorCode (syserr_to_errnum(errno)); + return -1; + } + + #if defined(ifr_ifindex) + return ifr.ifr_ifindex; + #else + return ifr.ifr_index; + #endif +#else + this->setErrorCode (E_ENOIMPL); + return -1; +#endif +} + +int Socket::getIfceAddress (const qse_mchar_t* name, SocketAddress* addr) +{ +#if defined(SIOCGIFADDR) + return this->get_ifce_address(SIOCGIFADDR, name, false, addr); +#else + this->setErrorCode (E_ENOIMPL); + return -1; +#endif +} + +int Socket::getIfceAddress (const qse_wchar_t* name, SocketAddress* addr) +{ +#if defined(SIOCGIFADDR) + return this->get_ifce_address(SIOCGIFADDR, name, true, addr); +#else + this->setErrorCode (E_ENOIMPL); + return -1; +#endif +} + +int Socket::getIfceNetmask(const qse_mchar_t* name, SocketAddress* addr) +{ +#if defined(SIOCGIFADDR) + return this->get_ifce_address(SIOCGIFNETMASK, name, false, addr); +#else + this->setErrorCode (E_ENOIMPL); + return -1; +#endif +} + +int Socket::getIfceNetmask (const qse_wchar_t* name, SocketAddress* addr) +{ +#if defined(SIOCGIFADDR) + return this->get_ifce_address(SIOCGIFNETMASK, name, true, addr); +#else + this->setErrorCode (E_ENOIMPL); + return -1; +#endif +} + +int Socket::getIfceBroadcast(const qse_mchar_t* name, SocketAddress* addr) +{ +#if defined(SIOCGIFADDR) + return this->get_ifce_address(SIOCGIFBRDADDR, name, false, addr); +#else + this->setErrorCode (E_ENOIMPL); + return -1; +#endif +} + +int Socket::getIfceBroadcast (const qse_wchar_t* name, SocketAddress* addr) +{ +#if defined(SIOCGIFADDR) + return this->get_ifce_address(SIOCGIFBRDADDR, name, true, addr); +#else + this->setErrorCode (E_ENOIMPL); + return -1; +#endif +} + +int Socket::get_ifce_address (int cmd, const void* name, bool wchar, SocketAddress* addr) +{ + struct ifreq ifr; + + QSE_MEMSET (&ifr, 0, QSE_SIZEOF(ifr)); + if (wchar) + { + qse_size_t wlen, mlen = QSE_COUNTOF(ifr.ifr_name); + if (qse_wcstombs((const qse_wchar_t*)name, &wlen, ifr.ifr_name, &mlen) <= -1 || wlen < qse_wcslen((const qse_wchar_t*)name)) + { + this->setErrorCode (E_EINVAL); + return -1; + } + } + else + { + qse_mbsxcpy (ifr.ifr_name, QSE_COUNTOF(ifr.ifr_name), (const qse_mchar_t*)name); + } + +#if defined(HAVE_GETIFADDRS) + struct ifaddrs* ifa; + if (::getifaddrs(&ifa) == 0) + { + for (struct ifaddrs* ife = ifa; ife; ife = ife->ifa_next) + { + if (qse_mbscmp (ifr.ifr_name, ife->ifa_name) != 0) continue; + + struct sockaddr* sa = QSE_NULL; + + switch (cmd) + { + case SIOCGIFADDR: + sa = ife->ifa_addr; + break; + + case SIOCGIFNETMASK: + sa = ife->ifa_netmask; + break; + + case SIOCGIFBRDADDR: + sa = ife->ifa_broadaddr; + break; + + default: + break; + } + + if (!sa || !sa->sa_data) continue; + if (sa->sa_family != this->domain) continue; /* skip an address that doesn't match the socket's domain */ + + *addr = SocketAddress((const qse_skad_t*)sa); + freeifaddrs (ifa); + return 0; + } + + freeifaddrs (ifa); + } +#endif + + if (::ioctl (this->handle, cmd, &ifr) == -1) + { + this->setErrorCode (syserr_to_errnum(errno)); + return -1; + } + + struct sockaddr* sa = (struct sockaddr*)&ifr.ifr_addr; + if (sa->sa_family != this->domain) + { + this->setErrorCode (E_ENOENT); + return -1; + } + + *addr = SocketAddress((const qse_skad_t*)&ifr.ifr_addr); + return 0; +} + ///////////////////////////////// QSE_END_NAMESPACE(QSE) ///////////////////////////////// diff --git a/qse/lib/si/SocketAddress.cpp b/qse/lib/si/SocketAddress.cpp index f7cec2d5..2901d177 100644 --- a/qse/lib/si/SocketAddress.cpp +++ b/qse/lib/si/SocketAddress.cpp @@ -143,21 +143,21 @@ qse_uint16_t SocketAddress::getPort () const QSE_CPP_NOEXCEPT { switch (FAMILY(&this->skad)) { -#if defined(AF_INET) + #if defined(AF_INET) case AF_INET: { struct sockaddr_in* v4 = (struct sockaddr_in*)&this->skad; return v4->sin_port; } -#endif + #endif -#if defined(AF_INET6) + #if defined(AF_INET6) case AF_INET6: { struct sockaddr_in6* v6 = (struct sockaddr_in6*)&this->skad; return v6->sin6_port; } -#endif + #endif } return 0; @@ -167,14 +167,14 @@ void SocketAddress::setPort (qse_uint16_t port) QSE_CPP_NOEXCEPT { switch (FAMILY(&this->skad)) { -#if defined(AF_INET) + #if defined(AF_INET) case AF_INET: { struct sockaddr_in* v4 = (struct sockaddr_in*)&this->skad; v4->sin_port = port; break; } -#endif + #endif #if defined(AF_INET6) case AF_INET6: @@ -187,6 +187,37 @@ void SocketAddress::setPort (qse_uint16_t port) QSE_CPP_NOEXCEPT } } +qse_uint32_t SocketAddress::getScopeId () QSE_CPP_NOEXCEPT +{ + switch (FAMILY(&this->skad)) + { + #if defined(AF_INET6) + case AF_INET6: + { + struct sockaddr_in6* v6 = (struct sockaddr_in6*)&this->skad; + return v6->sin6_scope_id; + } + #endif + } + + return 0; +} + +void SocketAddress::setScopeId (qse_uint32_t scope_id) QSE_CPP_NOEXCEPT +{ + switch (FAMILY(&this->skad)) + { + #if defined(AF_INET6) + case AF_INET6: + { + struct sockaddr_in6* v6 = (struct sockaddr_in6*)&this->skad; + v6->sin6_scope_id = scope_id; + break; + } + #endif + } +} + int SocketAddress::set (const qse_skad_t* skad) QSE_CPP_NOEXCEPT { this->skad = *skad; diff --git a/qse/samples/si/sck01.cpp b/qse/samples/si/sck01.cpp index 02d62597..16702f10 100644 --- a/qse/samples/si/sck01.cpp +++ b/qse/samples/si/sck01.cpp @@ -24,6 +24,42 @@ static int test1 () return -1; } + qse_printf (QSE_T("lo ifindex ===> %d\n"), s.getIfceIndex(QSE_WT("lo"))); + if (s.getIfceAddress(QSE_WT("lo"), &addr) >= 0) + { + qse_char_t buf[256]; + qse_printf (QSE_T("lo ifaddr ===> [%s]\n"), addr.toStrBuf(buf, QSE_COUNTOF(buf))); + } + if (s.getIfceNetmask(QSE_WT("lo"), &addr) >= 0) + { + qse_char_t buf[256]; + qse_printf (QSE_T("lo netmask ===> [%s]\n"), addr.toStrBuf(buf, QSE_COUNTOF(buf))); + } + if (s.getIfceBroadcast(QSE_WT("lo"), &addr) >= 0) + { + qse_char_t buf[256]; + qse_printf (QSE_T("lo broadcast ===> [%s]\n"), addr.toStrBuf(buf, QSE_COUNTOF(buf))); + } + + + qse_printf (QSE_T("eth0 ifindex ===> %d\n"), s.getIfceIndex(QSE_WT("eth0"))); + if (s.getIfceAddress(QSE_WT("eth0"), &addr) >= 0) + { + qse_char_t buf[256]; + qse_printf (QSE_T("eth0 ifaddr ===> [%s]\n"), addr.toStrBuf(buf, QSE_COUNTOF(buf))); + } + if (s.getIfceNetmask(QSE_WT("eth0"), &addr) >= 0) + { + qse_char_t buf[256]; + qse_printf (QSE_T("eth0 netmask ===> [%s]\n"), addr.toStrBuf(buf, QSE_COUNTOF(buf))); + } + if (s.getIfceBroadcast(QSE_WT("eth0"), &addr) >= 0) + { + qse_char_t buf[256]; + qse_printf (QSE_T("eth0 broadcast ===> [%s]\n"), addr.toStrBuf(buf, QSE_COUNTOF(buf))); + } + + addr.set ("[::1]:9999"); s.connect (addr);