diff --git a/coreapi/TunnelManager.cc b/coreapi/TunnelManager.cc index eeb24cb7f..edd9af703 100644 --- a/coreapi/TunnelManager.cc +++ b/coreapi/TunnelManager.cc @@ -41,12 +41,39 @@ void TunnelManager::addServer(const char *ip, int port) { ms_warning("Adding tunnel server with empty ip, it will not work!"); return; } + if (mUseDualClient) { + ms_warning("TunnelManager is configured in dual mode, use addServerPair instead"); + return; + } + mServerAddrs.push_back(ServerAddr(ip,port)); - if (mTunnelClient) mTunnelClient->addServer(ip,port); + if (mTunnelClient && !mUseDualClient) { + ((TunnelClient*)mTunnelClient)->addServer(ip,port); + } +} + +void TunnelManager::addServerPair(const char *ip1, int port1, const char *ip2, int port2) { + if (ip1 == NULL || ip2 == NULL) { + ms_warning("Adding tunnel server with empty ip, it will not work!"); + return; + } + if (!mUseDualClient) { + ms_warning("TunnelManager is configured in single mode, use addServer instead"); + return; + } + + pair addr; + addr.first = ServerAddr(ip1, port1); + addr.second = ServerAddr(ip2, port2); + mDualServerAddrs.push_back(addr); + if (mTunnelClient && mUseDualClient) { + ((DualTunnelClient*)mTunnelClient)->addServerPair(ip1, port1, ip2, port2); + } } void TunnelManager::cleanServers() { mServerAddrs.clear(); + mDualServerAddrs.clear(); if (mLongRunningTaskId > 0) { sal_end_background_task(mLongRunningTaskId); mLongRunningTaskId = 0; @@ -61,15 +88,27 @@ void TunnelManager::cleanServers() { if (mTunnelClient) mTunnelClient->cleanServers(); } +void TunnelManager::enableDualMode(bool enable) { + mUseDualClient = enable; +} + +bool TunnelManager::isDualModeEnabled() { + return mUseDualClient; +} + void TunnelManager::reconnect(){ if (mTunnelClient) mTunnelClient->reconnect(); } static void sCloseRtpTransport(RtpTransport *t){ - TunnelSocket *s=(TunnelSocket*)t->data; - TunnelManager *manager=(TunnelManager*)s->getUserPointer(); - manager->closeRtpTransport(t, s); + DualSocket *ds = (DualSocket *)t->data; + TunnelSocket *sendSocket = ds->sendSocket; + TunnelSocket *recvSocket = ds->recvSocket; + TunnelManager *manager=(TunnelManager*)sendSocket->getUserPointer(); + manager->closeRtpTransport(t, sendSocket); + manager->closeRtpTransport(t, recvSocket); + ms_free(ds); } void TunnelManager::closeRtpTransport(RtpTransport *t, TunnelSocket *s){ mTunnelClient->closeSocket(s); @@ -84,15 +123,26 @@ void sDestroyRtpTransport(RtpTransport *t){ } RtpTransport *TunnelManager::createRtpTransport(int port){ - TunnelSocket *socket=mTunnelClient->createSocket(port); - socket->setUserPointer(this); - RtpTransport *t=ms_new0(RtpTransport,1); + DualSocket *dualSocket = ms_new0(DualSocket, 1); + if (!mUseDualClient) { + TunnelSocket *socket = ((TunnelClient *)mTunnelClient)->createSocket(port); + socket->setUserPointer(this); + dualSocket->sendSocket = socket; + dualSocket->recvSocket = socket; + } else { + dualSocket->sendSocket = ((DualTunnelClient *)mTunnelClient)->createSocket(TunnelSendOnly, port); + dualSocket->sendSocket->setUserPointer(this); + dualSocket->recvSocket = ((DualTunnelClient *)mTunnelClient)->createSocket(TunnelRecvOnly, port); + dualSocket->recvSocket->setUserPointer(this); + } + + RtpTransport *t = ms_new0(RtpTransport,1); t->t_getsocket=NULL; t->t_recvfrom=customRecvfrom; t->t_sendto=customSendto; t->t_close=sCloseRtpTransport; t->t_destroy=sDestroyRtpTransport; - t->data=socket; + t->data=dualSocket; ms_message("Creating tunnel RTP transport for local virtual port %i", port); return t; } @@ -102,7 +152,11 @@ void TunnelManager::startClient() { if (!mTunnelClient){ mTunnelClient = new TunnelClient(TRUE); sal_set_tunnel(mCore->sal, mTunnelClient); - mTunnelClient->setCallback(tunnelCallback,this); + if (!mUseDualClient) { + ((TunnelClient*)mTunnelClient)->setCallback(tunnelCallback,this); + } else { + ((DualTunnelClient*)mTunnelClient)->setCallback(tunnelCallback2,this); + } } if (mVerifyServerCertificate) { @@ -115,10 +169,20 @@ void TunnelManager::startClient() { } } mTunnelClient->cleanServers(); - list::iterator it; - for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){ - const ServerAddr &addr=*it; - mTunnelClient->addServer(addr.mAddr.c_str(), addr.mPort); + if (mUseDualClient) { + list>::iterator it; + for(it=mDualServerAddrs.begin();it!=mDualServerAddrs.end();++it){ + pair &addr=*it; + const ServerAddr addr1 = addr.first; + const ServerAddr addr2 = addr.second; + ((DualTunnelClient*)mTunnelClient)->addServerPair(addr1.mAddr.c_str(), addr1.mPort, addr2.mAddr.c_str(), addr2.mPort); + } + } else { + list::iterator it; + for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){ + const ServerAddr &addr=*it; + ((TunnelClient*)mTunnelClient)->addServer(addr.mAddr.c_str(), addr.mPort); + } } mTunnelClient->setHttpProxy(mHttpProxyHost.c_str(), mHttpProxyPort, mHttpUserName.c_str(), mHttpPasswd.c_str()); if (!mTunnelClient->isStarted()) @@ -145,19 +209,21 @@ bool TunnelManager::isConnected() const { int TunnelManager::customSendto(struct _RtpTransport *t, mblk_t *msg , int flags, const struct sockaddr *to, socklen_t tolen){ int size; + DualSocket *ds = (DualSocket *)t->data; msgpullup(msg,-1); size=msgdsize(msg); - ((TunnelSocket*)t->data)->sendto(msg->b_rptr,size,to,tolen); + ds->sendSocket->sendto(msg->b_rptr,size,to,tolen); return size; } int TunnelManager::customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen){ + DualSocket *ds = (DualSocket *)t->data; memset(&msg->recv_addr,0,sizeof(msg->recv_addr)); - int err=((TunnelSocket*)t->data)->recvfrom(msg->b_wptr,dblk_lim(msg->b_datap)-dblk_base(msg->b_datap),from,*fromlen); + int err=ds->recvSocket->recvfrom(msg->b_wptr,dblk_lim(msg->b_datap)-dblk_base(msg->b_datap),from,*fromlen); //to make ice happy - inet_aton(((TunnelManager*)((TunnelSocket*)t->data)->getUserPointer())->mLocalAddr,&msg->recv_addr.addr.ipi_addr); + inet_aton(((TunnelManager*)(ds->recvSocket)->getUserPointer())->mLocalAddr,&msg->recv_addr.addr.ipi_addr); msg->recv_addr.family = AF_INET; - msg->recv_addr.port = htons((unsigned short)((TunnelSocket*)t->data)->getPort()); + msg->recv_addr.port = htons((unsigned short)(ds->recvSocket)->getPort()); if (err>0) return err; return 0; } @@ -169,7 +235,8 @@ TunnelManager::TunnelManager(LinphoneCore* lc) : mHttpProxyPort(0), mVTable(NULL), mLongRunningTaskId(0), - mSimulateUdpLoss(false) + mSimulateUdpLoss(false), + mUseDualClient(false) { linphone_core_add_iterate_hook(mCore,(LinphoneCoreIterateHook)sOnIterate,this); mTransportFactories.audio_rtcp_func=sCreateRtpTransport; @@ -248,7 +315,6 @@ void TunnelManager::untunnelizeLiblinphone(){ } } - void TunnelManager::applyState() { if (!linphone_core_is_network_reachable(mCore)) return; if (mTargetState == On && mState == Off){ @@ -267,8 +333,6 @@ void TunnelManager::setState ( TunnelManager::State state ) { applyState(); } - - void TunnelManager::processTunnelEvent(const Event &ev){ if (ev.mData.mConnected){ ms_message("TunnelManager: tunnel is connected"); @@ -296,7 +360,6 @@ void TunnelManager::applyMode() { } } - void TunnelManager::setMode(LinphoneTunnelMode mode) { if(mMode == mode) return; ms_message("TunnelManager: switching mode from %s to %s", @@ -314,7 +377,6 @@ void TunnelManager::stopLongRunningTask() { } } - void TunnelManager::tunnelCallback(bool connected, void *user_pointer){ TunnelManager *zis = static_cast(user_pointer); Event ev; @@ -324,6 +386,15 @@ void TunnelManager::tunnelCallback(bool connected, void *user_pointer){ zis->postEvent(ev); } +void TunnelManager::tunnelCallback2(TunnelDirection direction, bool connected, void *user_pointer){ + TunnelManager *zis = static_cast(user_pointer); + Event ev; + + ev.mType=TunnelEvent; + ev.mData.mConnected=connected; + zis->postEvent(ev); +} + void TunnelManager::onIterate(){ mMutex.lock(); while(!mEvq.empty()){ @@ -384,7 +455,6 @@ void TunnelManager::enableLogs(bool isEnabled,LogHandler logHandler) { } } - LinphoneTunnelMode TunnelManager::getMode() const { return mMode; } diff --git a/coreapi/TunnelManager.hh b/coreapi/TunnelManager.hh index 4bf281c2e..f3ea64833 100644 --- a/coreapi/TunnelManager.hh +++ b/coreapi/TunnelManager.hh @@ -28,7 +28,11 @@ namespace belledonnecomm { * @addtogroup tunnel_client * @{ **/ - + struct DualSocket { + TunnelSocket *sendSocket; + TunnelSocket *recvSocket; + }; + /** * The TunnelManager class extends the LinphoneCore functionnality in order to provide an easy to use API to * - provision tunnel servers ip addresses and ports @@ -59,10 +63,32 @@ namespace belledonnecomm { * @param delay udp packet round trip delay in ms considered as acceptable. recommended value is 1000 ms. */ void addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay); + /** + * Add a tunnel server couple. At least one should be provided to be able to connect. + * This is used when using the dual socket mode where one client will connect to one ip and the other client to the other ip. + * + * @param ip1 server ip address n°1 + * @param port1 tunnel server tls port, recommended value is 443 + * @param ip2 server ip address n°2 + * @param port2 tunnel server tls port, recommended value is 443 + */ + void addServerPair(const char *ip1, int port1, const char *ip2, int port2); /** * Removes all tunnel server address previously entered with addServer() **/ void cleanServers(); + + /** + * Enables the dual socket mode. In this mode, we have to configure pairs or ServerAddr + * 2 TunneClient will be used, one for each IP and each one will only either send or receive the data stream. + * @param enable true to enable the DualMode, false otherwise + */ + void enableDualMode(bool enable); + /** + * Returns whether or not the DualMode is enabled + * @return true if it is enabled, false otherwise + */ + bool isDualModeEnabled(); /** * Forces reconnection to the tunnel server. * This method is useful when the device switches from wifi to Edge/3G or vice versa. In most cases the tunnel client socket @@ -174,6 +200,7 @@ namespace belledonnecomm { static int customSendto(struct _RtpTransport *t, mblk_t *msg , int flags, const struct sockaddr *to, socklen_t tolen); static int customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen); static void tunnelCallback(bool connected, void *zis); + static void tunnelCallback2(TunnelDirection direction, bool connected, void *zis); static void sOnIterate(TunnelManager *zis); static void sUdpMirrorClientCallback(bool result, void* data); static void networkReachableCb(LinphoneCore *lc, bool_t reachable); @@ -203,13 +230,14 @@ namespace belledonnecomm { LinphoneCore* mCore; LinphoneTunnelMode mMode; - TunnelClient* mTunnelClient; + TunnelClientI* mTunnelClient; std::string mHttpUserName; std::string mHttpPasswd; std::string mHttpProxyHost; int mHttpProxyPort; LinphoneCoreVTable *mVTable; std::list mServerAddrs; + std::list > mDualServerAddrs; UdpMirrorClientList mUdpMirrorClients; UdpMirrorClientList::iterator mCurrentUdpMirrorClient; LinphoneRtpTransportFactories mTransportFactories; @@ -224,6 +252,7 @@ namespace belledonnecomm { bool mAutodetectionRunning; bool mTunnelizeSipPackets; bool mSimulateUdpLoss; + bool mUseDualClient; }; /** diff --git a/coreapi/linphone_tunnel.cc b/coreapi/linphone_tunnel.cc index 4ec4640eb..36edcae8d 100644 --- a/coreapi/linphone_tunnel.cc +++ b/coreapi/linphone_tunnel.cc @@ -63,6 +63,7 @@ void linphone_tunnel_destroy(LinphoneTunnel *tunnel){ static char *linphone_tunnel_config_to_string(const LinphoneTunnelConfig *tunnel_config) { char *str = NULL; const char *host = linphone_tunnel_config_get_host(tunnel_config); + const char *host2 = linphone_tunnel_config_get_host2(tunnel_config); if(host != NULL) { if(linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config) != -1) { str = ms_strdup_printf("%s:%d:%d:%d", @@ -70,6 +71,12 @@ static char *linphone_tunnel_config_to_string(const LinphoneTunnelConfig *tunnel linphone_tunnel_config_get_port(tunnel_config), linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config), linphone_tunnel_config_get_delay(tunnel_config)); + } else if (host2 != NULL) { + str = ms_strdup_printf("%s:%d/%s:%d", + linphone_tunnel_config_get_host(tunnel_config), + linphone_tunnel_config_get_port(tunnel_config), + linphone_tunnel_config_get_host2(tunnel_config), + linphone_tunnel_config_get_port2(tunnel_config)); } else { str = ms_strdup_printf("%s:%d", linphone_tunnel_config_get_host(tunnel_config), @@ -88,8 +95,12 @@ static LinphoneTunnelConfig *linphone_tunnel_config_from_string(const char *str) int delay = -1; int pos = 0; char *pch; - pch = strtok(dstr, ":"); - while(pch != NULL) { + char *tok1, *tok2; + tok1 = strtok(dstr, "/"); + tok2 = strtok(NULL, "/"); + + pch = strtok(tok1, ":"); + while (pch != NULL) { switch(pos) { case 0: host = pch; @@ -112,17 +123,50 @@ static LinphoneTunnelConfig *linphone_tunnel_config_from_string(const char *str) ++pos; pch = strtok(NULL, ":"); } - if(pos >= 2) { + if (pos >= 2) { tunnel_config = linphone_tunnel_config_new(); linphone_tunnel_config_set_host(tunnel_config, host); linphone_tunnel_config_set_port(tunnel_config, port); } - if(pos >= 3) { + if (pos >= 3) { linphone_tunnel_config_set_remote_udp_mirror_port(tunnel_config, remote_udp_mirror_port); } - if(pos == 4) { + if (pos == 4) { linphone_tunnel_config_set_delay(tunnel_config, delay); } + + if (tok2) { + pos = 0; + pch = strtok(tok2, ":"); + while (pch != NULL) { + switch(pos) { + case 0: + host = pch; + break; + case 1: + port = atoi(pch); + break; + case 2: + remote_udp_mirror_port = atoi(pch); + break; + case 3: + delay = atoi(pch); + break; + default: + // Abort + pos = 0; + break; + + } + ++pos; + pch = strtok(NULL, ":"); + } + if (pos >= 2 && tunnel_config) { + linphone_tunnel_config_set_host2(tunnel_config, host); + linphone_tunnel_config_set_port2(tunnel_config, port); + } + } + ms_free(dstr); return tunnel_config; } @@ -153,14 +197,19 @@ static void linphone_tunnel_save_config(const LinphoneTunnel *tunnel) { static void linphone_tunnel_add_server_intern(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config) { - if(linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config) == -1) { - bcTunnel(tunnel)->addServer(linphone_tunnel_config_get_host(tunnel_config), - linphone_tunnel_config_get_port(tunnel_config)); - } else { + if(linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config) != -1) { bcTunnel(tunnel)->addServer(linphone_tunnel_config_get_host(tunnel_config), linphone_tunnel_config_get_port(tunnel_config), linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config), linphone_tunnel_config_get_delay(tunnel_config)); + } else if (linphone_tunnel_config_get_host2(tunnel_config) != NULL) { + bcTunnel(tunnel)->addServerPair(linphone_tunnel_config_get_host(tunnel_config), + linphone_tunnel_config_get_port(tunnel_config), + linphone_tunnel_config_get_host2(tunnel_config), + linphone_tunnel_config_get_port2(tunnel_config)); + } else { + bcTunnel(tunnel)->addServer(linphone_tunnel_config_get_host(tunnel_config), + linphone_tunnel_config_get_port(tunnel_config)); } tunnel->config_list = bctbx_list_append(tunnel->config_list, linphone_tunnel_config_ref(tunnel_config)); } @@ -241,6 +290,14 @@ LinphoneTunnelMode linphone_tunnel_get_mode(const LinphoneTunnel *tunnel){ return bcTunnel(tunnel)->getMode(); } +void linphone_tunnel_set_dual_mode(LinphoneTunnel *tunnel, bool_t dual_mode_enabled) { + bcTunnel(tunnel)->enableDualMode(dual_mode_enabled); +} + +bool_t linphone_tunnel_get_dual_mode(const LinphoneTunnel *tunnel) { + return bcTunnel(tunnel)->isDualModeEnabled(); +} + bool_t linphone_tunnel_connected(const LinphoneTunnel *tunnel){ return bcTunnel(tunnel)->isConnected(); } diff --git a/coreapi/linphone_tunnel_config.c b/coreapi/linphone_tunnel_config.c index 1c0ee4b8b..b95d153fe 100644 --- a/coreapi/linphone_tunnel_config.c +++ b/coreapi/linphone_tunnel_config.c @@ -31,6 +31,8 @@ struct _LinphoneTunnelConfig { int remote_udp_mirror_port; int delay; void *user_data; + char *host2; + int port2; }; LinphoneTunnelConfig *linphone_tunnel_config_new() { @@ -38,6 +40,8 @@ LinphoneTunnelConfig *linphone_tunnel_config_new() { ltc->remote_udp_mirror_port = 12345; ltc->delay = 1000; ltc->port = 443; + ltc->host2 = NULL; + ltc->port2 = 0; return ltc; } @@ -63,6 +67,28 @@ int linphone_tunnel_config_get_port(const LinphoneTunnelConfig *tunnel) { return tunnel->port; } +void linphone_tunnel_config_set_host2(LinphoneTunnelConfig *tunnel, const char *host) { + if(tunnel->host2 != NULL) { + ms_free(tunnel->host2); + tunnel->host2 = NULL; + } + if(host != NULL && strlen(host)) { + tunnel->host2 = ms_strdup(host); + } +} + +const char *linphone_tunnel_config_get_host2(const LinphoneTunnelConfig *tunnel) { + return tunnel->host2; +} + +void linphone_tunnel_config_set_port2(LinphoneTunnelConfig *tunnel, int port) { + tunnel->port2 = port; +} + +int linphone_tunnel_config_get_port2(const LinphoneTunnelConfig *tunnel) { + return tunnel->port2; +} + void linphone_tunnel_config_set_remote_udp_mirror_port(LinphoneTunnelConfig *tunnel, int remote_udp_mirror_port) { tunnel->remote_udp_mirror_port = remote_udp_mirror_port; } diff --git a/include/linphone/tunnel.h b/include/linphone/tunnel.h index c312c6fbb..ab1fc060b 100644 --- a/include/linphone/tunnel.h +++ b/include/linphone/tunnel.h @@ -146,6 +146,34 @@ LINPHONE_PUBLIC void linphone_tunnel_config_set_port(LinphoneTunnelConfig *tunne */ LINPHONE_PUBLIC int linphone_tunnel_config_get_port(const LinphoneTunnelConfig *tunnel); +/** + * Set the IP address or hostname of the second tunnel server when using dual tunnel client. + * @param tunnel LinphoneTunnelConfig object + * @param host The tunnel server IP address or hostname + */ +LINPHONE_PUBLIC void linphone_tunnel_config_set_host2(LinphoneTunnelConfig *tunnel, const char *host); + +/** + * Get the IP address or hostname of the second tunnel server when using dual tunnel client. + * @param tunnel LinphoneTunnelConfig object + * @return The tunnel server IP address or hostname + */ +LINPHONE_PUBLIC const char *linphone_tunnel_config_get_host2(const LinphoneTunnelConfig *tunnel); + +/** + * Set tls port of the second server when using dual tunnel client. + * @param tunnel LinphoneTunnelConfig object + * @param port The tunnel server TLS port, recommended value is 443 + */ +LINPHONE_PUBLIC void linphone_tunnel_config_set_port2(LinphoneTunnelConfig *tunnel, int port); + +/** + * Get the TLS port of the second tunnel server when using dual tunnel client. + * @param tunnel LinphoneTunnelConfig object + * @return The TLS port of the tunnel server + */ +LINPHONE_PUBLIC int linphone_tunnel_config_get_port2(const LinphoneTunnelConfig *tunnel); + /** * Set the remote port on the tunnel server side used to test UDP reachability. * This is used when the mode is set auto, to detect whether the tunnel has to be enabled or not. @@ -255,6 +283,22 @@ LINPHONE_PUBLIC void linphone_tunnel_set_mode(LinphoneTunnel *tunnel, LinphoneTu **/ LINPHONE_PUBLIC LinphoneTunnelMode linphone_tunnel_get_mode(const LinphoneTunnel *tunnel); +/** + * Sets whether or not to use the dual tunnel client mode. + * By default this feature is disabled. + * After enabling it, add a server with 2 hosts and 2 ports for the feature to work. + * @param tunnel LinphoneTunnel object + * @param dual_mode_enabled TRUE to enable it, FALSE to disable it + */ +LINPHONE_PUBLIC void linphone_tunnel_set_dual_mode(LinphoneTunnel *tunnel, bool_t dual_mode_enabled); + +/** + * Get the dual tunnel client mode + * @param tunnel LinphoneTunnel object + * @return TRUE if dual tunnel client mode is enabled, FALSE otherwise +**/ +LINPHONE_PUBLIC bool_t linphone_tunnel_get_dual_mode(const LinphoneTunnel *tunnel); + /** * Returns whether the tunnel is activated. If mode is set to auto, this gives indication whether the automatic detection determined * that tunnel was necessary or not.