From d4c2c25cd5e4ad2a434bb4474afdd573d0322061 Mon Sep 17 00:00:00 2001 From: smorlat Date: Sat, 13 Sep 2008 21:27:23 +0000 Subject: [PATCH] - stun improvements (untested) - support for one relay (work in progress) git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@18 3f6dc0c8-ddfe-455d-9043-3cd528dc4637 --- linphone/coreapi/exevents.c | 13 ++- linphone/coreapi/linphonecore.c | 45 +++------ linphone/coreapi/linphonecore.h | 6 +- linphone/coreapi/misc.c | 164 ++++++++++++++++++++++++++++++++ linphone/coreapi/private.h | 1 + linphone/oRTP/src/stun.c | 2 + 6 files changed, 193 insertions(+), 38 deletions(-) diff --git a/linphone/coreapi/exevents.c b/linphone/coreapi/exevents.c index 693d1367d..39efc2808 100644 --- a/linphone/coreapi/exevents.c +++ b/linphone/coreapi/exevents.c @@ -436,7 +436,8 @@ int linphone_set_audio_offer(sdp_context_t *ctx) sdp_payload_init(&payload); payload.a_rtpmap=ortp_strdup_printf("%s/%i/1",codec->mime_type,codec->clock_rate); payload.pt=rtp_profile_get_payload_number_from_rtpmap(lc->local_profile,payload.a_rtpmap); - payload.localport=lc->rtp_conf.audio_rtp_port; + payload.localport=call->audio_params.natd_port > 0 ? + call->audio_params.natd_port : lc->rtp_conf.audio_rtp_port; if (strcasecmp(codec->mime_type,"iLBC")==0){ /* prefer the 30 ms mode */ payload.a_fmtp="ptime=30"; @@ -511,7 +512,8 @@ int linphone_set_video_offer(sdp_context_t *ctx) sdp_payload_init(&payload); payload.line=1; payload.a_rtpmap=ortp_strdup_printf("%s/%i",codec->mime_type,codec->clock_rate); - payload.localport=lc->rtp_conf.video_rtp_port; + payload.localport=call->video_params.natd_port>0 ? + call->video_params.natd_port : lc->rtp_conf.video_rtp_port; payload.pt=find_payload_type_number(lc->local_profile,codec); payload.a_fmtp=codec->recv_fmtp; if(firsttime){ @@ -628,7 +630,9 @@ int linphone_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload) params=&call->audio_params; if (params->initialized==0){ /* this is the first codec we accept, it is going to be used*/ - params->localport=payload->localport=lc->rtp_conf.audio_rtp_port; + params->localport=lc->rtp_conf.audio_rtp_port; + payload->localport=params->natd_port>0 ? + params->natd_port : lc->rtp_conf.audio_rtp_port; params->line=payload->line; params->pt=payload->pt; /* remember the first payload accepted */ if (payload->relay_host!=NULL){ @@ -678,7 +682,8 @@ int linphone_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload) params=&call->video_params; if (params->initialized==0){ /* this is the first codec we may accept*/ - params->localport=payload->localport=lc->rtp_conf.video_rtp_port; + params->localport=lc->rtp_conf.video_rtp_port; + payload->localport=params->natd_port>0 ? params->natd_port : lc->rtp_conf.video_rtp_port; params->line=payload->line; params->pt=payload->pt; /* remember the first payload accepted */ if (payload->relay_host!=NULL){ diff --git a/linphone/coreapi/linphonecore.c b/linphone/coreapi/linphonecore.c index 947dc535c..2e4071556 100644 --- a/linphone/coreapi/linphonecore.c +++ b/linphone/coreapi/linphonecore.c @@ -25,7 +25,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sdphandler.h" #include -#include #include "exevents.h" @@ -81,6 +80,8 @@ static void linphone_call_init_common(LinphoneCall *call, char *from, char *to) call->start_time=time(NULL); call->log=linphone_call_log_new(call, from, to); linphone_core_notify_all_friends(call->core,LINPHONE_STATUS_ONTHEPHONE); + if (linphone_core_get_firewall_policy(call->core)==LINPHONE_POLICY_USE_STUN) + linphone_core_run_stun_tests(call->core,call); } void linphone_call_init_media_params(LinphoneCall *call){ @@ -112,10 +113,12 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, const osip_f call->tid=-1; call->core=lc; linphone_core_get_local_ip(lc,to->url->host,localip); - call->sdpctx=sdp_handler_create_context(&linphone_sdphandler,localip,from->url->username,NULL); osip_from_to_str(from,&fromstr); osip_to_to_str(to,&tostr); linphone_call_init_common(call,fromstr,tostr); + call->sdpctx=sdp_handler_create_context(&linphone_sdphandler, + call->audio_params.natd_port>0 ? call->audio_params.natd_addr : localip, + from->url->username,NULL); discover_mtu(lc,to->url->host); return call; } @@ -135,8 +138,10 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, const char *from, co osip_from_init(&from_url); osip_from_parse(from_url, from); linphone_core_get_local_ip(lc,from_url->url->host,localip); - call->sdpctx=sdp_handler_create_context(&linphone_sdphandler,localip,me->url->username,NULL); linphone_call_init_common(call, osip_strdup(from), osip_strdup(to)); + call->sdpctx=sdp_handler_create_context(&linphone_sdphandler, + call->audio_params.natd_port>0 ? call->audio_params.natd_addr : localip, + me->url->username,NULL); discover_mtu(lc,from_url->url->host); osip_from_free(me); osip_from_free(from_url); @@ -733,35 +738,6 @@ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) return 0; } -static bool_t stun_get_localip(LinphoneCore *lc, char *result, int *port){ - const char *server=linphone_core_get_stun_server(lc); - StunAddress4 addr; - StunAddress4 mapped; - StunAddress4 changed; - if (server!=NULL){ - if (stunParseServerName((char*)server,&addr)){ - if (lc->vtable.display_status!=NULL) - lc->vtable.display_status(lc,_("Stun lookup in progress...")); - if (stunTest(&addr,1,TRUE,NULL,&mapped,&changed)==0){ - struct in_addr inaddr; - char *tmp; - inaddr.s_addr=ntohl(mapped.addr); - tmp=inet_ntoa(inaddr); - *port=ntohs(mapped.port); - strncpy(result,tmp,LINPHONE_IPADDR_SIZE); - if (lc->vtable.display_status!=NULL) - lc->vtable.display_status(lc,_("Stun lookup done...")); - ms_message("Stun server says we have address %s:i",result,*port); - return TRUE; - }else{ - ms_warning("stun lookup failed."); - } - }else{ - ms_warning("Fail to resolv or parse %s",server); - } - } - return FALSE; -} /*result must be an array of chars at least LINPHONE_IPADDR_SIZE */ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){ @@ -777,14 +753,17 @@ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result if (lc->sip_conf.ipv6_enabled){ ms_warning("stun support is not implemented for ipv6"); }else{ + /* we no more use stun for sip socket*/ +#if 0 int mport=0; ms_message("doing stun lookup for local address..."); - if (stun_get_localip(lc,result,&mport)){ + if (stun_get_localip(lc,sock,linphone_core_get_sip_port(lc),result,&mport)){ if (!lc->net_conf.nat_sdp_only) eXosip_masquerade_contact(result,mport); return; } ms_warning("stun lookup failed, falling back to a local interface..."); +#endif } } diff --git a/linphone/coreapi/linphonecore.h b/linphone/coreapi/linphonecore.h index aa0216a43..bf867cad0 100644 --- a/linphone/coreapi/linphonecore.h +++ b/linphone/coreapi/linphonecore.h @@ -25,6 +25,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "ortp/payloadtype.h" #include "mediastreamer2/mscommon.h" +#define LINPHONE_IPADDR_SIZE 64 + #ifdef __cplusplus extern "C" { #endif @@ -145,6 +147,8 @@ typedef struct _StreamParams char *remoteaddr; int pt; char *relay_session_id; + char natd_addr[LINPHONE_IPADDR_SIZE]; + int natd_port; } StreamParams; typedef enum _LCState{ @@ -486,7 +490,7 @@ typedef struct _LinphoneCore #endif } LinphoneCore; -#define LINPHONE_IPADDR_SIZE 64 + /* THE main API */ diff --git a/linphone/coreapi/misc.c b/linphone/coreapi/misc.c index 418e01c99..3efa59f99 100644 --- a/linphone/coreapi/misc.c +++ b/linphone/coreapi/misc.c @@ -27,7 +27,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include +#include +#include #ifndef WIN32 @@ -448,3 +450,165 @@ bool_t host_has_ipv6_network() #endif + +static ortp_socket_t create_socket(int local_port){ + struct sockaddr_in laddr; + ortp_socket_t sock; + int optval; + sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); + if (sock<0) { + ms_error("Fail to create socket"); + return -1; + } + memset (&laddr,0,sizeof(laddr)); + laddr.sin_family=AF_INET; + laddr.sin_addr.s_addr=INADDR_ANY; + laddr.sin_port=htons(local_port); + if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){ + ms_error("Bind to 0.0.0.0:%i failed: %s",local_port,strerror(errno)); + close_socket(sock); + return -1; + } + optval=1; + if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, + (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){ + ms_warning("Fail to set SO_REUSEADDR"); + } + set_non_blocking_socket(sock); + return sock; +} + +static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id){ + char buf[STUN_MAX_MESSAGE_SIZE]; + int len = STUN_MAX_MESSAGE_SIZE; + StunAtrString username; + StunAtrString password; + StunMessage req; + int err; + memset(&req, 0, sizeof(StunMessage)); + memset(&username,0,sizeof(username)); + memset(&password,0,sizeof(password)); + stunBuildReqSimple( &req, &username, FALSE , FALSE , id); + len = stunEncodeMessage( &req, buf, len, &password, FALSE); + if (len<=0){ + ms_error("Fail to encode stun message."); + return -1; + } + err=sendto(sock,buf,len,0,server,addrlen); + if (err<0){ + ms_error("sendto failed: %s",strerror(errno)); + return -1; + } + return 0; +} + +static int parse_stun_server_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){ + struct addrinfo hints,*res=NULL; + int ret; + const char *port; + char host[NI_MAXHOST]; + char *p; + host[NI_MAXHOST-1]='\0'; + strncpy(host,server,sizeof(host)-1); + p=strchr(server,':'); + if (p) { + *p='\0'; + port=p+1; + }else port="3478"; + memset(&hints,0,sizeof(hints)); + hints.ai_family=PF_INET; + hints.ai_socktype=SOCK_DGRAM; + hints.ai_protocol=IPPROTO_UDP; + ret=getaddrinfo(host,port,&hints,&res); + if (ret!=0){ + ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret)); + return -1; + } + if (!res) return -1; + memcpy(ss,res->ai_addr,res->ai_addrlen); + *socklen=res->ai_addrlen; + freeaddrinfo(res); + return 0; +} + +static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port){ + char buf[STUN_MAX_MESSAGE_SIZE]; + int len = STUN_MAX_MESSAGE_SIZE; + StunMessage resp; + len=recv(sock,buf,len,0); + if (len>0){ + struct in_addr ia; + stunParseMessage(buf,len, &resp,FALSE ); + *port = resp.changedAddress.ipv4.port; + ia.s_addr=htonl(resp.changedAddress.ipv4.addr); + strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE); + } + return len; +} + +void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ + const char *server=linphone_core_get_stun_server(lc); + + if (lc->sip_conf.ipv6_enabled){ + ms_warning("stun support is not implemented for ipv6"); + return; + } + if (server!=NULL){ + struct sockaddr_storage ss; + socklen_t ss_len; + ortp_socket_t sock1=-1, sock2=-1; + bool_t video_enabled=linphone_core_video_enabled(lc); + bool_t got_audio,got_video; + struct timeval init,cur; + if (parse_stun_server_addr(server,&ss,&ss_len)<0){ + return; + } + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Stun lookup in progress...")); + + /*create the two audio and video RTP sockets, and send STUN message to our stun server */ + sock1=create_socket(linphone_core_get_audio_port(lc)); + if (sock1<0) return; + if (video_enabled){ + sock2=create_socket(linphone_core_get_video_port(lc)); + if (sock2<0) return ; + } + sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1); + if (sock2>=0) + sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2); + + got_audio=FALSE; + got_video=FALSE; + gettimeofday(&init,NULL); + do{ + double elapsed; +#ifdef WIN32 + Sleep(10); +#else + usleep(10000); +#endif + + if (recvStunResponse(sock1,call->audio_params.natd_addr, + &call->audio_params.natd_port)>0){ + ms_message("STUN test result: local audio port maps to %s:%i", + call->audio_params.natd_addr, + call->audio_params.natd_port); + got_audio=TRUE; + } + if (recvStunResponse(sock2,call->video_params.natd_addr, + &call->video_params.natd_port)>0){ + ms_message("STUN test result: local video port maps to %s:%i", + call->video_params.natd_addr, + call->video_params.natd_port); + got_video=TRUE; + } + gettimeofday(&cur,NULL); + elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0); + if (elapsed>2000) break; + }while(!(got_audio && (got_video||sock2<0) ) ); + close_socket(sock1); + if (sock2>=0) close_socket(sock2); + } +} + + diff --git a/linphone/coreapi/private.h b/linphone/coreapi/private.h index bbb06a39b..13668e436 100644 --- a/linphone/coreapi/private.h +++ b/linphone/coreapi/private.h @@ -117,5 +117,6 @@ LinphoneFriend *linphone_find_friend_by_nid(MSList *l, int nid); LinphoneFriend *linphone_find_friend_by_sid(MSList *l, int sid); void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc, const PayloadType *pt); +void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call); #endif /* _PRIVATE_H */ diff --git a/linphone/oRTP/src/stun.c b/linphone/oRTP/src/stun.c index a0e34a471..7b9038938 100644 --- a/linphone/oRTP/src/stun.c +++ b/linphone/oRTP/src/stun.c @@ -2067,6 +2067,8 @@ stunTest( StunAddress4 *dest, int testNum, bool_t verbose, StunAddress4* sAddr , } + + NatType stunNatType( StunAddress4 *dest, bool_t verbose,