]> sjero.net Git - linphone/blobdiff - coreapi/TunnelManager.cc
- do not register outside of tunnel when tunnel is activated but not yet connected.
[linphone] / coreapi / TunnelManager.cc
index 2243119d47ecd903bd401b0c951f870068ea5d62..409f9c42a83635c41ba6ff2890b7166aae46d9f6 100644 (file)
 #include <android/log.h>
 #endif
 
-#ifdef recvfrom 
-#undef recvfrom
-#endif
-#ifdef sendto 
-#undef sendto
-#endif
-#ifdef select 
-#undef select
-#endif
 
 using namespace belledonnecomm;
+using namespace ::std;
 
 Mutex TunnelManager::sMutex;
 
 int TunnelManager::eXosipSendto(int fd,const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen,void* userdata){
        TunnelManager* lTunnelMgr=(TunnelManager*)userdata;
-       int err;
+       
        sMutex.lock();
        if (lTunnelMgr->mSipSocket==NULL){
                sMutex.unlock();
-               return len;//let ignore the error
+               return len;
        }
-       err=lTunnelMgr->mSipSocket->sendto(buf,len,to,tolen);
+       lTunnelMgr->mSipSocket->sendto(buf,len,to,tolen);
        sMutex.unlock();
-       return err;
+       //ignore the error in all cases, retransmissions might be successful.
+       return len;
 }
 
 int TunnelManager::eXosipRecvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen,void* userdata){
@@ -66,26 +59,12 @@ int TunnelManager::eXosipRecvfrom(int fd, void *buf, size_t len, int flags, stru
 int TunnelManager::eXosipSelect(int max_fds, fd_set *s1, fd_set *s2, fd_set *s3, struct timeval *tv,void* userdata){
        struct timeval begin,cur;
        TunnelManager* lTunnelMgr=(TunnelManager*)userdata;
-       if (tv!=0 && tv->tv_sec){
+       if (s1 && tv!=0 && tv->tv_sec){
                /*this is the select from udp.c, the one that is interesting to us*/
-               int i;
-               int udp_fd=eXosip_get_udp_socket();
-               int controlfd=-1;
-
-               /*
-                       Find the udp fd and the control fd
-               */
-               for(i=0;i<max_fds;++i){
-                       if (FD_ISSET(i,s1) && i!=udp_fd){
-                               controlfd=i;
-                               break;
-                       }
-               }
-               if (controlfd==-1){
-                       ms_error("Could not find control fd !");
-                       return -1;
-               }
-               FD_ZERO(s1);            
+               NativeSocket udp_fd=(NativeSocket)eXosip_get_udp_socket();
+               NativeSocket controlfd=(NativeSocket)eXosip_get_control_fd();
+
+               FD_ZERO(s1);
                gettimeofday(&begin,NULL);
                do{
                        struct timeval abit;
@@ -123,11 +102,19 @@ int TunnelManager::eXosipSelect(int max_fds, fd_set *s1, fd_set *s2, fd_set *s3,
 
 
 void TunnelManager::addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay) {
+       if (ip == NULL) {
+               ip = "";
+               ms_warning("Adding tunnel server with empty ip, it will not work!");
+       }
        addServer(ip,port);
        mUdpMirrorClients.push_back(UdpMirrorClient(ServerAddr(ip,udpMirrorPort),delay));
 }
 
 void TunnelManager::addServer(const char *ip, int port) {
+       if (ip == NULL) {
+               ip = "";
+               ms_warning("Adding tunnel server with empty ip, it will not work!");
+       }
        mServerAddrs.push_back(ServerAddr(ip,port));
        if (mTunnelClient) mTunnelClient->addServer(ip,port);
 }
@@ -157,7 +144,7 @@ void TunnelManager::setCallback(StateCallback cb, void *userdata) {
 
 static void sCloseRtpTransport(RtpTransport *t, void *userData){
        TunnelSocket *s=(TunnelSocket*)userData;
-       TunnelManager::TunnelManager *manager=(TunnelManager::TunnelManager*)s->getUserPointer();
+       TunnelManager *manager=(TunnelManager*)s->getUserPointer();
        manager->closeRtpTransport(t, s);
 }
 void TunnelManager::closeRtpTransport(RtpTransport *t, TunnelSocket *s){
@@ -166,7 +153,7 @@ void TunnelManager::closeRtpTransport(RtpTransport *t, TunnelSocket *s){
 }
 
 static RtpTransport *sCreateRtpTransport(void* userData, int port){
-       return ((TunnelManager::TunnelManager *) userData)->createRtpTransport(port);
+       return ((TunnelManager *) userData)->createRtpTransport(port);
 }
 
 RtpTransport *TunnelManager::createRtpTransport(int port){
@@ -185,14 +172,12 @@ void TunnelManager::start() {
        if (!mTunnelClient) {
                mTunnelClient = new TunnelClient();
                mTunnelClient->setCallback((StateCallback)tunnelCallback,this);
-               std::list<ServerAddr>::iterator it;
+               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(!mHttpUserName.empty()) {
-                       mTunnelClient->setHttpProxyAuthInfo(mHttpUserName.c_str(), mHttpPasswd.c_str());
-               }
+               mTunnelClient->setHttpProxy(mHttpProxyHost.c_str(), mHttpProxyPort, mHttpUserName.c_str(), mHttpPasswd.c_str());
        }
        mTunnelClient->start();
 
@@ -204,7 +189,7 @@ bool TunnelManager::isStarted() {
 }
 
 bool TunnelManager::isReady() const {
-       return mTunnelClient && mTunnelClient->isReady();
+       return mTunnelClient && mTunnelClient->isReady() && mReady;
 }
 
 int TunnelManager::customSendto(struct _RtpTransport *t, mblk_t *msg , int flags, const struct sockaddr *to, socklen_t tolen){
@@ -228,13 +213,14 @@ TunnelManager::TunnelManager(LinphoneCore* lc) :TunnelClientController()
 ,mCallback(NULL)
 ,mEnabled(false)
 ,mTunnelClient(NULL)
-,mAutoDetectStarted(false) {
+,mAutoDetectStarted(false)
+,mReady(false)
+,mHttpProxyPort(0){
 
        mExosipTransport.data=this;
        mExosipTransport.recvfrom=eXosipRecvfrom;
        mExosipTransport.sendto=eXosipSendto;
        mExosipTransport.select=eXosipSelect;
-       mStateChanged=false;
        linphone_core_add_iterate_hook(mCore,(LinphoneCoreIterateHook)sOnIterate,this);
        mTransportFactories.audio_rtcp_func=sCreateRtpTransport;
        mTransportFactories.audio_rtcp_func_data=this;
@@ -264,27 +250,29 @@ void TunnelManager::stopClient(){
        }
 }
 
-void TunnelManager::processTunnelEvent(){
+void TunnelManager::processTunnelEvent(const Event &ev){
        LinphoneProxyConfig* lProxy;
        linphone_core_get_default_proxy(mCore, &lProxy);
 
        if (mEnabled && mTunnelClient->isReady()){
-               ms_message("Tunnel is up, registering now");            
+               ms_message("Tunnel is up, registering now");
+               linphone_core_set_firewall_policy(mCore,LinphonePolicyNoFirewall);
                linphone_core_set_rtp_transport_factories(mCore,&mTransportFactories);
                eXosip_transport_hook_register(&mExosipTransport);
                //force transport to udp
                LCSipTransports lTransport;
                
-               lTransport.udp_port=15060;
+               lTransport.udp_port=(0xDFFF&random())+1024;
                lTransport.tcp_port=0;
                lTransport.tls_port=0;
                lTransport.dtls_port=0;
                
-               linphone_core_set_sip_transports(mCore, &lTransport);           
+               linphone_core_set_sip_transports(mCore, &lTransport);
                //register
                if (lProxy) {
                        linphone_proxy_config_done(lProxy);
                }
+               mReady=true;
        }else if (mEnabled && !mTunnelClient->isReady()){
                /* we got disconnected from the tunnel */
                if (lProxy && linphone_proxy_config_is_registered(lProxy)) {
@@ -292,46 +280,56 @@ void TunnelManager::processTunnelEvent(){
                        linphone_proxy_config_edit(lProxy);
                        linphone_core_iterate(mCore);
                }
+               mReady=false;
        }
 }
 
+void TunnelManager::waitUnRegistration(){
+       LinphoneProxyConfig* lProxy;
+       linphone_core_get_default_proxy(mCore, &lProxy);
+       if (lProxy && linphone_proxy_config_get_state(lProxy)==LinphoneRegistrationOk) {
+               int i=0;
+               linphone_proxy_config_edit(lProxy);
+               //make sure unregister is sent and authenticated
+               do{
+                       linphone_core_iterate(mCore);
+                       ms_usleep(20000);
+                       if (i>100){
+                               ms_message("tunnel: timeout for unregistration expired, giving up");
+                               break;
+                       }
+                       i++;
+               }while(linphone_proxy_config_get_state(lProxy)!=LinphoneRegistrationCleared);
+       }       
+}
+
 void TunnelManager::enable(bool isEnable) {
        ms_message("Turning tunnel [%s]",(isEnable?"on":"off"));
        if (isEnable && !mEnabled){
                mEnabled=true;
-               //1 save transport 
+               //1 save transport and firewall policy
                linphone_core_get_sip_transports(mCore, &mRegularTransport);
+               mPreviousFirewallPolicy=linphone_core_get_firewall_policy(mCore);
                //2 unregister
-               LinphoneProxyConfig* lProxy;
-               linphone_core_get_default_proxy(mCore, &lProxy);
-               if (lProxy) {
-                       linphone_proxy_config_edit(lProxy);
-                       //make sure unregister is sent
-                       linphone_core_iterate(mCore); 
-               }
+               waitUnRegistration();
                //3 insert tunnel
                start();
        }else if (!isEnable && mEnabled){
-               mEnabled=false;
-               stopClient();
                //1 unregister
-               LinphoneProxyConfig* lProxy;
-               linphone_core_get_default_proxy(mCore, &lProxy);
-               if (lProxy) {
-                       linphone_proxy_config_edit(lProxy);
-                       //make sure unregister is sent
-                       linphone_core_iterate(mCore); 
-               }
-               
-               //make sure unregister is sent
-               linphone_core_iterate(mCore); 
+               waitUnRegistration();
                
+               mEnabled=false;
+               stopClient();
+               mReady=false;
                linphone_core_set_rtp_transport_factories(mCore,NULL);
 
                eXosip_transport_hook_register(NULL);
-               //Restore transport
+               //Restore transport and firewall policy
                linphone_core_set_sip_transports(mCore, &mRegularTransport);
+               linphone_core_set_firewall_policy(mCore, mPreviousFirewallPolicy);
                //register
+               LinphoneProxyConfig* lProxy;
+               linphone_core_get_default_proxy(mCore, &lProxy);
                if (lProxy) {
                        linphone_proxy_config_done(lProxy);
                }
@@ -340,19 +338,36 @@ void TunnelManager::enable(bool isEnable) {
 }
 
 void TunnelManager::tunnelCallback(bool connected, TunnelManager *zis){
-       zis->mStateChanged=true;
+       Event ev;
+       ev.mType=TunnelEvent;
+       ev.mData.mConnected=connected;
+       zis->postEvent(ev);
+}
+
+void TunnelManager::onIterate(){
+       mMutex.lock();
+       while(!mEvq.empty()){
+               Event ev=mEvq.front();
+               mEvq.pop();
+               mMutex.unlock();
+               if (ev.mType==TunnelEvent)
+                       processTunnelEvent(ev);
+               else if (ev.mType==UdpMirrorClientEvent){
+                       processUdpMirrorEvent(ev);
+               }
+               mMutex.lock();
+       }
+       mMutex.unlock();
 }
 
 /*invoked from linphone_core_iterate() */
 void TunnelManager::sOnIterate(TunnelManager *zis){
-       if (zis->mStateChanged){
-               zis->mStateChanged=false;
-               zis->processTunnelEvent();
-       }
+       zis->onIterate();
 }
 
 #ifdef ANDROID
-static void linphone_android_log_handler(int lev, const char *fmt, va_list args){
+extern void linphone_android_log_handler(int prio, const char *fmt, va_list args);
+static void linphone_android_tunnel_log_handler(int lev, const char *fmt, va_list args) {
        int prio;
        switch(lev){
        case TUNNEL_DEBUG:      prio = ANDROID_LOG_DEBUG;       break;
@@ -360,9 +375,9 @@ static void linphone_android_log_handler(int lev, const char *fmt, va_list args)
        case TUNNEL_NOTICE:     prio = ANDROID_LOG_INFO;        break;
        case TUNNEL_WARN:       prio = ANDROID_LOG_WARN;        break;
        case TUNNEL_ERROR:      prio = ANDROID_LOG_ERROR;       break;
-       default:                        prio = ANDROID_LOG_DEFAULT;     break;
+       default:                prio = ANDROID_LOG_DEFAULT;     break;
        }
-       __android_log_vprint(prio, LOG_DOMAIN, fmt, args);
+       linphone_android_log_handler(prio, fmt, args);
 }
 #endif /*ANDROID*/
 
@@ -373,7 +388,7 @@ void TunnelManager::enableLogs(bool value) {
 void TunnelManager::enableLogs(bool isEnabled,LogHandler logHandler) {
        if (logHandler != NULL) SetLogHandler(logHandler);
 #ifdef ANDROID
-       else SetLogHandler(linphone_android_log_handler);
+       else SetLogHandler(linphone_android_tunnel_log_handler);
 #else
        else SetLogHandler(default_log_handler);
 #endif
@@ -389,26 +404,40 @@ void TunnelManager::enableLogs(bool isEnabled,LogHandler logHandler) {
 bool TunnelManager::isEnabled() {
        return mEnabled;
 }
-void TunnelManager::UdpMirrorClientListener(bool isUdpAvailable, void* data) {
-       TunnelManager* thiz = (TunnelManager*)data;
-       if (isUdpAvailable) {
+
+void TunnelManager::processUdpMirrorEvent(const Event &ev){
+       if (ev.mData.mHaveUdp) {
                LOGI("Tunnel is not required, disabling");
-               thiz->enable(false);
-               thiz->mAutoDetectStarted = false;
+               enable(false);
+               mAutoDetectStarted = false;
        } else {
-               if (++thiz->mCurrentUdpMirrorClient !=thiz->mUdpMirrorClients.end()) {
-                       //1 enable tunnable but also try backup server
+               mCurrentUdpMirrorClient++;
+               if (mCurrentUdpMirrorClient !=mUdpMirrorClients.end()) {
+                       // enable tunnel but also try backup server
                        LOGI("Tunnel is required, enabling; Trying backup udp mirror");
                        
-                       UdpMirrorClient &lUdpMirrorClient=*thiz->mCurrentUdpMirrorClient;
-                       lUdpMirrorClient.start(TunnelManager::UdpMirrorClientListener,(void*)thiz);
+                       UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient;
+                       lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this);
                } else {
                        LOGI("Tunnel is required, enabling; no backup udp mirror available");
-                       thiz->mAutoDetectStarted = false;
+                       mAutoDetectStarted = false;
                }
-               thiz->enable(true);
+               enable(true);
        }
-       return;
+}
+
+void TunnelManager::postEvent(const Event &ev){
+       mMutex.lock();
+       mEvq.push(ev);
+       mMutex.unlock();
+}
+
+void TunnelManager::sUdpMirrorClientCallback(bool isUdpAvailable, void* data) {
+       TunnelManager* thiz = (TunnelManager*)data;
+       Event ev;
+       ev.mType=UdpMirrorClientEvent;
+       ev.mData.mHaveUdp=isUdpAvailable;
+       thiz->postEvent(ev);
 }
 
 void TunnelManager::autoDetect() {
@@ -424,7 +453,7 @@ void TunnelManager::autoDetect() {
        mAutoDetectStarted=true;
        mCurrentUdpMirrorClient =mUdpMirrorClients.begin();
        UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient;
-       lUdpMirrorClient.start(TunnelManager::UdpMirrorClientListener,(void*)this);
+       lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this);
        
 }
 
@@ -434,6 +463,14 @@ void TunnelManager::setHttpProxyAuthInfo(const char* username,const char* passwd
        if (mTunnelClient) mTunnelClient->setHttpProxyAuthInfo(username,passwd);
 }
 
+void TunnelManager::setHttpProxy(const char *host,int port, const char *username, const char *passwd){
+       mHttpUserName=username?username:"";
+       mHttpPasswd=passwd?passwd:"";
+       mHttpProxyPort=(port>0) ? port : 0;
+       mHttpProxyHost=host ? host : "";
+       if (mTunnelClient) mTunnelClient->setHttpProxy(host, port, username, passwd);
+}
+
 LinphoneCore *TunnelManager::getLinphoneCore(){
        return mCore;
 }