Older/ToolKit/Network/sockutil.cpp
amass 9de3af15eb
All checks were successful
Deploy / PullDocker (push) Successful in 12s
Deploy / Build (push) Successful in 1m51s
add ZLMediaKit code for learning.
2024-09-28 23:55:00 +08:00

1141 lines
34 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <fcntl.h>
#include <assert.h>
#include <cstdio>
#include <cstring>
#include <mutex>
#include <string>
#include <unordered_map>
#include "sockutil.h"
#include "Util/util.h"
#include "Util/logger.h"
#include "Util/uv_errno.h"
#include "Util/onceToken.h"
#if defined (__APPLE__)
#include <ifaddrs.h>
#include <netinet/tcp.h>
#endif
using namespace std;
namespace toolkit {
#if defined(_WIN32)
static onceToken g_token([]() {
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
}, []() {
WSACleanup();
});
int ioctl(int fd, long cmd, u_long *ptr) {
return ioctlsocket(fd, cmd, ptr);
}
int close(int fd) {
return closesocket(fd);
}
#if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) {
struct sockaddr_storage ss;
unsigned long s = size;
ZeroMemory(&ss, sizeof(ss));
ss.ss_family = af;
switch (af) {
case AF_INET:
((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
break;
case AF_INET6:
((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
break;
default:
return NULL;
}
/* cannot direclty use &size because of strict aliasing rules */
return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0) ? dst : NULL;
}
int inet_pton(int af, const char *src, void *dst) {
struct sockaddr_storage ss;
int size = sizeof(ss);
char src_copy[INET6_ADDRSTRLEN + 1];
ZeroMemory(&ss, sizeof(ss));
/* stupid non-const API */
strncpy(src_copy, src, INET6_ADDRSTRLEN + 1);
src_copy[INET6_ADDRSTRLEN] = 0;
if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) {
switch (af) {
case AF_INET:
*(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
return 1;
case AF_INET6:
*(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
return 1;
}
}
return 0;
}
#endif
#endif // defined(_WIN32)
static inline string my_inet_ntop(int af, const void *addr) {
string ret;
ret.resize(128);
if (!inet_ntop(af, const_cast<void*>(addr), (char *) ret.data(), ret.size())) {
ret.clear();
} else {
ret.resize(strlen(ret.data()));
}
return ret;
}
static inline bool support_ipv6_l() {
auto fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (fd == -1) {
return false;
}
close(fd);
return true;
}
bool SockUtil::support_ipv6() {
static auto flag = support_ipv6_l();
return flag;
}
string SockUtil::inet_ntoa(const struct in_addr &addr) {
return my_inet_ntop(AF_INET, &addr);
}
std::string SockUtil::inet_ntoa(const struct in6_addr &addr) {
return my_inet_ntop(AF_INET6, &addr);
}
std::string SockUtil::inet_ntoa(const struct sockaddr *addr) {
switch (addr->sa_family) {
case AF_INET: return SockUtil::inet_ntoa(((struct sockaddr_in *)addr)->sin_addr);
case AF_INET6: {
if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr)) {
struct in_addr addr4;
memcpy(&addr4, 12 + (char *)&(((struct sockaddr_in6 *)addr)->sin6_addr), 4);
return SockUtil::inet_ntoa(addr4);
}
return SockUtil::inet_ntoa(((struct sockaddr_in6 *)addr)->sin6_addr);
}
default: return "";
}
}
uint16_t SockUtil::inet_port(const struct sockaddr *addr) {
switch (addr->sa_family) {
case AF_INET: return ntohs(((struct sockaddr_in *)addr)->sin_port);
case AF_INET6: return ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
default: return 0;
}
}
int SockUtil::setCloseWait(int fd, int second) {
linger m_sLinger;
//在调用closesocket()时还有数据未发送完,允许等待
// 若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭
m_sLinger.l_onoff = (second > 0);
m_sLinger.l_linger = second; //设置等待时间为x秒
int ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &m_sLinger, sizeof(linger));
if (ret == -1) {
#ifndef _WIN32
TraceL << "setsockopt SO_LINGER failed";
#endif
}
return ret;
}
int SockUtil::setNoDelay(int fd, bool on) {
int opt = on ? 1 : 0;
int ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
if (ret == -1) {
TraceL << "setsockopt TCP_NODELAY failed";
}
return ret;
}
int SockUtil::setReuseable(int fd, bool on, bool reuse_port) {
int opt = on ? 1 : 0;
int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
if (ret == -1) {
TraceL << "setsockopt SO_REUSEADDR failed";
return ret;
}
#if defined(SO_REUSEPORT)
if (reuse_port) {
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
if (ret == -1) {
TraceL << "setsockopt SO_REUSEPORT failed";
}
}
#endif
return ret;
}
int SockUtil::setBroadcast(int fd, bool on) {
int opt = on ? 1 : 0;
int ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
if (ret == -1) {
TraceL << "setsockopt SO_BROADCAST failed";
}
return ret;
}
int SockUtil::setKeepAlive(int fd, bool on, int interval, int idle, int times) {
// Enable/disable the keep-alive option
int opt = on ? 1 : 0;
int ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, static_cast<socklen_t>(sizeof(opt)));
if (ret == -1) {
TraceL << "setsockopt SO_KEEPALIVE failed";
}
#if !defined(_WIN32)
#if !defined(SOL_TCP) && defined(IPPROTO_TCP)
#define SOL_TCP IPPROTO_TCP
#endif
#if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE)
#define TCP_KEEPIDLE TCP_KEEPALIVE
#endif
// Set the keep-alive parameters
if (on && interval > 0 && ret != -1) {
ret = setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (char *) &idle, static_cast<socklen_t>(sizeof(idle)));
if (ret == -1) {
TraceL << "setsockopt TCP_KEEPIDLE failed";
}
ret = setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (char *) &interval, static_cast<socklen_t>(sizeof(interval)));
if (ret == -1) {
TraceL << "setsockopt TCP_KEEPINTVL failed";
}
ret = setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (char *) &times, static_cast<socklen_t>(sizeof(times)));
if (ret == -1) {
TraceL << "setsockopt TCP_KEEPCNT failed";
}
}
#endif
return ret;
}
int SockUtil::setCloExec(int fd, bool on) {
#if !defined(_WIN32)
int flags = fcntl(fd, F_GETFD);
if (flags == -1) {
TraceL << "fcntl F_GETFD failed";
return -1;
}
if (on) {
flags |= FD_CLOEXEC;
} else {
int cloexec = FD_CLOEXEC;
flags &= ~cloexec;
}
int ret = fcntl(fd, F_SETFD, flags);
if (ret == -1) {
TraceL << "fcntl F_SETFD failed";
return -1;
}
return ret;
#else
return -1;
#endif
}
int SockUtil::setNoSigpipe(int fd) {
#if defined(SO_NOSIGPIPE)
int set = 1;
auto ret = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (char *) &set, sizeof(int));
if (ret == -1) {
TraceL << "setsockopt SO_NOSIGPIPE failed";
}
return ret;
#else
return -1;
#endif
}
int SockUtil::setNoBlocked(int fd, bool noblock) {
#if defined(_WIN32)
unsigned long ul = noblock;
#else
int ul = noblock;
#endif //defined(_WIN32)
int ret = ioctl(fd, FIONBIO, &ul); //设置为非阻塞模式
if (ret == -1) {
TraceL << "ioctl FIONBIO failed";
}
return ret;
}
int SockUtil::setRecvBuf(int fd, int size) {
if (size <= 0) {
// use the system default value
return 0;
}
int ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size));
if (ret == -1) {
TraceL << "setsockopt SO_RCVBUF failed";
}
return ret;
}
int SockUtil::setSendBuf(int fd, int size) {
if (size <= 0) {
return 0;
}
int ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size));
if (ret == -1) {
TraceL << "setsockopt SO_SNDBUF failed";
}
return ret;
}
class DnsCache {
public:
static DnsCache &Instance() {
static DnsCache instance;
return instance;
}
bool getDomainIP(const char *host, sockaddr_storage &storage, int ai_family = AF_INET,
int ai_socktype = SOCK_STREAM, int ai_protocol = IPPROTO_TCP, int expire_sec = 60) {
try {
storage = SockUtil::make_sockaddr(host, 0);
return true;
} catch (...) {
auto item = getCacheDomainIP(host, expire_sec);
if (!item) {
item = getSystemDomainIP(host);
if (item) {
setCacheDomainIP(host, item);
}
}
if (item) {
auto addr = getPerferredAddress(item.get(), ai_family, ai_socktype, ai_protocol);
memcpy(&storage, addr->ai_addr, addr->ai_addrlen);
}
return (bool)item;
}
}
private:
class DnsItem {
public:
std::shared_ptr<struct addrinfo> addr_info;
time_t create_time;
};
std::shared_ptr<struct addrinfo> getCacheDomainIP(const char *host, int expireSec) {
lock_guard<mutex> lck(_mtx);
auto it = _dns_cache.find(host);
if (it == _dns_cache.end()) {
//没有记录
return nullptr;
}
if (it->second.create_time + expireSec < time(nullptr)) {
//已过期
_dns_cache.erase(it);
return nullptr;
}
return it->second.addr_info;
}
void setCacheDomainIP(const char *host, std::shared_ptr<struct addrinfo> addr) {
lock_guard<mutex> lck(_mtx);
DnsItem item;
item.addr_info = std::move(addr);
item.create_time = time(nullptr);
_dns_cache[host] = std::move(item);
}
std::shared_ptr<struct addrinfo> getSystemDomainIP(const char *host) {
struct addrinfo *answer = nullptr;
//阻塞式dns解析可能被打断
int ret = -1;
do {
ret = getaddrinfo(host, nullptr, nullptr, &answer);
} while (ret == -1 && get_uv_error(true) == UV_EINTR);
if (!answer) {
WarnL << "getaddrinfo failed: " << host;
return nullptr;
}
return std::shared_ptr<struct addrinfo>(answer, freeaddrinfo);
}
struct addrinfo *getPerferredAddress(struct addrinfo *answer, int ai_family, int ai_socktype, int ai_protocol) {
auto ptr = answer;
while (ptr) {
if (ptr->ai_family == ai_family && ptr->ai_socktype == ai_socktype && ptr->ai_protocol == ai_protocol) {
return ptr;
}
ptr = ptr->ai_next;
}
return answer;
}
private:
mutex _mtx;
unordered_map<string, DnsItem> _dns_cache;
};
bool SockUtil::getDomainIP(const char *host, uint16_t port, struct sockaddr_storage &addr,
int ai_family, int ai_socktype, int ai_protocol, int expire_sec) {
bool flag = DnsCache::Instance().getDomainIP(host, addr, ai_family, ai_socktype, ai_protocol, expire_sec);
if (flag) {
switch (addr.ss_family ) {
case AF_INET : ((sockaddr_in *) &addr)->sin_port = htons(port); break;
case AF_INET6 : ((sockaddr_in6 *) &addr)->sin6_port = htons(port); break;
default: break;
}
}
return flag;
}
static int set_ipv6_only(int fd, bool flag) {
int opt = flag;
int ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof opt);
if (ret == -1) {
TraceL << "setsockopt IPV6_V6ONLY failed";
}
return ret;
}
static int bind_sock6(int fd, const char *ifr_ip, uint16_t port) {
set_ipv6_only(fd, false);
struct sockaddr_in6 addr;
bzero(&addr, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
if (1 != inet_pton(AF_INET6, ifr_ip, &(addr.sin6_addr))) {
if (strcmp(ifr_ip, "0.0.0.0")) {
WarnL << "inet_pton to ipv6 address failed: " << ifr_ip;
}
addr.sin6_addr = IN6ADDR_ANY_INIT;
}
if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
WarnL << "Bind socket failed: " << get_uv_errmsg(true);
return -1;
}
return 0;
}
static int bind_sock4(int fd, const char *ifr_ip, uint16_t port) {
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (1 != inet_pton(AF_INET, ifr_ip, &(addr.sin_addr))) {
if (strcmp(ifr_ip, "::")) {
WarnL << "inet_pton to ipv4 address failed: " << ifr_ip;
}
addr.sin_addr.s_addr = INADDR_ANY;
}
if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
WarnL << "Bind socket failed: " << get_uv_errmsg(true);
return -1;
}
return 0;
}
static int bind_sock(int fd, const char *ifr_ip, uint16_t port, int family) {
switch (family) {
case AF_INET: return bind_sock4(fd, ifr_ip, port);
case AF_INET6: return bind_sock6(fd, ifr_ip, port);
default: return -1;
}
}
int SockUtil::connect(const char *host, uint16_t port, bool async, const char *local_ip, uint16_t local_port) {
sockaddr_storage addr;
//优先使用ipv4地址
if (!getDomainIP(host, port, addr, AF_INET, SOCK_STREAM, IPPROTO_TCP)) {
//dns解析失败
return -1;
}
int sockfd = (int) socket(addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0) {
WarnL << "Create socket failed: " << host;
return -1;
}
setReuseable(sockfd);
setNoSigpipe(sockfd);
setNoBlocked(sockfd, async);
setNoDelay(sockfd);
setSendBuf(sockfd);
setRecvBuf(sockfd);
setCloseWait(sockfd);
setCloExec(sockfd);
if (bind_sock(sockfd, local_ip, local_port, addr.ss_family) == -1) {
close(sockfd);
return -1;
}
if (::connect(sockfd, (sockaddr *) &addr, get_sock_len((sockaddr *)&addr)) == 0) {
//同步连接成功
return sockfd;
}
if (async && get_uv_error(true) == UV_EAGAIN) {
//异步连接成功
return sockfd;
}
WarnL << "Connect socket to " << host << " " << port << " failed: " << get_uv_errmsg(true);
close(sockfd);
return -1;
}
int SockUtil::listen(const uint16_t port, const char *local_ip, int back_log) {
int fd = -1;
int family = support_ipv6() ? (is_ipv4(local_ip) ? AF_INET : AF_INET6) : AF_INET;
if ((fd = (int)socket(family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
WarnL << "Create socket failed: " << get_uv_errmsg(true);
return -1;
}
setReuseable(fd, true, false);
setNoBlocked(fd);
setCloExec(fd);
if (bind_sock(fd, local_ip, port, family) == -1) {
close(fd);
return -1;
}
//开始监听
if (::listen(fd, back_log) == -1) {
WarnL << "Listen socket failed: " << get_uv_errmsg(true);
close(fd);
return -1;
}
return fd;
}
int SockUtil::getSockError(int fd) {
int opt;
socklen_t optLen = static_cast<socklen_t>(sizeof(opt));
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *) &opt, &optLen) < 0) {
return get_uv_error(true);
} else {
return uv_translate_posix_error(opt);
}
}
using getsockname_type = decltype(getsockname);
static bool get_socket_addr(int fd, struct sockaddr_storage &addr, getsockname_type func) {
socklen_t addr_len = sizeof(addr);
if (-1 == func(fd, (struct sockaddr *)&addr, &addr_len)) {
return false;
}
return true;
}
bool SockUtil::get_sock_local_addr(int fd, struct sockaddr_storage &addr) {
return get_socket_addr(fd, addr, getsockname);
}
bool SockUtil::get_sock_peer_addr(int fd, struct sockaddr_storage &addr) {
return get_socket_addr(fd, addr, getpeername);
}
static string get_socket_ip(int fd, getsockname_type func) {
struct sockaddr_storage addr;
if (!get_socket_addr(fd, addr, func)) {
return "";
}
return SockUtil::inet_ntoa((struct sockaddr *)&addr);
}
static uint16_t get_socket_port(int fd, getsockname_type func) {
struct sockaddr_storage addr;
if (!get_socket_addr(fd, addr, func)) {
return 0;
}
return SockUtil::inet_port((struct sockaddr *)&addr);
}
string SockUtil::get_local_ip(int fd) {
return get_socket_ip(fd, getsockname);
}
string SockUtil::get_peer_ip(int fd) {
return get_socket_ip(fd, getpeername);
}
uint16_t SockUtil::get_local_port(int fd) {
return get_socket_port(fd, getsockname);
}
uint16_t SockUtil::get_peer_port(int fd) {
return get_socket_port(fd, getpeername);
}
#if defined(__APPLE__)
template<typename FUN>
void for_each_netAdapter_apple(FUN &&fun) { //type: struct ifaddrs *
struct ifaddrs *interfaces = nullptr;
struct ifaddrs *adapter = nullptr;
if (getifaddrs(&interfaces) == 0) {
adapter = interfaces;
while (adapter) {
if (adapter->ifa_addr->sa_family == AF_INET) {
if (fun(adapter)) {
break;
}
}
adapter = adapter->ifa_next;
}
freeifaddrs(interfaces);
}
}
#endif //defined(__APPLE__)
#if defined(_WIN32)
template<typename FUN>
void for_each_netAdapter_win32(FUN && fun) { //type: PIP_ADAPTER_INFO
unsigned long nSize = sizeof(IP_ADAPTER_INFO);
PIP_ADAPTER_INFO adapterList = (PIP_ADAPTER_INFO)new char[nSize];
int nRet = GetAdaptersInfo(adapterList, &nSize);
if (ERROR_BUFFER_OVERFLOW == nRet) {
delete[] adapterList;
adapterList = (PIP_ADAPTER_INFO)new char[nSize];
nRet = GetAdaptersInfo(adapterList, &nSize);
}
auto adapterPtr = adapterList;
while (adapterPtr && ERROR_SUCCESS == nRet) {
if (fun(adapterPtr)) {
break;
}
adapterPtr = adapterPtr->Next;
}
//释放内存空间
delete[] adapterList;
}
#endif //defined(_WIN32)
#if !defined(_WIN32) && !defined(__APPLE__)
template<typename FUN>
void for_each_netAdapter_posix(FUN &&fun){ //type: struct ifreq *
struct ifconf ifconf;
char buf[1024 * 10];
//初始化ifconf
ifconf.ifc_len = sizeof(buf);
ifconf.ifc_buf = buf;
int sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
WarnL << "Create socket failed: " << get_uv_errmsg(true);
return;
}
if (-1 == ioctl(sockfd, SIOCGIFCONF, &ifconf)) { //获取所有接口信息
WarnL << "ioctl SIOCGIFCONF failed: " << get_uv_errmsg(true);
close(sockfd);
return;
}
close(sockfd);
//接下来一个一个的获取IP地址
struct ifreq * adapter = (struct ifreq*) buf;
for (int i = (ifconf.ifc_len / sizeof(struct ifreq)); i > 0; --i,++adapter) {
if(fun(adapter)){
break;
}
}
}
#endif //!defined(_WIN32) && !defined(__APPLE__)
bool check_ip(string &address, const string &ip) {
if (ip != "127.0.0.1" && ip != "0.0.0.0") {
/*获取一个有效IP*/
address = ip;
uint32_t addressInNetworkOrder = htonl(inet_addr(ip.data()));
if (/*(addressInNetworkOrder >= 0x0A000000 && addressInNetworkOrder < 0x0E000000) ||*/
(addressInNetworkOrder >= 0xAC100000 && addressInNetworkOrder < 0xAC200000) ||
(addressInNetworkOrder >= 0xC0A80000 && addressInNetworkOrder < 0xC0A90000)) {
//A类私有IP地址
//10.0.0.010.255.255.255
//B类私有IP地址
//172.16.0.0172.31.255.255
//C类私有IP地址
//192.168.0.0192.168.255.255
//如果是私有地址 说明在nat内部
/* 优先采用局域网地址该地址很可能是wifi地址
* 一般来说,无线路由器分配的地址段是BC类私有ip地址
* 而A类地址多用于蜂窝移动网络
*/
return true;
}
}
return false;
}
string SockUtil::get_local_ip() {
#if defined(__APPLE__)
string address = "127.0.0.1";
for_each_netAdapter_apple([&](struct ifaddrs *adapter) {
string ip = SockUtil::inet_ntoa(adapter->ifa_addr);
if (strstr(adapter->ifa_name, "docker")) {
return false;
}
return check_ip(address, ip);
});
return address;
#elif defined(_WIN32)
string address = "127.0.0.1";
for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) {
IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList);
while (ipAddr) {
string ip = ipAddr->IpAddress.String;
if (strstr(adapter->AdapterName, "docker")) {
return false;
}
if(check_ip(address,ip)){
return true;
}
ipAddr = ipAddr->Next;
}
return false;
});
return address;
#else
string address = "127.0.0.1";
for_each_netAdapter_posix([&](struct ifreq *adapter){
string ip = SockUtil::inet_ntoa(&(adapter->ifr_addr));
if (strstr(adapter->ifr_name, "docker")) {
return false;
}
return check_ip(address,ip);
});
return address;
#endif
}
vector<map<string, string> > SockUtil::getInterfaceList() {
vector<map<string, string> > ret;
#if defined(__APPLE__)
for_each_netAdapter_apple([&](struct ifaddrs *adapter) {
map<string, string> obj;
obj["ip"] = SockUtil::inet_ntoa(adapter->ifa_addr);
obj["name"] = adapter->ifa_name;
ret.emplace_back(std::move(obj));
return false;
});
#elif defined(_WIN32)
for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) {
IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList);
while (ipAddr) {
map<string,string> obj;
obj["ip"] = ipAddr->IpAddress.String;
obj["name"] = adapter->AdapterName;
ret.emplace_back(std::move(obj));
ipAddr = ipAddr->Next;
}
return false;
});
#else
for_each_netAdapter_posix([&](struct ifreq *adapter){
map<string,string> obj;
obj["ip"] = SockUtil::inet_ntoa(&(adapter->ifr_addr));
obj["name"] = adapter->ifr_name;
ret.emplace_back(std::move(obj));
return false;
});
#endif
return ret;
}
int SockUtil::bindUdpSock(const uint16_t port, const char *local_ip, bool enable_reuse) {
int fd = -1;
int family = support_ipv6() ? (is_ipv4(local_ip) ? AF_INET : AF_INET6) : AF_INET;
if ((fd = (int)socket(family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
WarnL << "Create socket failed: " << get_uv_errmsg(true);
return -1;
}
if (enable_reuse) {
setReuseable(fd);
}
setNoSigpipe(fd);
setNoBlocked(fd);
setSendBuf(fd);
setRecvBuf(fd);
setCloseWait(fd);
setCloExec(fd);
if (bind_sock(fd, local_ip, port, family) == -1) {
close(fd);
return -1;
}
return fd;
}
int SockUtil::dissolveUdpSock(int fd) {
struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
if (-1 == getsockname(fd, (struct sockaddr *)&addr, &addr_len)) {
return -1;
}
addr.ss_family = AF_UNSPEC;
if (-1 == ::connect(fd, (struct sockaddr *)&addr, addr_len) && get_uv_error() != UV_EAFNOSUPPORT) {
// mac/ios时返回EAFNOSUPPORT错误
WarnL << "Connect socket AF_UNSPEC failed: " << get_uv_errmsg(true);
return -1;
}
return 0;
}
string SockUtil::get_ifr_ip(const char *if_name) {
#if defined(__APPLE__)
string ret;
for_each_netAdapter_apple([&](struct ifaddrs *adapter) {
if (strcmp(adapter->ifa_name, if_name) == 0) {
ret = SockUtil::inet_ntoa(adapter->ifa_addr);
return true;
}
return false;
});
return ret;
#elif defined(_WIN32)
string ret;
for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) {
IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList);
while (ipAddr){
if (strcmp(if_name,adapter->AdapterName) == 0){
//ip匹配到了
ret.assign(ipAddr->IpAddress.String);
return true;
}
ipAddr = ipAddr->Next;
}
return false;
});
return ret;
#else
string ret;
for_each_netAdapter_posix([&](struct ifreq *adapter){
if(strcmp(adapter->ifr_name,if_name) == 0) {
ret = SockUtil::inet_ntoa(&(adapter->ifr_addr));
return true;
}
return false;
});
return ret;
#endif
}
string SockUtil::get_ifr_name(const char *local_ip) {
#if defined(__APPLE__)
string ret = "en0";
for_each_netAdapter_apple([&](struct ifaddrs *adapter) {
string ip = SockUtil::inet_ntoa(adapter->ifa_addr);
if (ip == local_ip) {
ret = adapter->ifa_name;
return true;
}
return false;
});
return ret;
#elif defined(_WIN32)
string ret = "en0";
for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) {
IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList);
while (ipAddr){
if (strcmp(local_ip,ipAddr->IpAddress.String) == 0){
//ip匹配到了
ret.assign(adapter->AdapterName);
return true;
}
ipAddr = ipAddr->Next;
}
return false;
});
return ret;
#else
string ret = "en0";
for_each_netAdapter_posix([&](struct ifreq *adapter){
string ip = SockUtil::inet_ntoa(&(adapter->ifr_addr));
if(ip == local_ip) {
ret = adapter->ifr_name;
return true;
}
return false;
});
return ret;
#endif
}
string SockUtil::get_ifr_mask(const char *if_name) {
#if defined(__APPLE__)
string ret;
for_each_netAdapter_apple([&](struct ifaddrs *adapter) {
if (strcmp(if_name, adapter->ifa_name) == 0) {
ret = SockUtil::inet_ntoa(adapter->ifa_netmask);
return true;
}
return false;
});
return ret;
#elif defined(_WIN32)
string ret;
for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) {
if (strcmp(if_name,adapter->AdapterName) == 0){
//找到了该网卡
IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList);
//获取第一个ip的子网掩码
ret.assign(ipAddr->IpMask.String);
return true;
}
return false;
});
return ret;
#else
int fd;
struct ifreq ifr_mask;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
WarnL << "Create socket failed: " << get_uv_errmsg(true);
return "";
}
memset(&ifr_mask, 0, sizeof(ifr_mask));
strncpy(ifr_mask.ifr_name, if_name, sizeof(ifr_mask.ifr_name) - 1);
if ((ioctl(fd, SIOCGIFNETMASK, &ifr_mask)) < 0) {
WarnL << "ioctl SIOCGIFNETMASK on " << if_name << " failed: " << get_uv_errmsg(true);
close(fd);
return "";
}
close(fd);
return SockUtil::inet_ntoa(&(ifr_mask.ifr_netmask));
#endif // defined(_WIN32)
}
string SockUtil::get_ifr_brdaddr(const char *if_name) {
#if defined(__APPLE__)
string ret;
for_each_netAdapter_apple([&](struct ifaddrs *adapter) {
if (strcmp(if_name, adapter->ifa_name) == 0) {
ret = SockUtil::inet_ntoa(adapter->ifa_broadaddr);
return true;
}
return false;
});
return ret;
#elif defined(_WIN32)
string ret;
for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) {
if (strcmp(if_name, adapter->AdapterName) == 0) {
//找到该网卡
IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList);
in_addr broadcast;
broadcast.S_un.S_addr = (inet_addr(ipAddr->IpAddress.String) & inet_addr(ipAddr->IpMask.String)) | (~inet_addr(ipAddr->IpMask.String));
ret = SockUtil::inet_ntoa(broadcast);
return true;
}
return false;
});
return ret;
#else
int fd;
struct ifreq ifr_mask;
fd = socket( AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
WarnL << "Create socket failed: " << get_uv_errmsg(true);
return "";
}
memset(&ifr_mask, 0, sizeof(ifr_mask));
strncpy(ifr_mask.ifr_name, if_name, sizeof(ifr_mask.ifr_name) - 1);
if ((ioctl(fd, SIOCGIFBRDADDR, &ifr_mask)) < 0) {
WarnL << "ioctl SIOCGIFBRDADDR failed: " << get_uv_errmsg(true);
close(fd);
return "";
}
close(fd);
return SockUtil::inet_ntoa(&(ifr_mask.ifr_broadaddr));
#endif
}
#define ip_addr_netcmp(addr1, addr2, mask) (((addr1) & (mask)) == ((addr2) & (mask)))
bool SockUtil::in_same_lan(const char *myIp, const char *dstIp) {
string mask = get_ifr_mask(get_ifr_name(myIp).data());
return ip_addr_netcmp(inet_addr(myIp), inet_addr(dstIp), inet_addr(mask.data()));
}
static void clearMulticastAllSocketOption(int socket) {
#if defined(IP_MULTICAST_ALL)
// This option is defined in modern versions of Linux to overcome a bug in the Linux kernel's default behavior.
// When set to 0, it ensures that we receive only packets that were sent to the specified IP multicast address,
// even if some other process on the same system has joined a different multicast group with the same port number.
int multicastAll = 0;
(void)setsockopt(socket, IPPROTO_IP, IP_MULTICAST_ALL, (void*)&multicastAll, sizeof multicastAll);
// Ignore the call's result. Should it fail, we'll still receive packets (just perhaps more than intended)
#endif
}
int SockUtil::setMultiTTL(int fd, uint8_t ttl) {
int ret = -1;
#if defined(IP_MULTICAST_TTL)
ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &ttl, sizeof(ttl));
if (ret == -1) {
TraceL << "setsockopt IP_MULTICAST_TTL failed";
}
#endif
clearMulticastAllSocketOption(fd);
return ret;
}
int SockUtil::setMultiIF(int fd, const char *local_ip) {
int ret = -1;
#if defined(IP_MULTICAST_IF)
struct in_addr addr;
addr.s_addr = inet_addr(local_ip);
ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr, sizeof(addr));
if (ret == -1) {
TraceL << "setsockopt IP_MULTICAST_IF failed";
}
#endif
clearMulticastAllSocketOption(fd);
return ret;
}
int SockUtil::setMultiLOOP(int fd, bool accept) {
int ret = -1;
#if defined(IP_MULTICAST_LOOP)
uint8_t loop = accept;
ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop, sizeof(loop));
if (ret == -1) {
TraceL << "setsockopt IP_MULTICAST_LOOP failed";
}
#endif
clearMulticastAllSocketOption(fd);
return ret;
}
int SockUtil::joinMultiAddr(int fd, const char *addr, const char *local_ip) {
int ret = -1;
#if defined(IP_ADD_MEMBERSHIP)
struct ip_mreq imr;
imr.imr_multiaddr.s_addr = inet_addr(addr);
imr.imr_interface.s_addr = inet_addr(local_ip);
ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq));
if (ret == -1) {
TraceL << "setsockopt IP_ADD_MEMBERSHIP failed: " << get_uv_errmsg(true);
}
#endif
clearMulticastAllSocketOption(fd);
return ret;
}
int SockUtil::leaveMultiAddr(int fd, const char *addr, const char *local_ip) {
int ret = -1;
#if defined(IP_DROP_MEMBERSHIP)
struct ip_mreq imr;
imr.imr_multiaddr.s_addr = inet_addr(addr);
imr.imr_interface.s_addr = inet_addr(local_ip);
ret = setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq));
if (ret == -1) {
TraceL << "setsockopt IP_DROP_MEMBERSHIP failed: " << get_uv_errmsg(true);
}
#endif
clearMulticastAllSocketOption(fd);
return ret;
}
template<typename A, typename B>
static inline void write4Byte(A &&a, B &&b) {
memcpy(&a, &b, sizeof(a));
}
int SockUtil::joinMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip) {
int ret = -1;
#if defined(IP_ADD_SOURCE_MEMBERSHIP)
struct ip_mreq_source imr;
write4Byte(imr.imr_multiaddr, inet_addr(addr));
write4Byte(imr.imr_sourceaddr, inet_addr(src_ip));
write4Byte(imr.imr_interface, inet_addr(local_ip));
ret = setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq_source));
if (ret == -1) {
TraceL << "setsockopt IP_ADD_SOURCE_MEMBERSHIP failed: " << get_uv_errmsg(true);
}
#endif
clearMulticastAllSocketOption(fd);
return ret;
}
int SockUtil::leaveMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip) {
int ret = -1;
#if defined(IP_DROP_SOURCE_MEMBERSHIP)
struct ip_mreq_source imr;
write4Byte(imr.imr_multiaddr, inet_addr(addr));
write4Byte(imr.imr_sourceaddr, inet_addr(src_ip));
write4Byte(imr.imr_interface, inet_addr(local_ip));
ret = setsockopt(fd, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq_source));
if (ret == -1) {
TraceL << "setsockopt IP_DROP_SOURCE_MEMBERSHIP failed: " << get_uv_errmsg(true);
}
#endif
clearMulticastAllSocketOption(fd);
return ret;
}
bool SockUtil::is_ipv4(const char *host) {
struct in_addr addr;
return 1 == inet_pton(AF_INET, host, &addr);
}
bool SockUtil::is_ipv6(const char *host) {
struct in6_addr addr;
return 1 == inet_pton(AF_INET6, host, &addr);
}
socklen_t SockUtil::get_sock_len(const struct sockaddr *addr) {
switch (addr->sa_family) {
case AF_INET : return sizeof(sockaddr_in);
case AF_INET6 : return sizeof(sockaddr_in6);
default: assert(0); return 0;
}
}
struct sockaddr_storage SockUtil::make_sockaddr(const char *host, uint16_t port) {
struct sockaddr_storage storage;
bzero(&storage, sizeof(storage));
struct in_addr addr;
struct in6_addr addr6;
if (1 == inet_pton(AF_INET, host, &addr)) {
// host是ipv4
reinterpret_cast<struct sockaddr_in &>(storage).sin_addr = addr;
reinterpret_cast<struct sockaddr_in &>(storage).sin_family = AF_INET;
reinterpret_cast<struct sockaddr_in &>(storage).sin_port = htons(port);
return storage;
}
if (1 == inet_pton(AF_INET6, host, &addr6)) {
// host是ipv6
reinterpret_cast<struct sockaddr_in6 &>(storage).sin6_addr = addr6;
reinterpret_cast<struct sockaddr_in6 &>(storage).sin6_family = AF_INET6;
reinterpret_cast<struct sockaddr_in6 &>(storage).sin6_port = htons(port);
return storage;
}
throw std::invalid_argument(string("Not ip address: ") + host);
}
} // namespace toolkit