|
|
@@ -20,11 +20,27 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
|
|
#include "JackNetUnixSocket.h" |
|
|
|
#include "JackError.h" |
|
|
|
|
|
|
|
#include <arpa/inet.h> |
|
|
|
#include <sys/socket.h> |
|
|
|
#include <ifaddrs.h> |
|
|
|
#include <net/if.h> |
|
|
|
#include <unistd.h> |
|
|
|
#include <fcntl.h> |
|
|
|
|
|
|
|
using namespace std; |
|
|
|
|
|
|
|
// See RFC 3493; The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 Edition |
|
|
|
#define _sock4(x) (*(struct sockaddr_in *)&x) |
|
|
|
#define _sock6(x) (*(struct sockaddr_in6*)&x) |
|
|
|
#define _ss_addr_p(x) ((fFamily==AF_INET6) ? \ |
|
|
|
(void*)&(((struct sockaddr_in6*)&x)->sin6_addr) \ |
|
|
|
: (void*)&(((struct sockaddr_in *)&x)->sin_addr)) |
|
|
|
#define JNS_UNSPEC 0x0 |
|
|
|
#define JNS_PROBED 0x1 |
|
|
|
#define JNS_BOUND 0x2 |
|
|
|
#define JNS_CONNCD 0x4 |
|
|
|
#define JNS_MCAST 0x10 |
|
|
|
|
|
|
|
namespace Jack |
|
|
|
{ |
|
|
|
//utility ********************************************************************************************************* |
|
|
@@ -40,40 +56,23 @@ namespace Jack |
|
|
|
|
|
|
|
//construct/destruct*********************************************************************************************** |
|
|
|
JackNetUnixSocket::JackNetUnixSocket() |
|
|
|
: fFamily(AF_UNSPEC), fSockfd(0), fState(JNS_UNSPEC), fPort(0), fTimeOut(0) |
|
|
|
{ |
|
|
|
fSockfd = 0; |
|
|
|
fPort = 0; |
|
|
|
fTimeOut = 0; |
|
|
|
fSendAddr.sin_family = AF_INET; |
|
|
|
fSendAddr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
|
memset(&fSendAddr.sin_zero, 0, 8); |
|
|
|
fRecvAddr.sin_family = AF_INET; |
|
|
|
fRecvAddr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
|
memset(&fRecvAddr.sin_zero, 0, 8); |
|
|
|
Reset(); |
|
|
|
} |
|
|
|
|
|
|
|
JackNetUnixSocket::JackNetUnixSocket(const char* ip, int port) |
|
|
|
: fFamily(AF_UNSPEC), fSockfd(0), fState(JNS_UNSPEC), fPort(0), fTimeOut(0) |
|
|
|
{ |
|
|
|
fSockfd = 0; |
|
|
|
Reset(); |
|
|
|
fPort = port; |
|
|
|
fTimeOut = 0; |
|
|
|
fSendAddr.sin_family = AF_INET; |
|
|
|
fSendAddr.sin_port = htons(port); |
|
|
|
inet_aton(ip, &fSendAddr.sin_addr); |
|
|
|
memset(&fSendAddr.sin_zero, 0, 8); |
|
|
|
fRecvAddr.sin_family = AF_INET; |
|
|
|
fRecvAddr.sin_port = htons(port); |
|
|
|
fRecvAddr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
|
memset(&fRecvAddr.sin_zero, 0, 8); |
|
|
|
if(NewSocket(ip)==SOCKET_ERROR) |
|
|
|
jack_error("JackNetUnixSocket::JackNetUnixSocket Cannot initialize (%s:%d): %s",ip,port,strerror(NET_ERROR_CODE)); |
|
|
|
} |
|
|
|
|
|
|
|
JackNetUnixSocket::JackNetUnixSocket(const JackNetUnixSocket& socket) |
|
|
|
{ |
|
|
|
fSockfd = 0; |
|
|
|
fTimeOut = 0; |
|
|
|
fPort = socket.fPort; |
|
|
|
fSendAddr = socket.fSendAddr; |
|
|
|
fRecvAddr = socket.fRecvAddr; |
|
|
|
Clone(socket); |
|
|
|
} |
|
|
|
|
|
|
|
JackNetUnixSocket::~JackNetUnixSocket() |
|
|
@@ -83,23 +82,106 @@ namespace Jack |
|
|
|
|
|
|
|
JackNetUnixSocket& JackNetUnixSocket::operator=(const JackNetUnixSocket& socket) |
|
|
|
{ |
|
|
|
if (this != &socket) { |
|
|
|
fSockfd = 0; |
|
|
|
fPort = socket.fPort; |
|
|
|
fSendAddr = socket.fSendAddr; |
|
|
|
fRecvAddr = socket.fRecvAddr; |
|
|
|
} |
|
|
|
if (this != &socket) |
|
|
|
Clone(socket); |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
//socket*********************************************************************************************************** |
|
|
|
void JackNetUnixSocket::Clone(const JackNetUnixSocket& socket) |
|
|
|
{ |
|
|
|
fSockfd = 0; |
|
|
|
fTimeOut = 0; |
|
|
|
fPort = socket.fPort; |
|
|
|
fFamily = socket.fFamily; |
|
|
|
fState = socket.fState; |
|
|
|
fState &= (JNS_MCAST | JNS_PROBED); // Reset all fields except mcast and probed |
|
|
|
memcpy(&fSendAddr, & socket.fSendAddr, sizeof(fSendAddr)); |
|
|
|
memcpy(&fRecvAddr, & socket.fRecvAddr, sizeof(fRecvAddr)); |
|
|
|
} |
|
|
|
/* When using Multicast always create socket with new (mip,port) or call NewSocket(mip) for autoinitialized sockets first. |
|
|
|
* This makes sure proper workarounds for Multicast incompatibility will be activated while setting up address family. |
|
|
|
*/ |
|
|
|
int JackNetUnixSocket::ProbeAF(const char* ip, struct sockaddr_storage *addr, int (*call)(int,const struct sockaddr*,socklen_t)) |
|
|
|
{ |
|
|
|
struct addrinfo hint,*res, *ri; |
|
|
|
char sport[6]; |
|
|
|
int ret; |
|
|
|
|
|
|
|
snprintf(sport,6,"%d",fPort); |
|
|
|
memset(&hint,0,sizeof(hint)); |
|
|
|
hint.ai_family = fFamily; |
|
|
|
hint.ai_socktype = SOCK_DGRAM; |
|
|
|
hint.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_NUMERICSERV; |
|
|
|
if(ip == NULL) hint.ai_flags |= AI_PASSIVE; |
|
|
|
ret = getaddrinfo(ip,sport,&hint,&res); |
|
|
|
if(ret) { |
|
|
|
jack_error("JackNetUnixSocket::ProbeAF getaddrinfo(%s:%s): %s",ip,sport,gai_strerror(ret)); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
#define GLIBC_BUG |
|
|
|
// An ugly GLIBC bug, which one may argue to be RFC bug. See http://sourceware.org/bugzilla/show_bug.cgi?id=14967 |
|
|
|
#ifdef GLIBC_BUG |
|
|
|
// Of course gai configuration can say to have v4 having precedence explicitly but... here we prefer ipv6. period |
|
|
|
if(!ip && res->ai_family == AF_INET && res->ai_next && res->ai_next->ai_family == AF_INET6) { |
|
|
|
jack_log("JackNetUnixSocket::ProbeAF swapping addrinfo entries to work around GLIBC getaddrinfo bug: AF_INET<->AF_INET6"); |
|
|
|
// Swapping first two entries |
|
|
|
ri = res->ai_next; |
|
|
|
res->ai_next = ri->ai_next; |
|
|
|
ri->ai_next = res; |
|
|
|
res = ri; |
|
|
|
} else // << Remove up to this very line including once GLIBC is fixed << |
|
|
|
#endif |
|
|
|
ri = res; |
|
|
|
do { |
|
|
|
jack_log("JackNetUnixSocket::ProbeAF trying AF[%d], TYPE[%d], PROTO[%d]", |
|
|
|
ri->ai_family,ri->ai_socktype,ri->ai_protocol); |
|
|
|
ret = socket(ri->ai_family, ri->ai_socktype, ri->ai_protocol); |
|
|
|
if(ret < 0) |
|
|
|
continue; |
|
|
|
if(!(*call)(ret, ri->ai_addr, ri->ai_addrlen)) |
|
|
|
break; |
|
|
|
close(ret); |
|
|
|
jack_log("JackNetUnixSocket::ProbeAF failed for AF[%d], %s",ri->ai_family,(ri->ai_next)?"trying next":"giving up"); |
|
|
|
} while((ri = ri->ai_next) != NULL); |
|
|
|
// probe successfully made a *call on socket for ip represented by ri |
|
|
|
if(ri) { |
|
|
|
fSockfd = ret; |
|
|
|
fFamily = ri->ai_family; |
|
|
|
memcpy(addr,ri->ai_addr,ri->ai_addrlen); |
|
|
|
jack_log("JackNetUnixSocket::ProbeAF probed[%d] AF[%d] for %s",ret,fFamily,ip); |
|
|
|
fState |= JNS_PROBED; |
|
|
|
if(fFamily == AF_INET6 && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr)) |
|
|
|
fState |= JNS_MCAST; |
|
|
|
} else |
|
|
|
ret = SOCKET_ERROR; |
|
|
|
freeaddrinfo(res); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
int JackNetUnixSocket::NewSocket() |
|
|
|
{ |
|
|
|
return NewSocket(NULL); |
|
|
|
} |
|
|
|
int JackNetUnixSocket::NewSocket(const char *ip) |
|
|
|
{ |
|
|
|
if(fFamily == AF_UNSPEC) { |
|
|
|
if(ip) { |
|
|
|
if(ProbeAF(ip,&fSendAddr,connect)<0) |
|
|
|
return SOCKET_ERROR; |
|
|
|
} else { |
|
|
|
if(ProbeAF(ip,&fRecvAddr,bind)<0) |
|
|
|
return SOCKET_ERROR; |
|
|
|
// It should be with AF/port/ANY after bind |
|
|
|
memcpy(&fSendAddr,&fRecvAddr,sizeof(fRecvAddr)); |
|
|
|
} |
|
|
|
// Now re-create the fd to get a brand-new unnamed socket |
|
|
|
} |
|
|
|
if (fSockfd) { |
|
|
|
Close(); |
|
|
|
Reset(); |
|
|
|
} |
|
|
|
fSockfd = socket(AF_INET, SOCK_DGRAM, 0); |
|
|
|
fSockfd = socket(fFamily, SOCK_DGRAM, 0); |
|
|
|
|
|
|
|
/* Enable address reuse */ |
|
|
|
int res, on = 1; |
|
|
@@ -139,58 +221,117 @@ namespace Jack |
|
|
|
{ |
|
|
|
if (strcmp(ip, "127.0.0.1") == 0) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
} else if(!strcmp(ip,"::1")) |
|
|
|
return true; |
|
|
|
|
|
|
|
char host_name[32]; |
|
|
|
gethostname(host_name, sizeof(host_name)); |
|
|
|
struct ifaddrs *ifas, *ifa; |
|
|
|
socklen_t len; |
|
|
|
|
|
|
|
struct hostent* host = gethostbyname(host_name); |
|
|
|
if (host) { |
|
|
|
for (int i = 0; host->h_addr_list[i] != 0; ++i) { |
|
|
|
struct in_addr addr; |
|
|
|
memcpy(&addr, host->h_addr_list[i], sizeof(struct in_addr)); |
|
|
|
if (strcmp(inet_ntoa(addr), ip) == 0) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
return false; |
|
|
|
} else { |
|
|
|
return false; |
|
|
|
if (getifaddrs(&ifas) == -1) { |
|
|
|
jack_error("JackNetUnixSocket::IsLocal error in getifaddrs"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) { |
|
|
|
if (ifa->ifa_addr == NULL) |
|
|
|
continue; // Address is mandatory |
|
|
|
len = (ifa->ifa_addr->sa_family==AF_INET)?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6); |
|
|
|
if(!getnameinfo(ifa->ifa_addr, len, f_addr_buff, INET6_ADDRSTRLEN, NULL,0, NI_NUMERICSERV | NI_DGRAM | NI_NUMERICSERV | NI_DGRAM | NI_NUMERICHOST)) |
|
|
|
if(!strcmp(f_addr_buff,ip)) |
|
|
|
break; |
|
|
|
} |
|
|
|
freeifaddrs(ifas); |
|
|
|
return (ifa != NULL); |
|
|
|
} |
|
|
|
|
|
|
|
int JackNetUnixSocket::Bind() |
|
|
|
{ |
|
|
|
return ::bind(fSockfd, reinterpret_cast<socket_address_t*>(&fRecvAddr), sizeof(socket_address_t)); |
|
|
|
int yes=1; |
|
|
|
if(fState & JNS_BOUND) return 0; |
|
|
|
// Multicast is incompatible with V4MAPPED or V4COMPAT addresses, if probe detected MC we need V6ONLY |
|
|
|
if(fFamily == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&_sock6(fRecvAddr).sin6_addr) && fState & JNS_MCAST) |
|
|
|
if(SetOption(IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes))) return SOCKET_ERROR; |
|
|
|
if(::bind(fSockfd, reinterpret_cast<struct sockaddr*>(&fRecvAddr), sizeof(fRecvAddr))) return SOCKET_ERROR; |
|
|
|
fState |= JNS_BOUND; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
int JackNetUnixSocket::Bind(const char *if_name) |
|
|
|
{ |
|
|
|
int ret = Bind(); |
|
|
|
if(!ret && strcmp(if_name,"any")) { |
|
|
|
if(fFamily == AF_INET) { |
|
|
|
// 'all' for this case will lead to 'last valid interface', which is not that one might expect |
|
|
|
if(strcmp(if_name,"all")) |
|
|
|
ret = BindMCastIface(if_name, IP_MULTICAST_IF, &_sock4(fSendAddr).sin_addr); |
|
|
|
else |
|
|
|
jack_error("Multicast Interface all not found, sending from default"); |
|
|
|
} else if(fFamily == AF_INET6) { |
|
|
|
struct if_nameindex *if_ni = if_nameindex(); // In V6 world we do everything differently. |
|
|
|
if(if_ni) { |
|
|
|
int i; |
|
|
|
for (i=0; if_ni[i].if_index > 0; i++) { |
|
|
|
if(if_ni[i].if_index == 1) |
|
|
|
continue; // Skip loopback |
|
|
|
if(!strcmp(if_ni[i].if_name,if_name)) { |
|
|
|
ret = SetOption(IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_ni[i].if_index, sizeof(if_ni[i].if_index)); |
|
|
|
jack_log("JackNetUnixSocket::Bind Multicasting from %s",if_ni[i].if_name); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if(if_ni[i].if_index == 0) jack_error("Multicast Interface %s not found, sending from default",if_name); |
|
|
|
if_freenameindex(if_ni); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
int JackNetUnixSocket::BindWith(const char* ip) |
|
|
|
{ |
|
|
|
int addr_conv = inet_aton(ip, &fRecvAddr.sin_addr); |
|
|
|
if (addr_conv < 0) { |
|
|
|
return addr_conv; |
|
|
|
if(fFamily == AF_UNSPEC) { |
|
|
|
if(!fPort) return SOCKET_ERROR; |
|
|
|
if(ProbeAF(ip,&fRecvAddr,&bind)<0) return SOCKET_ERROR; |
|
|
|
fState |= JNS_BOUND; |
|
|
|
return 0; |
|
|
|
} else { |
|
|
|
if(SetRecvIP(ip)==-1) return SOCKET_ERROR; |
|
|
|
return Bind(); |
|
|
|
} |
|
|
|
return Bind(); |
|
|
|
} |
|
|
|
|
|
|
|
int JackNetUnixSocket::BindWith(int port) |
|
|
|
{ |
|
|
|
fRecvAddr.sin_port = htons(port); |
|
|
|
return Bind(); |
|
|
|
if(fFamily == AF_UNSPEC) { |
|
|
|
fPort = port; |
|
|
|
if(ProbeAF(NULL,&fRecvAddr,&bind)<0) return SOCKET_ERROR; |
|
|
|
fState |= JNS_BOUND; |
|
|
|
return 0; |
|
|
|
} else { |
|
|
|
SetPort(port); |
|
|
|
return Bind(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int JackNetUnixSocket::Connect() |
|
|
|
{ |
|
|
|
return connect(fSockfd, reinterpret_cast<socket_address_t*>(&fSendAddr), sizeof(socket_address_t)); |
|
|
|
if(fFamily != AF_UNSPEC) |
|
|
|
return connect(fSockfd, (struct sockaddr*)&fSendAddr,sizeof(fSendAddr)); |
|
|
|
jack_error("JackNetUnixSocket::Connect Family not initialized"); |
|
|
|
return SOCKET_ERROR; |
|
|
|
} |
|
|
|
|
|
|
|
int JackNetUnixSocket::ConnectTo(const char* ip) |
|
|
|
{ |
|
|
|
int addr_conv = inet_aton(ip, &fSendAddr.sin_addr); |
|
|
|
if (addr_conv < 0) { |
|
|
|
return addr_conv; |
|
|
|
socklen_t l=sizeof(fRecvAddr); |
|
|
|
if(fPort==0) return SOCKET_ERROR; |
|
|
|
if(fState & JNS_PROBED) { |
|
|
|
Reset(); |
|
|
|
fFamily=AF_UNSPEC; |
|
|
|
} |
|
|
|
return Connect(); |
|
|
|
if(fSockfd) |
|
|
|
Close(); |
|
|
|
if(ProbeAF(ip,&fSendAddr,&connect)<0) return SOCKET_ERROR; |
|
|
|
fState |= JNS_CONNCD; |
|
|
|
return getsockname(fSockfd, (struct sockaddr *)&fRecvAddr, &l); |
|
|
|
} |
|
|
|
|
|
|
|
void JackNetUnixSocket::Close() |
|
|
@@ -199,18 +340,17 @@ namespace Jack |
|
|
|
close(fSockfd); |
|
|
|
} |
|
|
|
fSockfd = 0; |
|
|
|
fState = JNS_UNSPEC; |
|
|
|
} |
|
|
|
|
|
|
|
void JackNetUnixSocket::Reset() |
|
|
|
{ |
|
|
|
fSendAddr.sin_family = AF_INET; |
|
|
|
fSendAddr.sin_port = htons(fPort); |
|
|
|
fSendAddr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
|
memset(&fSendAddr.sin_zero, 0, 8); |
|
|
|
fRecvAddr.sin_family = AF_INET; |
|
|
|
fRecvAddr.sin_port = htons(fPort); |
|
|
|
fRecvAddr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
|
memset(&fRecvAddr.sin_zero, 0, 8); |
|
|
|
memset(&fSendAddr, 0, sizeof(fSendAddr)); |
|
|
|
fSendAddr.ss_family = fFamily; |
|
|
|
memset(&fRecvAddr, 0, sizeof(fRecvAddr)); |
|
|
|
fRecvAddr.ss_family = fFamily; |
|
|
|
if(fPort) |
|
|
|
SetPort(fPort); |
|
|
|
} |
|
|
|
|
|
|
|
bool JackNetUnixSocket::IsSocket() |
|
|
@@ -222,8 +362,18 @@ namespace Jack |
|
|
|
void JackNetUnixSocket::SetPort(int port) |
|
|
|
{ |
|
|
|
fPort = port; |
|
|
|
fSendAddr.sin_port = htons(port); |
|
|
|
fRecvAddr.sin_port = htons(port); |
|
|
|
switch(fFamily) { // Playing dumb here, in fact port section of both sockaddrs is compatible |
|
|
|
case AF_INET: |
|
|
|
_sock4(fSendAddr).sin_port = htons(port); |
|
|
|
_sock4(fRecvAddr).sin_port = htons(port); |
|
|
|
break; |
|
|
|
case AF_INET6: |
|
|
|
_sock6(fSendAddr).sin6_port = htons(port); |
|
|
|
_sock6(fRecvAddr).sin6_port = htons(port); |
|
|
|
break; |
|
|
|
default: |
|
|
|
jack_info("JackNetUnixSocket::SetPort: Family not initialized"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int JackNetUnixSocket::GetPort() |
|
|
@@ -234,22 +384,34 @@ namespace Jack |
|
|
|
//address*********************************************************************************************************** |
|
|
|
int JackNetUnixSocket::SetAddress(const char* ip, int port) |
|
|
|
{ |
|
|
|
int addr_conv = inet_aton(ip, &fSendAddr.sin_addr); |
|
|
|
if (addr_conv < 0) { |
|
|
|
return addr_conv; |
|
|
|
if(fFamily == AF_UNSPEC) { |
|
|
|
fPort=port; |
|
|
|
return ProbeAF(ip,&fSendAddr,&connect); |
|
|
|
} else { |
|
|
|
SetPort(port); |
|
|
|
return inet_pton(fFamily, ip, _ss_addr_p(fSendAddr)); |
|
|
|
} |
|
|
|
fSendAddr.sin_port = htons(port); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
int JackNetUnixSocket::SetSendIP(const char *ip) |
|
|
|
{ |
|
|
|
if(fFamily == AF_UNSPEC) return -1; |
|
|
|
return inet_pton(fFamily, ip, _ss_addr_p(fSendAddr)); |
|
|
|
} |
|
|
|
|
|
|
|
char* JackNetUnixSocket::GetSendIP() |
|
|
|
{ |
|
|
|
return inet_ntoa(fSendAddr.sin_addr); |
|
|
|
return (char*)inet_ntop(fFamily, _ss_addr_p(fSendAddr),f_addr_buff,INET6_ADDRSTRLEN); |
|
|
|
} |
|
|
|
|
|
|
|
int JackNetUnixSocket::SetRecvIP(const char *ip) |
|
|
|
{ |
|
|
|
if(fFamily == AF_UNSPEC) return -1; |
|
|
|
return inet_pton(fFamily, ip, _ss_addr_p(fRecvAddr)); |
|
|
|
} |
|
|
|
|
|
|
|
char* JackNetUnixSocket::GetRecvIP() |
|
|
|
{ |
|
|
|
return inet_ntoa(fRecvAddr.sin_addr); |
|
|
|
return (char*)inet_ntop(fFamily, _ss_addr_p(fRecvAddr),f_addr_buff,INET6_ADDRSTRLEN); |
|
|
|
} |
|
|
|
|
|
|
|
//utility************************************************************************************************************ |
|
|
@@ -260,10 +422,111 @@ namespace Jack |
|
|
|
|
|
|
|
int JackNetUnixSocket::JoinMCastGroup(const char* ip) |
|
|
|
{ |
|
|
|
struct ip_mreq multicast_req; |
|
|
|
inet_aton(ip, &multicast_req.imr_multiaddr); |
|
|
|
multicast_req.imr_interface.s_addr = htonl(INADDR_ANY); |
|
|
|
return SetOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, &multicast_req, sizeof(multicast_req)); |
|
|
|
return JoinMCastGroup(ip,"any"); |
|
|
|
} |
|
|
|
/** Glory and shame of IPv6 interoperability: Multicast |
|
|
|
* Not only API is completely incompatible, but even socket is incompatible. I.e. you cannot use V4MAPPED/COMPATIBLE |
|
|
|
* sockets/sockaddress for Multicast. Any V4 address is just a legacy unicast socket for IPv6. Moreover, IPv6 API |
|
|
|
* implementation itself is a big mess even though there's no time anymore and way back. Here we're using POSIX API. |
|
|
|
* When dealing with Multicast - Address Family should be set once and forever for used multicast address. This |
|
|
|
* implies we cannot use V4 addresses on V6 sockets. So multicast should have V6ONLY soscket option set to allow v6 |
|
|
|
* adapter, master or manager coexist on the same host. See ProbeAF/Bind for these tricks. |
|
|
|
*/ |
|
|
|
int JackNetUnixSocket::JoinMCastGroup(const char* ip, const char *if_name) |
|
|
|
{ |
|
|
|
int level, option, length; |
|
|
|
void *mreq; |
|
|
|
char addr[sizeof(in6_addr)]; |
|
|
|
inet_pton(fFamily, ip, addr); |
|
|
|
if(!strcmp(if_name,"any")) { // UNSPEC binding we can do in-place using void pointers |
|
|
|
if(fFamily == AF_INET) { |
|
|
|
struct ip_mreq multicast_req; |
|
|
|
multicast_req.imr_multiaddr.s_addr = *(uint32_t*)(addr); |
|
|
|
multicast_req.imr_interface.s_addr = htonl(INADDR_ANY); |
|
|
|
level = IPPROTO_IP; |
|
|
|
option = IP_ADD_MEMBERSHIP; |
|
|
|
mreq=&multicast_req; |
|
|
|
length = sizeof(ip_mreq); |
|
|
|
} else if(fFamily == AF_INET6) { |
|
|
|
struct ipv6_mreq mreq6; |
|
|
|
memcpy(&mreq6.ipv6mr_multiaddr,addr,sizeof(in6_addr)); |
|
|
|
mreq6.ipv6mr_interface = 0; |
|
|
|
level = IPPROTO_IPV6; |
|
|
|
option = IPV6_ADD_MEMBERSHIP; |
|
|
|
mreq = &mreq6; |
|
|
|
length = sizeof(ipv6_mreq); |
|
|
|
} else { |
|
|
|
jack_error("Unsupported family[%d]",fFamily); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
return SetOption(level, option, mreq, length); |
|
|
|
} else { // For anything more complex need to call family-specific routine |
|
|
|
if(fFamily == AF_INET) |
|
|
|
return BindMCastIface(if_name, IP_ADD_MEMBERSHIP, (struct in_addr *)addr); |
|
|
|
else if(fFamily == AF_INET6) |
|
|
|
return BindMCast6Iface(if_name, (struct in6_addr *)addr); |
|
|
|
else { |
|
|
|
jack_error("Unsupported family[%d]",fFamily); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
int JackNetUnixSocket::BindMCastIface(const char *if_name, const int option, struct in_addr *addr) |
|
|
|
{ |
|
|
|
struct ifaddrs *ifas, *ifa; |
|
|
|
struct ip_mreq mreq; |
|
|
|
int specific = strcmp("all",if_name); |
|
|
|
int ret=-1; |
|
|
|
char *if_last=(char *)"any"; |
|
|
|
|
|
|
|
if (getifaddrs(&ifas) == -1) { |
|
|
|
jack_error("JackNetUnixSocket::BindMCastIface error in getifaddrs"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
mreq.imr_multiaddr.s_addr = addr->s_addr; |
|
|
|
for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) { |
|
|
|
if (ifa->ifa_addr == NULL) |
|
|
|
continue; // Address is mandatory |
|
|
|
if(!ifa->ifa_name || !strcmp(if_last,ifa->ifa_name)) |
|
|
|
continue; // Name as well, also skip already enabled interface |
|
|
|
if(!(ifa->ifa_flags & IFF_MULTICAST) || !(ifa->ifa_flags & IFF_RUNNING)) |
|
|
|
continue; // And non multicast or down interface |
|
|
|
if(ifa->ifa_addr->sa_family != AF_INET) |
|
|
|
continue; // And non-matching family |
|
|
|
if(!specific || !strcmp(ifa->ifa_name,if_name)) { |
|
|
|
mreq.imr_interface.s_addr = ((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr.s_addr; |
|
|
|
ret = SetOption(IPPROTO_IP, option, &mreq, sizeof(struct ip_mreq)); |
|
|
|
if(ret) |
|
|
|
break; |
|
|
|
if_last = ifa->ifa_name; |
|
|
|
jack_log("JackNetUnixSocket::BindMCastIface attaching to %s", if_last); |
|
|
|
} |
|
|
|
} |
|
|
|
freeifaddrs(ifas); |
|
|
|
if(!strcmp(if_last,"any")) |
|
|
|
jack_error("JackNetUnixSocket::BindMCastIface cannot find valid interface"); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
int JackNetUnixSocket::BindMCast6Iface(const char *if_name, struct in6_addr *mip) |
|
|
|
{ |
|
|
|
int i, ret=-1, specific=strcmp("all",if_name); |
|
|
|
struct if_nameindex *if_ni = if_nameindex(); |
|
|
|
struct ipv6_mreq mreq; |
|
|
|
if(if_ni) { |
|
|
|
memcpy(&mreq.ipv6mr_multiaddr, mip, sizeof(struct in6_addr)); |
|
|
|
for (i=0; if_ni[i].if_index > 0; i++) { |
|
|
|
if(if_ni[i].if_index == 1) |
|
|
|
continue; // Skip loopback |
|
|
|
mreq.ipv6mr_interface = if_ni[i].if_index; |
|
|
|
if(!specific || !strcmp(if_ni[i].if_name,if_name)) { |
|
|
|
ret = SetOption(IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); |
|
|
|
if(ret < 0) |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if_freenameindex(if_ni); |
|
|
|
} |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
//options************************************************************************************************************ |
|
|
@@ -282,19 +545,19 @@ namespace Jack |
|
|
|
#if defined(__sun__) || defined(sun) |
|
|
|
int JackNetUnixSocket::SetTimeOut(int us) |
|
|
|
{ |
|
|
|
int flags; |
|
|
|
int flags; |
|
|
|
fTimeOut = us; |
|
|
|
|
|
|
|
if ((flags = fcntl(fSockfd, F_GETFL, 0)) < 0) { |
|
|
|
jack_error("JackNetUnixSocket::SetTimeOut error in fcntl F_GETFL"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
jack_error("JackNetUnixSocket::SetTimeOut error in fcntl F_GETFL"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
flags |= O_NONBLOCK; |
|
|
|
if (fcntl(fSockfd, F_SETFL, flags) < 0) { |
|
|
|
jack_error("JackNetUnixSocket::SetTimeOut error in fcntl F_SETFL"); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
flags |= O_NONBLOCK; |
|
|
|
if (fcntl(fSockfd, F_SETFL, flags) < 0) { |
|
|
|
jack_error("JackNetUnixSocket::SetTimeOut error in fcntl F_SETFL"); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
@@ -304,25 +567,25 @@ namespace Jack |
|
|
|
if (fTimeOut > 0) { |
|
|
|
|
|
|
|
struct timeval tv; |
|
|
|
fd_set fdset; |
|
|
|
ssize_t res; |
|
|
|
fd_set fdset; |
|
|
|
ssize_t res; |
|
|
|
|
|
|
|
tv.tv_sec = fTimeOut / 1000000; |
|
|
|
tv.tv_usec = fTimeOut % 1000000; |
|
|
|
tv.tv_usec = fTimeOut % 1000000; |
|
|
|
|
|
|
|
FD_ZERO(&fdset); |
|
|
|
FD_SET(fSockfd, &fdset); |
|
|
|
FD_ZERO(&fdset); |
|
|
|
FD_SET(fSockfd, &fdset); |
|
|
|
|
|
|
|
do { |
|
|
|
res = select(fSockfd + 1, &fdset, NULL, NULL, &tv); |
|
|
|
} while (res < 0 && errno == EINTR); |
|
|
|
|
|
|
|
if (res < 0) { |
|
|
|
return res; |
|
|
|
if (res < 0) { |
|
|
|
return res; |
|
|
|
} else if (res == 0) { |
|
|
|
errno = ETIMEDOUT; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
@@ -333,25 +596,25 @@ namespace Jack |
|
|
|
if (fTimeOut > 0) { |
|
|
|
|
|
|
|
struct timeval tv; |
|
|
|
fd_set fdset; |
|
|
|
ssize_t res; |
|
|
|
fd_set fdset; |
|
|
|
ssize_t res; |
|
|
|
|
|
|
|
tv.tv_sec = fTimeOut / 1000000; |
|
|
|
tv.tv_usec = fTimeOut % 1000000; |
|
|
|
|
|
|
|
FD_ZERO(&fdset); |
|
|
|
FD_SET(fSockfd, &fdset); |
|
|
|
FD_ZERO(&fdset); |
|
|
|
FD_SET(fSockfd, &fdset); |
|
|
|
|
|
|
|
do { |
|
|
|
res = select(fSockfd + 1, NULL, &fdset, NULL, &tv); |
|
|
|
} while (res < 0 && errno == EINTR); |
|
|
|
|
|
|
|
if (res < 0) { |
|
|
|
return res; |
|
|
|
if (res < 0) { |
|
|
|
return res; |
|
|
|
} else if (res == 0) { |
|
|
|
errno = ETIMEDOUT; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
@@ -382,7 +645,16 @@ namespace Jack |
|
|
|
int JackNetUnixSocket::SetLocalLoop() |
|
|
|
{ |
|
|
|
char disable = 0; |
|
|
|
return SetOption(IPPROTO_IP, IP_MULTICAST_LOOP, &disable, sizeof(disable)); |
|
|
|
unsigned int dis6 = 0; |
|
|
|
switch(fFamily) { |
|
|
|
case AF_INET: |
|
|
|
return SetOption(IPPROTO_IP, IP_MULTICAST_LOOP, &disable, sizeof(disable)); |
|
|
|
case AF_INET6: |
|
|
|
return SetOption(IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &dis6, sizeof(dis6)); |
|
|
|
default: |
|
|
|
jack_error("JackNetUnixSocket::SetLocalLoop: Family not initialized"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//network operations************************************************************************************************** |
|
|
@@ -394,7 +666,7 @@ namespace Jack |
|
|
|
} |
|
|
|
#endif |
|
|
|
int res; |
|
|
|
if ((res = sendto(fSockfd, buffer, nbytes, flags, reinterpret_cast<socket_address_t*>(&fSendAddr), sizeof(socket_address_t))) < 0) { |
|
|
|
if ((res = sendto(fSockfd, buffer, nbytes, flags, reinterpret_cast<struct sockaddr *>(&fSendAddr), sizeof(fSendAddr))) < 0) { |
|
|
|
jack_error("SendTo fd = %ld err = %s", fSockfd, strerror(errno)); |
|
|
|
} |
|
|
|
return res; |
|
|
@@ -402,11 +674,11 @@ namespace Jack |
|
|
|
|
|
|
|
int JackNetUnixSocket::SendTo(const void* buffer, size_t nbytes, int flags, const char* ip) |
|
|
|
{ |
|
|
|
int addr_conv = inet_aton(ip, &fSendAddr.sin_addr); |
|
|
|
int addr_conv = inet_pton(fFamily, ip, _ss_addr_p(fSendAddr)); |
|
|
|
if (addr_conv < 1) { |
|
|
|
return addr_conv; |
|
|
|
} |
|
|
|
fSendAddr.sin_port = htons(fPort); |
|
|
|
//fSendAddr.sin_port = htons(fPort); |
|
|
|
#if defined(__sun__) || defined(sun) |
|
|
|
if (WaitWrite() < 0) { |
|
|
|
return -1; |
|
|
@@ -431,14 +703,14 @@ namespace Jack |
|
|
|
|
|
|
|
int JackNetUnixSocket::RecvFrom(void* buffer, size_t nbytes, int flags) |
|
|
|
{ |
|
|
|
socklen_t addr_len = sizeof(socket_address_t); |
|
|
|
socklen_t addr_len = sizeof(fRecvAddr); |
|
|
|
#if defined(__sun__) || defined(sun) |
|
|
|
if (WaitRead() < 0) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
#endif |
|
|
|
int res; |
|
|
|
if ((res = recvfrom(fSockfd, buffer, nbytes, flags, reinterpret_cast<socket_address_t*>(&fRecvAddr), &addr_len)) < 0) { |
|
|
|
if ((res = recvfrom(fSockfd, buffer, nbytes, flags, reinterpret_cast<struct sockaddr *>(&fRecvAddr), &addr_len)) < 0) { |
|
|
|
jack_error("RecvFrom fd = %ld err = %s", fSockfd, strerror(errno)); |
|
|
|
} |
|
|
|
return res; |
|
|
@@ -460,14 +732,15 @@ namespace Jack |
|
|
|
|
|
|
|
int JackNetUnixSocket::CatchHost(void* buffer, size_t nbytes, int flags) |
|
|
|
{ |
|
|
|
socklen_t addr_len = sizeof(socket_address_t); |
|
|
|
jack_log("JackNetUnixSocket::CatchHost"); |
|
|
|
socklen_t addr_len = sizeof(fSendAddr); |
|
|
|
#if defined(__sun__) || defined(sun) |
|
|
|
if (WaitRead() < 0) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
#endif |
|
|
|
int res; |
|
|
|
if ((res = recvfrom(fSockfd, buffer, nbytes, flags, reinterpret_cast<socket_address_t*>(&fSendAddr), &addr_len)) < 0) { |
|
|
|
if ((res = recvfrom(fSockfd, buffer, nbytes, flags, reinterpret_cast<struct sockaddr *>(&fSendAddr), &addr_len)) < 0) { |
|
|
|
jack_log("CatchHost fd = %ld err = %s", fSockfd, strerror(errno)); |
|
|
|
} |
|
|
|
return res; |
|
|
|