📄 transportselector.cxx
字号:
// !kh!
// 1. Query local hostname.
char hostname[256] = "";
if(gethostname(hostname, sizeof(hostname)) != 0)
{
int e = getErrno();
Transport::error( e );
InfoLog(<< "Can't query local hostname : [" << e << "] " << strerror(e) );
throw Transport::Exception("Can't query local hostname", __FILE__, __LINE__);
}
InfoLog(<< "Local hostname is [" << hostname << "]");
// !kh!
// 2. Resolve address(es) of local hostname for specified transport.
const bool is_dgram = isDgramTransport(type);
addrinfo hint;
memset(&hint, 0, sizeof(hint));
hint.ai_family = is_v4 ? PF_INET : PF_INET6;
hint.ai_flags = AI_PASSIVE;
hint.ai_socktype = is_dgram ? SOCK_DGRAM : SOCK_STREAM;
addrinfo* results;
int ret = getaddrinfo(
hostname,
0,
&hint,
&results);
if(ret != 0)
{
Transport::error( ret ); // !kh! is this the correct sematics? ret is not errno.
InfoLog(<< "Can't resolve " << hostname << "'s address : [" << ret << "] " << gai_strerror(ret) );
throw Transport::Exception("Can't resolve hostname", __FILE__,__LINE__);
}
// !kh!
// 3. Use first address resolved if there are more than one.
// What should I do if there are more than one address?
// i.e. results->ai_next != 0.
Tuple source(*(results->ai_addr), type);
InfoLog(<< "Local address is " << source);
addrinfo* ai = results->ai_next;
for(; ai; ai = ai->ai_next)
{
Tuple addr(*(ai->ai_addr), type);
InfoLog(<<"Additional address " << addr);
}
freeaddrinfo(results);
return source;
#endif
}
Tuple
TransportSelector::determineSourceInterface(SipMessage* msg, const Tuple& target) const
{
assert(msg->exists(h_Vias));
assert(!msg->header(h_Vias).empty());
const Via& via = msg->header(h_Vias).front();
if (msg->isRequest() && !via.sentHost().empty())
// hint provided in sent-by of via by application
{
DebugLog( << "hint provided by app: " << msg->header(h_Vias).front());
return Tuple(via.sentHost(), via.sentPort(), target.ipVersion(), target.getType());
}
else
{
Tuple source(target);
#if defined(WIN32) && !defined(NO_IPHLPAPI)
try
{
source.getMutableSockaddr() = WinCompat::determineSourceInterface(target.toGenericIPAddress()).address;
}
catch (WinCompat::Exception&)
{
ErrLog (<< "Can't find source interface to use");
throw Transport::Exception("Can't find source interface", __FILE__, __LINE__);
}
#else
// !kh!
// The connected UDP technique doesn't work all the time.
// 1. Might not work on all implementaions as stated in UNP vol.1 8.14.
// 2. Might not work under unspecified condition on Windows,
// search "getsockname" in MSDN library.
// 3. We've experienced this issue on our production software.
// this process will determine which interface the kernel would use to
// send a packet to the target by making a connect call on a udp socket.
Socket tmp = INVALID_SOCKET;
if (target.isV4())
{
if (mSocket == INVALID_SOCKET)
{
mSocket = InternalTransport::socket(UDP, V4); // may throw
}
tmp = mSocket;
}
else
{
if (mSocket6 == INVALID_SOCKET)
{
mSocket6 = InternalTransport::socket(UDP, V6); // may throw
}
tmp = mSocket6;
}
int ret = connect(tmp,&target.getSockaddr(), target.length());
if (ret < 0)
{
int e = getErrno();
Transport::error( e );
InfoLog(<< "Unable to route to " << target << " : [" << e << "] " << strerror(e) );
throw Transport::Exception("Can't find source address for Via", __FILE__,__LINE__);
}
socklen_t len = source.length();
ret = getsockname(tmp,&source.getMutableSockaddr(), &len);
if (ret < 0)
{
int e = getErrno();
Transport::error(e);
InfoLog(<< "Can't determine name of socket " << target << " : " << strerror(e) );
throw Transport::Exception("Can't find source address for Via", __FILE__,__LINE__);
}
// !kh! test if connected UDP technique results INADDR_ANY, i.e. 0.0.0.0.
// if it does, assume the first avaiable interface.
if(source.isV4())
{
long src = (reinterpret_cast<const sockaddr_in*>(&source.getSockaddr())->sin_addr.s_addr);
if(src == INADDR_ANY)
{
InfoLog(<< "Connected UDP failed to determine source address, use first address instaed.");
source = getFirstInterface(true, target.getType());
}
}
else // IPv6
{
//should never reach here in WIN32 w/ V6 support
#if defined(USE_IPV6) && !defined(WIN32)
if (source.isAnyInterface()) //!dcm! -- when could this happen?
{
source = getFirstInterface(false, target.getType());
}
# endif
}
// Unconnect.
// !jf! This is necessary, but I am not sure what we can do if this
// fails. I'm not sure the stack can recover from this error condition.
if (target.isV4())
{
ret = connect(mSocket,
(struct sockaddr*)&mUnspecified.v4Address,
sizeof(mUnspecified.v4Address));
}
#ifdef USE_IPV6
else
{
ret = connect(mSocket6,
(struct sockaddr*)&mUnspecified6.v6Address,
sizeof(mUnspecified6.v6Address));
}
#else
else
{
assert(0);
}
#endif
if ( ret<0 )
{
int e = getErrno();
if ( e != EAFNOSUPPORT )
{
ErrLog(<< "Can't disconnect socket : " << strerror(e) );
Transport::error(e);
throw Transport::Exception("Can't disconnect socket", __FILE__,__LINE__);
}
}
#endif
// This is the port that the request will get sent out from. By default,
// this value will be 0, since the Helper that creates the request will not
// assign it. In this case, the stack will pick an arbitrary (but appropriate)
// transport. If it is non-zero, it will only match transports that are bound to
// the specified port (and fail if none are available)
if(msg->isRequest())
{
source.setPort(via.sentPort());
}
else
{
source.setPort(0);
}
DebugLog (<< "Looked up source for destination: " << target
<< " -> " << source
<< " sent-by=" << via.sentHost()
<< " sent-port=" << via.sentPort());
return source;
}
}
// !jf! there may be an extra copy of a tuple here. can probably get rid of it
// but there are some const issues.
void
TransportSelector::transmit(SipMessage* msg, Tuple& target)
{
assert(msg);
try
{
// !ah! You NEED to do this for responses too -- the transport doesn't
// !ah! know it's IP addres(es) in all cases, AND it's function of the dest.
// (imagine a synthetic message...)
Tuple source;
// !bwc! We need 3 things here:
// 1) A Transport* to call send() on.
// 2) A complete Tuple to pass in this call (target).
// 3) A host, port, and protocol for filling out the topmost via (source)
/*
Our Transport* might be found in target. If so, we can skip this block
of stuff.
Alternatively, we might not have the transport to start with. However,
given a connection id, we will be able to find the Connection we
should use, we can get the Transport we want. If we have no connection
id, but we know we are using TLS or DTLS and have a tls hostname, we
can use the hostname to find the appropriate transport. If all else
fails, we must resort to the connected UDP trick to fill out source,
which in turn is used to look up a matching transport.
Given the transport, it is always possible to get the port/protocol,
and usually possible to get the host (if it is bound to INADDR_ANY, we
can't tell). However, if we had to fill out source in order to find
the transport in the first place, this is not an issue.
*/
Data remoteSigcompId;
if (msg->isRequest())
{
Transport* transport=0;
transport = findTransportByDest(msg,target);
// !bwc! Here we use transport to find source.
if(transport)
{
source = transport->getTuple();
//!bwc! If the transport has an ambiguous interface, we need to
//look a little closer.
if(source.isAnyInterface())
{
Tuple temp = determineSourceInterface(msg,target);
/* determineSourceInterface can return 0 in the port as default
to let the stack pick an "arbitrary (but appropriate) transport"
so we'll assert that the port is 0 _or_ the same as source. (mjf)
*/
assert((source.getPort()==temp.getPort() ||
temp.getPort()==0) &&
source.ipVersion()==temp.ipVersion() &&
source.getType()==temp.getType());
source=temp;
/* determineSourceInterface will return an arbitrary port here,
so use the port specified in target.transport->port().
*/
source.setPort(transport->port());
}
}
// !bwc! Here we use source to find transport.
else
{
source = determineSourceInterface(msg, target);
transport = findTransportBySource(source);
// !bwc! determineSourceInterface doesn't give us a port
if(transport)
{
source.setPort(transport->port());
}
}
target.transport=transport;
// !bwc! Topmost Via is only filled out in the request case. Also, if
// we don't have a transport at this point, we're going to fail,
// so don't bother doing the work.
if(target.transport)
{
Via& topVia(msg->header(h_Vias).front());
topVia.remove(p_maddr); // !jf! why do this?
// insert the via
if (topVia.transport().empty())
{
topVia.transport() = Tuple::toData(source.getType());
}
if (!topVia.sentHost().size())
{
msg->header(h_Vias).front().sentHost() = Tuple::inet_ntop(source);
}
if (!topVia.sentPort())
{
msg->header(h_Vias).front().sentPort() = source.getPort();
}
if (mCompression.isEnabled())
{
// Indicate support for SigComp, if appropriate.
if (!topVia.exists(p_comp))
{
topVia.param(p_comp) = "sigcomp";
}
if (!topVia.exists(p_sigcompId))
{
topVia.param(p_sigcompId) = mCompression.getSigcompId();
}
// Figure out remote identifier (from Route header
// field, if present; otherwise, from Request-URI).
// XXX rohc-sip-sigcomp-03 says to use +sip.instance,
// but this is impossible to actually do if you're
// not actually the registrar, and really hard even
// if you are.
Uri& destination(*reinterpret_cast<Uri*>(0));
if(msg->exists(h_Routes) &&
!msg->header(h_Routes).empty())
{
destination = msg->header(h_Routes).front().uri();
}
else
{
destination = msg->header(h_RequestLine).uri();
}
if (destination.exists(p_comp) &&
destination.param(p_comp) == "sigcomp")
{
if (destination.exists(p_sigcompId))
{
remoteSigcompId = destination.param(p_sigcompId);
}
else
{
remoteSigcompId = destination.host();
}
}
// Squirrel the compartment away in the branch so that
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -