STUN-Simple Traversal of User Datagram Protocol (UDP) Through NetworkAddress Translators (NATs),基於UDP,關於STUN介紹不再贅述,想要了解的話可以參考stun協議RFC 3489。
Resiprocate可能用到STUN的場合
一、proxy架設在內網,需要獲得proxy外網IP(也可以用DNS實現);二、實現兩個內網客戶端SIP通信。客戶端支持Stun,路由器類型屬於完全圓錐NAT,地址受限NAT,或端口受限NAT,可以利用stun穿越。或者是通過路由器端口映射。
NAT
檢測NAT類型步驟
NAT的四種類型
Full cone NAT,亦即著名的一對一(one-to-one) NAT
|
|
Address-Restricted cone NAT
|
|
Port-Restricted cone NAT 類似受限制錐形NAT(Restricted cone NAT),但是還有端口限制。
|
|
Symmetric NAT(對稱NAT)
|
|
Resiprocate對STUN的支持
Resiprocate使用了Vovida的Stun庫。Resiprocate對Stun的支持可以參考STUN support,說的比較詳盡。簡單來說,Resiprocate對Stun支持全在UdpTransport:
UdpTransport::process接收監聽端口數據封包包括了Stunresponse, Stun request以及SIP message。
UdpTransport::stunSendTest發送Stun請求。
UdpTransport::stunResult輸出外網地址。
如果作爲Stun Client,需要當添加transport的時候保存返回的UdpTransport指針。發送Stun請求,並獲得外網地址是異步的,如果需要同步的話,如下使用:
boolstunSendTest(const Tuple& dest);
bool stunResult(Tuple& mappedAddress);
voidSendStunTest()
{
if (!m_pUdpTransport) return;
hostent* h =gethostbyname(STUNServer);
in_addr sin_addr = *(structin_addr*)h->h_addr;
resip::Tuple tStunDest(sin_addr,STUNPort, UDP, Data::Empty);
m_pUdpTransport->stunSendTest(tStunDest);
mLastStunTestTime= GetTickCount();
}
resip::TupleGetStunAddress()
{
resip::Tuple mMappedAddress;
mMappedAddress.setPort(0);
if(!m_pUdpTransport) return mMappedAddress;
if(!m_pUdpTransport->stunResult(mMappedAddress))
{
// no valid result available,send another request
SendStunTest();
}
else if((GetTickCount() - mLastStunTestTime) > 1000 * 60 * 3)
{
// don't use a STUN responsethat is older than 3 minutes
SendStunTest();
}
DWORDdwTmpLastStunTestTime = mLastStunTestTime;
while((GetTickCount() - dwTmpLastStunTestTime) < 5 * 1000) // wait 5s forresult
{
if(m_pUdpTransport->stunResult(mMappedAddress))
break;
Sleep(200);
}
returnmMappedAddress;
}
附加我自己的用法,實現proxy假設在內網的情況下,獲得外網IP:
UdpTransport::process
//每30s向mStunServer發送stun請求,發送失敗的話,使用DNS(mStunServer爲域名)
if(mUseStun)
{
static UInt64last=Timer::getTimeMs();
UInt64 now=Timer::getTimeMs();
if(now-last>30000) //30s 執行一次
{
hostent* h =gethostbyname(mStunServer);
if(h)
{
in_addr sin_addr = *(structin_addr*)h->h_addr;
resip::Tupledest(sin_addr, 3478, UDP,Data::Empty);
if(!stunSendTest(dest))
{
h= gethostbyname(mDomainName.c_str());
if(h)
{
mStunSuccess= true;
sin_addr= *(struct in_addr*)h->h_addr;
mStunMappedAddress= Tuple(sin_addr,mStunMappedAddress.getPort(), UDP);
}
}
}
last=Timer::getTimeMs();
}
}
獲得外網IP,外網IP存儲在mExternalIP。
resip::SdpContents*pSipSdp = dynamic_cast<resip::SdpContents*>(msg->getContents());
if ( pSipSdp )
{
UdpTransport* temp =dynamic_cast<UdpTransport*>(target.transport);
if(pSipSdp->session().origin().getAddress()== temp->mDomainName ||
pSipSdp->session().connection().getAddress() == temp->mDomainName)
{
Tupletuple;
boolbRet = temp->stunResult(tuple);
DataexternalIP = Tuple::inet_ntop(tuple);
ErrLog(<<"StunResultret "<<(bRet?"true. ":"false.")<<"local IP change to "<<externalIP);
if(bRet)
{
externalIP = Tuple::inet_ntop(tuple);
if (pSipSdp->session().connection().getAddress() == "0.0.0.0" )
{
//只修改
pSipSdp->session().origin().setAddress(externalIP);
}else
{
pSipSdp->session().origin().setAddress(externalIP);
pSipSdp->session().connection().setAddress(externalIP);
}
mExternalIp = externalIP;
}elseif(!mExternalIp.empty())
{
if (pSipSdp->session().connection().getAddress() == "0.0.0.0" )
{
//只修改
pSipSdp->session().origin().setAddress(mExternalIp);
}else
{
pSipSdp->session().origin().setAddress(mExternalIp);
pSipSdp->session().connection().setAddress(mExternalIp);
}
}
}
}