Branch netjack_squashed_interface_selection_plus_ipv6 Original PR: https://github.com/jackaudio/jack2/pull/27 by rufferson, first comment Mar 3, 2013 Commits on Mar 12, 2017 Add interface selection for NetJack components @rufferson rufferson committed on Mar 3, 2013 * Multicast interface selection to UnixSocket - added methods Bind and JoinMCastGroup with <char *if_name> argument * Interface handling to NetInterface class tree - new field and its initialization/usage in Slave branch. * Multicast interface selection to NetSlaves (Adapter/Driver) and NetMaster, where master can now listen all interfaces. Commits on Mar 15, 2017 Add IPv6 support to UnixSocket and NetInterfaces @rufferson rufferson committed on Feb 21, 2013 * Cleaned up direct references to sockaddr_in * Changed fRecvAddr & fSendAddr to sockaddr_storage * Added field fFamily indicating current probed AF. * Added protected method ProbeAF accepting IP to probe and addr to fill in. As well as final call to test which should be either connect or bind. * Reworked protocol specific methods to act on fFamily * IsLocal now checks assigned interface addresses * Introduced internal state tracker to avoid double bind * Workaround for GLIBC bug returning wrong order of AFs http://sourceware.org/bugzilla/show_bug.cgi?id=14967 * Corrected interface selection for Slaves - only one interface could be used to multicast the packet * Retab changes This branch has conflicts that must be resolved to resolve conflicts before continuing. Conflicting files common/JackNetDriver.h posix/JackNetUnixSocket.cpp wget https://github.com/jackaudio/jack2/pull/27.diff $ patch -p 1 < 27.diff patching file common/JackNetAdapter.cpp patching file common/JackNetDriver.cpp patching file common/JackNetDriver.h Hunk #1 FAILED at 79. 1 out of 1 hunk FAILED -- saving rejects to file common/JackNetDriver.h.rej patching file common/JackNetInterface.cpp patching file common/JackNetInterface.h patching file common/JackNetManager.cpp patching file common/JackNetManager.h patching file common/JackNetTool.h patching file posix/JackNetUnixSocket.cpp Hunk #4 FAILED at 229. Hunk #5 succeeded at 281 (offset -8 lines). Hunk #6 succeeded at 303 (offset -8 lines). Hunk #7 succeeded at 325 (offset -8 lines). Hunk #8 succeeded at 363 (offset -8 lines). Hunk #9 succeeded at 486 (offset -8 lines). Hunk #10 succeeded at 508 (offset -8 lines). Hunk #11 succeeded at 537 (offset -8 lines). Hunk #12 succeeded at 586 (offset -8 lines). Hunk #13 succeeded at 607 (offset -8 lines). Hunk #14 succeeded at 615 (offset -8 lines). Hunk #15 succeeded at 644 (offset -8 lines). Hunk #16 succeeded at 673 (offset -8 lines). 1 out of 16 hunks FAILED -- saving rejects to file posix/JackNetUnixSocket.cpp.rej find|grep rej ./posix/JackNetUnixSocket.cpp.rej ./common/JackNetDriver.h.rej --- posix/JackNetUnixSocket.cpp +++ posix/JackNetUnixSocket.cpp @@ -229,58 +311,117 @@ { 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_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() --- common/JackNetDriver.h +++ common/JackNetDriver.h @@ -79,7 +79,7 @@ public: JackNetDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, - const char* ip, int port, int mtu, int midi_input_ports, int midi_output_ports, + const char* ip, int port, const char* mcif, int mtu, int midi_input_ports, int midi_output_ports, char* net_name, uint transport_sync, int network_latency, int celt_encoding, int opus_encoding, bool auto_save); virtual ~JackNetDriver();netjack_squashed_interface_selection_plus_ipv6
@@ -35,7 +35,7 @@ namespace Jack | |||||
because we don't have full parametering right now, parameters will be parsed from the param list, | because we don't have full parametering right now, parameters will be parsed from the param list, | ||||
and then JackNetSlaveInterface will be filled with proper values. | and then JackNetSlaveInterface will be filled with proper values. | ||||
*/ | */ | ||||
char multicast_ip[32]; | |||||
char multicast_ip[32],multicast_if[32]; | |||||
uint udp_port; | uint udp_port; | ||||
GetHostName(fParams.fName, JACK_CLIENT_NAME_SIZE); | GetHostName(fParams.fName, JACK_CLIENT_NAME_SIZE); | ||||
fSocket.GetName(fParams.fSlaveNetName); | fSocket.GetName(fParams.fSlaveNetName); | ||||
@@ -58,11 +58,10 @@ namespace Jack | |||||
udp_port = (default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT; | udp_port = (default_udp_port) ? atoi(default_udp_port) : DEFAULT_PORT; | ||||
const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); | const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); | ||||
if (default_multicast_ip) { | |||||
strcpy(multicast_ip, default_multicast_ip); | |||||
} else { | |||||
strcpy(multicast_ip, DEFAULT_MULTICAST_IP); | |||||
} | |||||
strcpy(multicast_ip, (default_multicast_ip) ? default_multicast_ip : DEFAULT_MULTICAST_IP); | |||||
const char* default_multicast_if = getenv("JACK_NETJACK_INTERFACE"); | |||||
strcpy(multicast_if, (default_multicast_if) ? default_multicast_if : DEFAULT_MULTICAST_IF); | |||||
//options parsing | //options parsing | ||||
const JSList* node; | const JSList* node; | ||||
@@ -76,6 +75,10 @@ namespace Jack | |||||
assert(strlen(param->value.str) < 32); | assert(strlen(param->value.str) < 32); | ||||
strcpy(multicast_ip, param->value.str); | strcpy(multicast_ip, param->value.str); | ||||
break; | break; | ||||
case 'f' : | |||||
assert(strlen(param->value.str) < 32); | |||||
strcpy(multicast_if, param->value.str); | |||||
break; | |||||
case 'p' : | case 'p' : | ||||
udp_port = param->value.ui; | udp_port = param->value.ui; | ||||
break; | break; | ||||
@@ -128,9 +131,10 @@ namespace Jack | |||||
} | } | ||||
strcpy(fMulticastIP, multicast_ip); | strcpy(fMulticastIP, multicast_ip); | ||||
if(strcmp(multicast_if,DEFAULT_MULTICAST_IF)) | |||||
strcpy(fMulticastIF, multicast_if); | |||||
// Set the socket parameters | // Set the socket parameters | ||||
fSocket.SetPort(udp_port); | |||||
fSocket.SetAddress(fMulticastIP, udp_port); | fSocket.SetAddress(fMulticastIP, udp_port); | ||||
// If not set, takes default | // If not set, takes default | ||||
@@ -424,6 +428,9 @@ extern "C" | |||||
strcpy(value.str, DEFAULT_MULTICAST_IP); | strcpy(value.str, DEFAULT_MULTICAST_IP); | ||||
jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address, or explicit IP of the master", NULL); | jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address, or explicit IP of the master", NULL); | ||||
strcpy(value.str, DEFAULT_MULTICAST_IF); | |||||
jack_driver_descriptor_add_parameter(desc, &filler, "multicast-if", 'f', JackDriverParamString, &value, NULL, "Multicast interface name or any", "Multicast interface to send probes from (any - kernel chosen (default), if_name - from specified interface)"); | |||||
value.i = DEFAULT_PORT; | value.i = DEFAULT_PORT; | ||||
jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); | jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); | ||||
@@ -28,7 +28,7 @@ using namespace std; | |||||
namespace Jack | namespace Jack | ||||
{ | { | ||||
JackNetDriver::JackNetDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, | JackNetDriver::JackNetDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, | ||||
const char* ip, int udp_port, int mtu, int midi_input_ports, int midi_output_ports, | |||||
const char* ip, int udp_port, const char* mcif, int mtu, int midi_input_ports, int midi_output_ports, | |||||
char* net_name, uint transport_sync, int network_latency, | char* net_name, uint transport_sync, int network_latency, | ||||
int celt_encoding, int opus_encoding, bool auto_save) | int celt_encoding, int opus_encoding, bool auto_save) | ||||
: JackWaiterDriver(name, alias, engine, table), JackNetSlaveInterface(ip, udp_port) | : JackWaiterDriver(name, alias, engine, table), JackNetSlaveInterface(ip, udp_port) | ||||
@@ -39,6 +39,8 @@ namespace Jack | |||||
if (strcmp(net_name, "") == 0) { | if (strcmp(net_name, "") == 0) { | ||||
GetHostName(net_name, JACK_CLIENT_NAME_SIZE); | GetHostName(net_name, JACK_CLIENT_NAME_SIZE); | ||||
} | } | ||||
if(strcmp(mcif,DEFAULT_MULTICAST_IF)) | |||||
strcpy(fMulticastIF,mcif); | |||||
fParams.fMtu = mtu; | fParams.fMtu = mtu; | ||||
@@ -676,6 +678,9 @@ namespace Jack | |||||
strcpy(value.str, DEFAULT_MULTICAST_IP); | strcpy(value.str, DEFAULT_MULTICAST_IP); | ||||
jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address, or explicit IP of the master", NULL); | jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address, or explicit IP of the master", NULL); | ||||
strcpy(value.str, DEFAULT_MULTICAST_IF); | |||||
jack_driver_descriptor_add_parameter(desc, &filler, "multicast-if", 'f', JackDriverParamString, &value, NULL, "Multicast interface name or any", "Multicast interface to send probes from (any - kernel chosen (default), if_name - send from specified interface)"); | |||||
value.i = DEFAULT_PORT; | value.i = DEFAULT_PORT; | ||||
jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); | jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); | ||||
@@ -719,8 +724,9 @@ Deactivated for now.. | |||||
SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) | SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) | ||||
{ | { | ||||
char multicast_if[32]; | |||||
char multicast_ip[32]; | char multicast_ip[32]; | ||||
char net_name[JACK_CLIENT_NAME_SIZE+1] = {0}; | |||||
char net_name[JACK_CLIENT_NAME_SIZE + 1] = {0}; | |||||
int udp_port; | int udp_port; | ||||
int mtu = DEFAULT_MTU; | int mtu = DEFAULT_MTU; | ||||
// Desactivated for now... | // Desactivated for now... | ||||
@@ -747,6 +753,9 @@ Deactivated for now.. | |||||
const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); | const char* default_multicast_ip = getenv("JACK_NETJACK_MULTICAST"); | ||||
strcpy(multicast_ip, (default_multicast_ip) ? default_multicast_ip : DEFAULT_MULTICAST_IP); | strcpy(multicast_ip, (default_multicast_ip) ? default_multicast_ip : DEFAULT_MULTICAST_IP); | ||||
const char* default_multicast_if = getenv("JACK_NETJACK_INTERFACE"); | |||||
strcpy(multicast_if, (default_multicast_if) ? default_multicast_if : DEFAULT_MULTICAST_IF); | |||||
for (node = params; node; node = jack_slist_next(node)) { | for (node = params; node; node = jack_slist_next(node)) { | ||||
param = (const jack_driver_param_t*) node->data; | param = (const jack_driver_param_t*) node->data; | ||||
switch (param->character) | switch (param->character) | ||||
@@ -755,6 +764,10 @@ Deactivated for now.. | |||||
assert(strlen(param->value.str) < 32); | assert(strlen(param->value.str) < 32); | ||||
strcpy(multicast_ip, param->value.str); | strcpy(multicast_ip, param->value.str); | ||||
break; | break; | ||||
case 'f' : | |||||
assert(strlen(param->value.str) < 32); | |||||
strcpy(multicast_if, param->value.str); | |||||
break; | |||||
case 'p': | case 'p': | ||||
udp_port = param->value.ui; | udp_port = param->value.ui; | ||||
break; | break; | ||||
@@ -808,7 +821,7 @@ Deactivated for now.. | |||||
try { | try { | ||||
Jack::JackDriverClientInterface* driver = new Jack::JackWaitThreadedDriver( | Jack::JackDriverClientInterface* driver = new Jack::JackWaitThreadedDriver( | ||||
new Jack::JackNetDriver("system", "net_pcm", engine, table, multicast_ip, udp_port, mtu, | |||||
new Jack::JackNetDriver("system", "net_pcm", engine, table, multicast_ip, udp_port, multicast_if, mtu, | |||||
midi_input_ports, midi_output_ports, | midi_input_ports, midi_output_ports, | ||||
net_name, transport_sync, | net_name, transport_sync, | ||||
network_latency, celt_encoding, opus_encoding, auto_save)); | network_latency, celt_encoding, opus_encoding, auto_save)); | ||||
@@ -79,7 +79,7 @@ namespace Jack | |||||
public: | public: | ||||
JackNetDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, | JackNetDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, | ||||
const char* ip, int port, int mtu, int midi_input_ports, int midi_output_ports, | |||||
const char* ip, int port, const char* mcif, int mtu, int midi_input_ports, int midi_output_ports, | |||||
char* net_name, uint transport_sync, int network_latency, int celt_encoding, | char* net_name, uint transport_sync, int network_latency, int celt_encoding, | ||||
int opus_encoding, bool auto_save); | int opus_encoding, bool auto_save); | ||||
virtual ~JackNetDriver(); | virtual ~JackNetDriver(); | ||||
@@ -57,6 +57,7 @@ namespace Jack | |||||
fSetTimeOut = false; | fSetTimeOut = false; | ||||
fTxBuffer = NULL; | fTxBuffer = NULL; | ||||
fRxBuffer = NULL; | fRxBuffer = NULL; | ||||
fMulticastIF[0]=0; | |||||
fNetAudioCaptureBuffer = NULL; | fNetAudioCaptureBuffer = NULL; | ||||
fNetAudioPlaybackBuffer = NULL; | fNetAudioPlaybackBuffer = NULL; | ||||
fNetMidiCaptureBuffer = NULL; | fNetMidiCaptureBuffer = NULL; | ||||
@@ -700,6 +701,12 @@ namespace Jack | |||||
if (fSocket.IsLocal(fMulticastIP)) { | if (fSocket.IsLocal(fMulticastIP)) { | ||||
jack_info("Local IP is used..."); | jack_info("Local IP is used..."); | ||||
} else if (fMulticastIF[0]) { | |||||
// bind the socket & interface | |||||
if (fSocket.Bind(fMulticastIF) == SOCKET_ERROR) { | |||||
jack_error("Can't bind the socket : %s", StrError(NET_ERROR_CODE)); | |||||
return NET_SOCKET_ERROR; | |||||
} | |||||
} else { | } else { | ||||
// bind the socket | // bind the socket | ||||
if (fSocket.Bind() == SOCKET_ERROR) { | if (fSocket.Bind() == SOCKET_ERROR) { | ||||
@@ -719,8 +726,7 @@ namespace Jack | |||||
} | } | ||||
// send 'AVAILABLE' until 'SLAVE_SETUP' received | // send 'AVAILABLE' until 'SLAVE_SETUP' received | ||||
jack_info("Waiting for a master..."); | |||||
jack_info("Waiting for a master on %s...",(fMulticastIF[0])?fMulticastIF:"default"); | |||||
do { | do { | ||||
// send 'available' | // send 'available' | ||||
session_params_t net_params; | session_params_t net_params; | ||||
@@ -26,7 +26,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
namespace Jack | namespace Jack | ||||
{ | { | ||||
// Should be registered at IANA perhaps | |||||
#define DEFAULT_MULTICAST_IP6 "FF05::139a" | |||||
#define DEFAULT_MULTICAST_IP "225.3.19.154" | #define DEFAULT_MULTICAST_IP "225.3.19.154" | ||||
#define DEFAULT_MULTICAST_IF "any" | |||||
#define DEFAULT_PORT 19000 | #define DEFAULT_PORT 19000 | ||||
#define DEFAULT_MTU 1500 | #define DEFAULT_MTU 1500 | ||||
#define MAX_MTU 9000 | #define MAX_MTU 9000 | ||||
@@ -60,6 +63,7 @@ namespace Jack | |||||
session_params_t fParams; | session_params_t fParams; | ||||
JackNetSocket fSocket; | JackNetSocket fSocket; | ||||
char fMulticastIP[32]; | char fMulticastIP[32]; | ||||
char fMulticastIF[32]; | |||||
// headers | // headers | ||||
packet_header_t fTxHeader; | packet_header_t fTxHeader; | ||||
@@ -505,7 +505,7 @@ namespace Jack | |||||
if (out) { | if (out) { | ||||
memset(out, 0, sizeof(float) * fParams.fPeriodSize); | memset(out, 0, sizeof(float) * fParams.fPeriodSize); | ||||
} | } | ||||
fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, out))); | |||||
fNetAudioPlaybackBuffer->SetBuffer(audio_port_index, out); | |||||
#endif | #endif | ||||
} | } | ||||
@@ -660,6 +660,13 @@ namespace Jack | |||||
strcpy(fMulticastIP, DEFAULT_MULTICAST_IP); | strcpy(fMulticastIP, DEFAULT_MULTICAST_IP); | ||||
} | } | ||||
const char* default_multicast_if = getenv("JACK_NETJACK_INTERFACE"); | |||||
if (default_multicast_if) { | |||||
strcpy(fMulticastIF, default_multicast_if); | |||||
} else { | |||||
strcpy(fMulticastIF, DEFAULT_MULTICAST_IF); | |||||
} | |||||
for (node = params; node; node = jack_slist_next(node)) { | for (node = params; node; node = jack_slist_next(node)) { | ||||
param = (const jack_driver_param_t*) node->data; | param = (const jack_driver_param_t*) node->data; | ||||
@@ -672,6 +679,14 @@ namespace Jack | |||||
} | } | ||||
break; | break; | ||||
case 'f' : | |||||
if (strlen(param->value.str) < 32) { | |||||
strcpy(fMulticastIF, param->value.str); | |||||
} else { | |||||
jack_error("Can't use multicast interface %s, using default %s", param->value.ui, DEFAULT_MULTICAST_IF); | |||||
} | |||||
break; | |||||
case 'p': | case 'p': | ||||
fSocket.SetPort(param->value.ui); | fSocket.SetPort(param->value.ui); | ||||
break; | break; | ||||
@@ -761,7 +776,7 @@ namespace Jack | |||||
{ | { | ||||
JackNetMasterManager* master_manager = static_cast<JackNetMasterManager*>(arg); | JackNetMasterManager* master_manager = static_cast<JackNetMasterManager*>(arg); | ||||
jack_info("Starting Jack NetManager"); | jack_info("Starting Jack NetManager"); | ||||
jack_info("Listening on '%s:%d'", master_manager->fMulticastIP, master_manager->fSocket.GetPort()); | |||||
jack_info("Listening on '%s:%d%%%s'", master_manager->fMulticastIP, master_manager->fSocket.GetPort(),master_manager->fMulticastIF); | |||||
master_manager->Run(); | master_manager->Run(); | ||||
return NULL; | return NULL; | ||||
} | } | ||||
@@ -783,8 +798,8 @@ namespace Jack | |||||
return; | return; | ||||
} | } | ||||
//socket | |||||
if (fSocket.NewSocket() == SOCKET_ERROR) { | |||||
//socket: we need to have socket probed first if we want to use multicast | |||||
if (fSocket.NewSocket(fMulticastIP) == SOCKET_ERROR) { | |||||
jack_error("Can't create NetManager input socket : %s", StrError(NET_ERROR_CODE)); | jack_error("Can't create NetManager input socket : %s", StrError(NET_ERROR_CODE)); | ||||
return; | return; | ||||
} | } | ||||
@@ -797,7 +812,7 @@ namespace Jack | |||||
} | } | ||||
//join multicast group | //join multicast group | ||||
if (fSocket.JoinMCastGroup(fMulticastIP) == SOCKET_ERROR) { | |||||
if (fSocket.JoinMCastGroup(fMulticastIP,fMulticastIF) == SOCKET_ERROR) { | |||||
jack_error("Can't join multicast group : %s", StrError(NET_ERROR_CODE)); | jack_error("Can't join multicast group : %s", StrError(NET_ERROR_CODE)); | ||||
} | } | ||||
@@ -843,6 +858,7 @@ namespace Jack | |||||
} | } | ||||
break; | break; | ||||
default: | default: | ||||
jack_log("JackNetMasterManager::Run: read: %d; type: %d; peer: %s",rx_bytes,host_params.fPacketID,host_params.fName); | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
@@ -949,6 +965,9 @@ extern "C" | |||||
strcpy(value.str, DEFAULT_MULTICAST_IP); | strcpy(value.str, DEFAULT_MULTICAST_IP); | ||||
jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address", NULL); | jack_driver_descriptor_add_parameter(desc, &filler, "multicast-ip", 'a', JackDriverParamString, &value, NULL, "Multicast address", NULL); | ||||
strcpy(value.str, DEFAULT_MULTICAST_IF); | |||||
jack_driver_descriptor_add_parameter(desc, &filler, "multicast-if", 'f', JackDriverParamString, &value, NULL, "Multicast interface", "Multicast interface to bind to. ('all' - all ip ifs; 'any' (default) kernel chosen; ifname i.e. eth0)"); | |||||
value.i = DEFAULT_PORT; | value.i = DEFAULT_PORT; | ||||
jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); | jack_driver_descriptor_add_parameter(desc, &filler, "udp-net-port", 'p', JackDriverParamInt, &value, NULL, "UDP port", NULL); | ||||
@@ -112,6 +112,7 @@ namespace Jack | |||||
jack_client_t* fClient; | jack_client_t* fClient; | ||||
const char* fName; | const char* fName; | ||||
char fMulticastIP[32]; | char fMulticastIP[32]; | ||||
char fMulticastIF[32]; | |||||
JackNetSocket fSocket; | JackNetSocket fSocket; | ||||
jack_native_thread_t fThread; | jack_native_thread_t fThread; | ||||
master_list_t fMasterList; | master_list_t fMasterList; | ||||
@@ -56,8 +56,6 @@ namespace Jack | |||||
typedef struct _session_params session_params_t; | typedef struct _session_params session_params_t; | ||||
typedef struct _packet_header packet_header_t; | typedef struct _packet_header packet_header_t; | ||||
typedef struct _net_transport_data net_transport_data_t; | typedef struct _net_transport_data net_transport_data_t; | ||||
typedef struct sockaddr socket_address_t; | |||||
typedef struct in_addr address_t; | |||||
typedef jack_default_audio_sample_t sample_t; | typedef jack_default_audio_sample_t sample_t; | ||||
enum JackNetEncoder { | enum JackNetEncoder { | ||||
@@ -20,11 +20,27 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
#include "JackNetUnixSocket.h" | #include "JackNetUnixSocket.h" | ||||
#include "JackError.h" | #include "JackError.h" | ||||
#include <arpa/inet.h> | |||||
#include <sys/socket.h> | |||||
#include <ifaddrs.h> | |||||
#include <net/if.h> | |||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
using namespace std; | 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 | namespace Jack | ||||
{ | { | ||||
//utility ********************************************************************************************************* | //utility ********************************************************************************************************* | ||||
@@ -40,40 +56,23 @@ namespace Jack | |||||
//construct/destruct*********************************************************************************************** | //construct/destruct*********************************************************************************************** | ||||
JackNetUnixSocket::JackNetUnixSocket() | 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) | JackNetUnixSocket::JackNetUnixSocket(const char* ip, int port) | ||||
: fFamily(AF_UNSPEC), fSockfd(0), fState(JNS_UNSPEC), fPort(0), fTimeOut(0) | |||||
{ | { | ||||
fSockfd = 0; | |||||
Reset(); | |||||
fPort = port; | 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) | JackNetUnixSocket::JackNetUnixSocket(const JackNetUnixSocket& socket) | ||||
{ | { | ||||
fSockfd = 0; | |||||
fTimeOut = 0; | |||||
fPort = socket.fPort; | |||||
fSendAddr = socket.fSendAddr; | |||||
fRecvAddr = socket.fRecvAddr; | |||||
Clone(socket); | |||||
} | } | ||||
JackNetUnixSocket::~JackNetUnixSocket() | JackNetUnixSocket::~JackNetUnixSocket() | ||||
@@ -83,23 +82,106 @@ namespace Jack | |||||
JackNetUnixSocket& JackNetUnixSocket::operator=(const JackNetUnixSocket& socket) | 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; | return *this; | ||||
} | } | ||||
//socket*********************************************************************************************************** | //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() | 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) { | if (fSockfd) { | ||||
Close(); | Close(); | ||||
Reset(); | Reset(); | ||||
} | } | ||||
fSockfd = socket(AF_INET, SOCK_DGRAM, 0); | |||||
fSockfd = socket(fFamily, SOCK_DGRAM, 0); | |||||
/* Enable address reuse */ | /* Enable address reuse */ | ||||
int res, on = 1; | int res, on = 1; | ||||
@@ -139,58 +221,117 @@ namespace Jack | |||||
{ | { | ||||
if (strcmp(ip, "127.0.0.1") == 0) { | if (strcmp(ip, "127.0.0.1") == 0) { | ||||
return true; | 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() | 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 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) | 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() | 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 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() | void JackNetUnixSocket::Close() | ||||
@@ -199,18 +340,17 @@ namespace Jack | |||||
close(fSockfd); | close(fSockfd); | ||||
} | } | ||||
fSockfd = 0; | fSockfd = 0; | ||||
fState = JNS_UNSPEC; | |||||
} | } | ||||
void JackNetUnixSocket::Reset() | 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() | bool JackNetUnixSocket::IsSocket() | ||||
@@ -222,8 +362,18 @@ namespace Jack | |||||
void JackNetUnixSocket::SetPort(int port) | void JackNetUnixSocket::SetPort(int port) | ||||
{ | { | ||||
fPort = 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() | int JackNetUnixSocket::GetPort() | ||||
@@ -234,22 +384,34 @@ namespace Jack | |||||
//address*********************************************************************************************************** | //address*********************************************************************************************************** | ||||
int JackNetUnixSocket::SetAddress(const char* ip, int port) | 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() | 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() | char* JackNetUnixSocket::GetRecvIP() | ||||
{ | { | ||||
return inet_ntoa(fRecvAddr.sin_addr); | |||||
return (char*)inet_ntop(fFamily, _ss_addr_p(fRecvAddr),f_addr_buff,INET6_ADDRSTRLEN); | |||||
} | } | ||||
//utility************************************************************************************************************ | //utility************************************************************************************************************ | ||||
@@ -260,10 +422,111 @@ namespace Jack | |||||
int JackNetUnixSocket::JoinMCastGroup(const char* ip) | 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************************************************************************************************************ | //options************************************************************************************************************ | ||||
@@ -282,19 +545,19 @@ namespace Jack | |||||
#if defined(__sun__) || defined(sun) | #if defined(__sun__) || defined(sun) | ||||
int JackNetUnixSocket::SetTimeOut(int us) | int JackNetUnixSocket::SetTimeOut(int us) | ||||
{ | { | ||||
int flags; | |||||
int flags; | |||||
fTimeOut = us; | fTimeOut = us; | ||||
if ((flags = fcntl(fSockfd, F_GETFL, 0)) < 0) { | 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; | return 0; | ||||
} | } | ||||
@@ -304,25 +567,25 @@ namespace Jack | |||||
if (fTimeOut > 0) { | if (fTimeOut > 0) { | ||||
struct timeval tv; | struct timeval tv; | ||||
fd_set fdset; | |||||
ssize_t res; | |||||
fd_set fdset; | |||||
ssize_t res; | |||||
tv.tv_sec = fTimeOut / 1000000; | 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 { | do { | ||||
res = select(fSockfd + 1, &fdset, NULL, NULL, &tv); | res = select(fSockfd + 1, &fdset, NULL, NULL, &tv); | ||||
} while (res < 0 && errno == EINTR); | } while (res < 0 && errno == EINTR); | ||||
if (res < 0) { | |||||
return res; | |||||
if (res < 0) { | |||||
return res; | |||||
} else if (res == 0) { | } else if (res == 0) { | ||||
errno = ETIMEDOUT; | errno = ETIMEDOUT; | ||||
return -1; | |||||
} | |||||
return -1; | |||||
} | |||||
} | } | ||||
return 0; | return 0; | ||||
@@ -333,25 +596,25 @@ namespace Jack | |||||
if (fTimeOut > 0) { | if (fTimeOut > 0) { | ||||
struct timeval tv; | struct timeval tv; | ||||
fd_set fdset; | |||||
ssize_t res; | |||||
fd_set fdset; | |||||
ssize_t res; | |||||
tv.tv_sec = fTimeOut / 1000000; | 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 { | do { | ||||
res = select(fSockfd + 1, NULL, &fdset, NULL, &tv); | res = select(fSockfd + 1, NULL, &fdset, NULL, &tv); | ||||
} while (res < 0 && errno == EINTR); | } while (res < 0 && errno == EINTR); | ||||
if (res < 0) { | |||||
return res; | |||||
if (res < 0) { | |||||
return res; | |||||
} else if (res == 0) { | } else if (res == 0) { | ||||
errno = ETIMEDOUT; | errno = ETIMEDOUT; | ||||
return -1; | |||||
} | |||||
return -1; | |||||
} | |||||
} | } | ||||
return 0; | return 0; | ||||
@@ -382,7 +645,16 @@ namespace Jack | |||||
int JackNetUnixSocket::SetLocalLoop() | int JackNetUnixSocket::SetLocalLoop() | ||||
{ | { | ||||
char disable = 0; | 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************************************************************************************************** | //network operations************************************************************************************************** | ||||
@@ -394,7 +666,7 @@ namespace Jack | |||||
} | } | ||||
#endif | #endif | ||||
int res; | 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)); | jack_error("SendTo fd = %ld err = %s", fSockfd, strerror(errno)); | ||||
} | } | ||||
return res; | return res; | ||||
@@ -402,11 +674,11 @@ namespace Jack | |||||
int JackNetUnixSocket::SendTo(const void* buffer, size_t nbytes, int flags, const char* ip) | 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) { | if (addr_conv < 1) { | ||||
return addr_conv; | return addr_conv; | ||||
} | } | ||||
fSendAddr.sin_port = htons(fPort); | |||||
//fSendAddr.sin_port = htons(fPort); | |||||
#if defined(__sun__) || defined(sun) | #if defined(__sun__) || defined(sun) | ||||
if (WaitWrite() < 0) { | if (WaitWrite() < 0) { | ||||
return -1; | return -1; | ||||
@@ -431,14 +703,14 @@ namespace Jack | |||||
int JackNetUnixSocket::RecvFrom(void* buffer, size_t nbytes, int flags) | 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 defined(__sun__) || defined(sun) | ||||
if (WaitRead() < 0) { | if (WaitRead() < 0) { | ||||
return -1; | return -1; | ||||
} | } | ||||
#endif | #endif | ||||
int res; | 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)); | jack_error("RecvFrom fd = %ld err = %s", fSockfd, strerror(errno)); | ||||
} | } | ||||
return res; | return res; | ||||
@@ -460,14 +732,15 @@ namespace Jack | |||||
int JackNetUnixSocket::CatchHost(void* buffer, size_t nbytes, int flags) | 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 defined(__sun__) || defined(sun) | ||||
if (WaitRead() < 0) { | if (WaitRead() < 0) { | ||||
return -1; | return -1; | ||||
} | } | ||||
#endif | #endif | ||||
int res; | 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)); | jack_log("CatchHost fd = %ld err = %s", fSockfd, strerror(errno)); | ||||
} | } | ||||
return res; | return res; | ||||
@@ -33,20 +33,27 @@ namespace Jack | |||||
#define SOCKET_ERROR -1 | #define SOCKET_ERROR -1 | ||||
#define StrError strerror | #define StrError strerror | ||||
typedef struct sockaddr socket_address_t; | |||||
typedef struct in_addr address_t; | |||||
//JackNetUnixSocket******************************************** | //JackNetUnixSocket******************************************** | ||||
class SERVER_EXPORT JackNetUnixSocket | class SERVER_EXPORT JackNetUnixSocket | ||||
{ | { | ||||
private: | |||||
protected: | |||||
int fFamily; | |||||
int fSockfd; | int fSockfd; | ||||
int fState; | |||||
int fPort; | int fPort; | ||||
int fTimeOut; | int fTimeOut; | ||||
struct sockaddr_storage fSendAddr; | |||||
struct sockaddr_storage fRecvAddr; | |||||
void Clone(const JackNetUnixSocket& socket); | |||||
int ProbeAF(const char* ip, struct sockaddr_storage *addr, int (*call)(int, const struct sockaddr*, socklen_t)); | |||||
int BindMCastIface(const char *if_name, const int option, struct in_addr *addr); | |||||
int BindMCast6Iface(const char *if_name, struct in6_addr *addr); | |||||
private: | |||||
char f_addr_buff[INET6_ADDRSTRLEN]; | |||||
struct sockaddr_in fSendAddr; | |||||
struct sockaddr_in fRecvAddr; | |||||
#if defined(__sun__) || defined(sun) | #if defined(__sun__) || defined(sun) | ||||
int WaitRead(); | int WaitRead(); | ||||
int WaitWrite(); | int WaitWrite(); | ||||
@@ -62,8 +69,10 @@ namespace Jack | |||||
JackNetUnixSocket& operator=(const JackNetUnixSocket& socket); | JackNetUnixSocket& operator=(const JackNetUnixSocket& socket); | ||||
//socket management | //socket management | ||||
int NewSocket(const char *ip); | |||||
int NewSocket(); | int NewSocket(); | ||||
int Bind(); | int Bind(); | ||||
int Bind(const char *if_name); | |||||
int BindWith(const char* ip); | int BindWith(const char* ip); | ||||
int BindWith(int port); | int BindWith(int port); | ||||
int Connect(); | int Connect(); | ||||
@@ -79,11 +88,14 @@ namespace Jack | |||||
//address management | //address management | ||||
int SetAddress(const char* ip, int port); | int SetAddress(const char* ip, int port); | ||||
char* GetSendIP(); | char* GetSendIP(); | ||||
int SetSendIP(const char *ip); | |||||
char* GetRecvIP(); | char* GetRecvIP(); | ||||
int SetRecvIP(const char *ip); | |||||
//utility | //utility | ||||
int GetName(char* name); | int GetName(char* name); | ||||
int JoinMCastGroup(const char* mcast_ip); | int JoinMCastGroup(const char* mcast_ip); | ||||
int JoinMCastGroup(const char* mcast_ip, const char* if_name); | |||||
//options management | //options management | ||||
int SetOption(int level, int optname, const void* optval, socklen_t optlen); | int SetOption(int level, int optname, const void* optval, socklen_t optlen); | ||||