Commit 463a24d3 authored by Sylvain Berfini's avatar Sylvain Berfini 🎩
Browse files

Added dual tunnel client feature in TunnelManager and C API above it

parent 8ba17edd
......@@ -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<ServerAddr, ServerAddr> 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<ServerAddr>::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<pair<ServerAddr, ServerAddr>>::iterator it;
for(it=mDualServerAddrs.begin();it!=mDualServerAddrs.end();++it){
pair<ServerAddr, ServerAddr> &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<ServerAddr>::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<TunnelManager*>(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<TunnelManager*>(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;
}
......
......@@ -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 <ServerAddr> mServerAddrs;
std::list <std::pair<ServerAddr, ServerAddr>> mDualServerAddrs;
UdpMirrorClientList mUdpMirrorClients;
UdpMirrorClientList::iterator mCurrentUdpMirrorClient;
LinphoneRtpTransportFactories mTransportFactories;
......@@ -224,6 +252,7 @@ namespace belledonnecomm {
bool mAutodetectionRunning;
bool mTunnelizeSipPackets;
bool mSimulateUdpLoss;
bool mUseDualClient;
};
/**
......
......@@ -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();
}
......
......@@ -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;
}
......
......@@ -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.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment