📄 dtlstransport.cxx
字号:
#ifdef USE_DTLS
#if defined(HAVE_CONFIG_H)
#include "resip/stack/config.hxx"
#endif
#include <memory>
#ifndef RESIP_COMPAT_HXX
#include "rutil/compat.hxx"
#endif
#ifndef RESIP_DATA_HXX
#include "rutil/Data.hxx"
#endif
#ifndef RESIP_DNSUTIL_HXX
#include "rutil/DnsUtil.hxx"
#endif
#ifndef RESIP_SOCKET_HXX
#include "rutil/Socket.hxx"
#endif
#ifndef RESIP_LOGGER_HXX
#include "rutil/Logger.hxx"
#endif
#ifndef RESIP_SIPMESSAGE_HXX
#include "resip/stack/SipMessage.hxx"
#endif
#ifndef RESIP_HELPER_HXX
#include "resip/stack/Helper.hxx"
#endif
#ifndef RESIP_SECURITY_HXX
#include "resip/stack/Security.hxx"
#endif
#ifndef RESIP_DTLSMESSAGE_HXX
#include "resip/stack/DtlsMessage.hxx"
#endif
#ifndef RESIP_DTLSTRANSPORT_HXX
#include "resip/stack/DtlsTransport.hxx"
#endif
#include "rutil/WinLeakCheck.hxx"
#include <openssl/e_os2.h>
#include <openssl/evp.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/x509v3.h>
#include <openssl/ssl.h>
#ifdef USE_SIGCOMP
#include <osc/Stack.h>
#include <osc/StateChanges.h>
#include <osc/SigcompMessage.h>
#endif
#define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT
using namespace std;
using namespace resip;
DtlsTransport::DtlsTransport(Fifo<TransactionMessage>& fifo,
int portNum,
IpVersion version,
const Data& interfaceObj,
Security& security,
const Data& sipDomain,
Compression& compression)
: UdpTransport( fifo, portNum, version,
StunDisabled, interfaceObj, 0, compression ),
mTimer( mHandshakePending ),
mSecurity( &security ),
mDomain(sipDomain)
{
setTlsDomain(sipDomain);
InfoLog ( << "Creating DTLS transport host=" << interfaceObj
<< " port=" << portNum
<< " ipv4=" << version ) ;
mTuple.setType( transport() );
mClientCtx = SSL_CTX_new( DTLSv1_client_method() ) ;
mServerCtx = SSL_CTX_new( DTLSv1_server_method() ) ;
assert( mClientCtx ) ;
assert( mServerCtx ) ;
mDummyBio = BIO_new( BIO_s_mem() ) ;
assert( mDummyBio ) ;
mSendData = NULL ;
/* trying to read from this BIO always returns retry */
BIO_set_mem_eof_return( mDummyBio, -1 ) ;
}
DtlsTransport::~DtlsTransport()
{
DebugLog (<< "Shutting down " << mTuple);
while(mDtlsConnections.begin() != mDtlsConnections.end())
{
_cleanupConnectionState(mDtlsConnections.begin()->second, mDtlsConnections.begin()->first);
}
SSL_CTX_free(mClientCtx);mClientCtx=0;
SSL_CTX_free(mServerCtx);mServerCtx=0;
BIO_free( mDummyBio) ;
}
void
DtlsTransport::_read( FdSet& fdset )
{
//should this buffer be allocated on the stack and then copied out, as it
//needs to be deleted every time EWOULDBLOCK is encountered
// .dlb. can we determine the size of the buffer before we allocate?
// something about MSG_PEEK|MSG_TRUNC in Stevens..
// .dlb. RFC3261 18.1.1 MUST accept 65K datagrams. would have to attempt to
// adjust the UDP buffer as well...
unsigned int bufferLen = UdpTransport::MaxBufferSize + 5 ;
char* buffer = new char[ bufferLen ] ;
unsigned char *pt = new unsigned char[ bufferLen ] ;
SSL *ssl ;
BIO *rbio ;
BIO *wbio ;
// !jf! how do we tell if it discarded bytes
// !ah! we use the len-1 trick :-(
Tuple tuple(mTuple) ;
socklen_t slen = tuple.length() ;
int len = recvfrom( mFd,
buffer,
UdpTransport::MaxBufferSize,
0 /*flags */,
&tuple.getMutableSockaddr(),
&slen ) ;
if ( len == SOCKET_ERROR )
{
int err = getErrno() ;
if ( err != EWOULDBLOCK )
{
error( err ) ;
}
}
if (len == 0 || len == SOCKET_ERROR)
{
delete [] buffer ;
buffer = 0 ;
return ;
}
if ( len + 1 >= UdpTransport::MaxBufferSize )
{
InfoLog (<<"Datagram exceeded max length "<<UdpTransport::MaxBufferSize ) ;
delete [] buffer ; buffer = 0 ;
return ;
}
//DebugLog ( << "UDP Rcv : " << len << " b" );
//DebugLog ( << Data(buffer, len).escaped().c_str());
/* begin SSL stuff */
struct sockaddr peer = tuple.getMutableSockaddr() ;
ssl = mDtlsConnections[ *((struct sockaddr_in *)&peer) ] ;
/*
* If we don't have a binding for this peer,
* then we're a server.
*/
if ( ssl == NULL )
{
ssl = SSL_new( mServerCtx ) ;
assert( ssl ) ;
SSL_set_accept_state( ssl ) ;
X509 *cert = mSecurity->getDomainCert( mDomain ) ;
EVP_PKEY *pkey = mSecurity->getDomainKey( mDomain ) ;
if( !cert )
{
Data error = Data("Could not load certifiacte for domain: ")
+ mDomain;
throw Security::Exception( error,__FILE__, __LINE__ ) ;
}
if( !pkey )
{
Data error = Data("Could not load private key for domain: ")
+ mDomain;
throw Security::Exception( error,__FILE__, __LINE__ ) ;
}
assert( cert ) ;
assert( pkey ) ;
if( ! SSL_use_certificate( ssl, cert ) )
{
throw Security::Exception( "SSL_use_certificate failed",
__FILE__, __LINE__ ) ;
}
if ( ! SSL_use_PrivateKey( ssl, pkey ) )
throw Security::Exception( "SSL_use_PrivateKey failed.",
__FILE__, __LINE__ ) ;
wbio = BIO_new_dgram( mFd, BIO_NOCLOSE ) ;
assert( wbio ) ;
BIO_dgram_set_peer( wbio, &peer ) ;
SSL_set_bio( ssl, NULL, wbio ) ;
/* remember this connection */
mDtlsConnections[ *((struct sockaddr_in *)&peer) ] = ssl ;
}
rbio = BIO_new_mem_buf( buffer, len ) ;
BIO_set_mem_eof_return( rbio, -1 ) ;
ssl->rbio = rbio ;
len = SSL_read( ssl, pt, UdpTransport::MaxBufferSize ) ;
int err = SSL_get_error( ssl, len ) ;
/* done with the rbio */
BIO_free( ssl->rbio ) ;
ssl->rbio = mDummyBio ;
delete [] buffer ;
buffer = 0 ;
if ( len <= 0 )
{
switch( err )
{
case SSL_ERROR_NONE:
break ;
case SSL_ERROR_SSL:
break ;
case SSL_ERROR_WANT_READ:
break ;
case SSL_ERROR_WANT_WRITE:
break ;
case SSL_ERROR_SYSCALL:
break ;
/* connection closed */
case SSL_ERROR_ZERO_RETURN:
_cleanupConnectionState( ssl, *((struct sockaddr_in *)&peer) ) ;
break ;
case SSL_ERROR_WANT_CONNECT:
break ;
case SSL_ERROR_WANT_ACCEPT:
break ;
default:
break ;
}
}
if ( len <= 0 )
return ;
if ( SSL_in_init( ssl ) )
mTimer.add( ssl, DtlsReceiveTimeout ) ;
#ifdef USE_SIGCOMP
osc::StateChanges *sc = 0;
#endif
if ((pt[0] & 0xf8) == 0xf8)
{
if(!mCompression.isEnabled())
{
InfoLog(<< "Discarding unexpected SigComp message");
delete [] pt;
return;
}
#ifdef USE_SIGCOMP
unsigned char *newPt = new unsigned char[ bufferLen ] ;
size_t uncompressedLength =
mSigcompStack->uncompressMessage(pt, len,
newPt, UdpTransport::MaxBufferSize,
sc);
DebugLog (<< "Unompressed message from "
<< len << " bytes to "
<< uncompressedLength << " bytes");
osc::SigcompMessage *nack = mSigcompStack->getNack();
if (nack)
{
mTxFifo.add(new SendData(tuple,
Data(nack->getDatagramMessage(),
nack->getDatagramLength()),
Data::Empty,
Data::Empty,
true)
);
delete nack;
}
delete[] buffer;
buffer = newBuffer;
len = uncompressedLength;
#endif
}
SipMessage* message = new SipMessage(this);
// set the received from information into the received= parameter in the
// via
// It is presumed that UDP Datagrams are arriving atomically and that
// each one is a unique SIP message
// Save all the info where this message came from
tuple.transport = this ;
message->setSource( tuple ) ;
//DebugLog (<< "Received from: " << tuple);
// Tell the SipMessage about this datagram buffer.
message->addBuffer( (char *)pt ) ;
mMsgHeaderScanner.prepareForMessage( message ) ;
char *unprocessedCharPtr ;
if (mMsgHeaderScanner.scanChunk( (char *)pt,
len,
&unprocessedCharPtr ) !=
MsgHeaderScanner::scrEnd)
{
DebugLog( << "Scanner rejecting datagram as unparsable / fragmented from "
<< tuple ) ;
DebugLog( << Data( pt, len ) ) ;
delete message ;
message = 0 ;
return ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -