reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
TransportSelector.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
5#ifdef WIN32
6#include <winsock2.h>
7#include <ws2tcpip.h>
8#include <wspiapi.h> // Required for freeaddrinfo implementation in Windows 2000, NT, Me/95/98
9#else
10#include <sys/types.h>
11#include <sys/socket.h>
12#include <arpa/inet.h>
13#include <netdb.h>
14#endif
15
17#include "resip/stack/Uri.hxx"
18
30#include "resip/stack/Uri.hxx"
31
32#include "rutil/DataStream.hxx"
33#include "rutil/DnsUtil.hxx"
34#include "rutil/Inserter.hxx"
35#include "rutil/Logger.hxx"
36#include "rutil/Socket.hxx"
37#include "rutil/FdPoll.hxx"
39#include "rutil/dns/DnsStub.hxx"
40#ifdef USE_NETNS
41# include "rutil/NetNs.hxx"
42#endif
43#ifdef USE_SIGCOMP
44#include <osc/Stack.h>
45#include <osc/SigcompMessage.h>
46#endif
47
48#ifdef USE_SSL
53#endif
54
55#ifdef WIN32
56#include "rutil/WinCompat.hxx"
57#endif
58
59#ifdef __MINGW32__
60#define gai_strerror strerror
61#endif
62
63#include <utility>
64#include <sys/types.h>
65
66using namespace resip;
67
68#define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT
69
71 mTuple(tuple)
72{
73 mTuple.setTargetDomain(domainName);
74}
75
76
77
78bool
80{
81 if(mTuple.getTargetDomain() < rhs.mTuple.getTargetDomain())
82 {
83 return true;
84 }
85 else if(mTuple.getTargetDomain() == rhs.mTuple.getTargetDomain())
86 {
87 return mTuple < rhs.mTuple;
88 }
89 return false;
90}
91
92TransportSelector::TransportSelector(Fifo<TransactionMessage>& fifo, Security* security, DnsStub& dnsStub, Compression &compression, bool useDnsVip) :
93 mDns(dnsStub, useDnsVip),
94 mStateMacFifo(fifo),
95 mSecurity(security),
96 mCompression(compression),
97 mSigcompStack (0),
98 mPollGrp(0),
99 mAvgBufferSize(1024),
100 mInterruptorHandle(0)
101{
102 memset(&mUnspecified.v4Address, 0, sizeof(sockaddr_in));
103 mUnspecified.v4Address.sin_family = AF_UNSPEC;
104
105#ifdef USE_IPV6
106 memset(&mUnspecified6.v6Address, 0, sizeof(sockaddr_in6));
107 mUnspecified6.v6Address.sin6_family = AF_UNSPEC;
108#endif
109
110#ifdef USE_SIGCOMP
111 if (mCompression.isEnabled())
112 {
113 DebugLog (<< "Compression enabled for Transport Selector");
114 mSigcompStack = new osc::Stack(mCompression.getStateHandler());
115 mCompression.addCompressorsToStack(mSigcompStack);
116 }
117 else
118 {
119 DebugLog (<< "Compression disabled for Transport Selector");
120 }
121#else
122 DebugLog (<< "No compression library available");
123#endif
124}
125
127{
128 mExactTransports.clear();
129 mAnyInterfaceTransports.clear();
130 mAnyPortTransports.clear();
131 mAnyPortAnyInterfaceTransports.clear();
132 mTlsTransports.clear();
133 mSharedProcessTransports.clear();
134 mHasOwnProcessTransports.clear();
135 mTypeToTransportMap.clear();
136 for(TransportKeyMap::iterator it = mTransports.begin(); it != mTransports.end(); it++)
137 {
138 delete it->second;
139 }
140#ifdef USE_SIGCOMP
141 delete mSigcompStack;
142#endif
143
144 for(HashMap<Data, Socket>::iterator socketIterator = mSockets.begin();
145 socketIterator != mSockets.end(); socketIterator++)
146 {
147 if (socketIterator->second != INVALID_SOCKET)
148 {
149 closeSocket(socketIterator->second);
150 DebugLog(<< "Closing TransportSelector::mSocket[" << socketIterator->first << "]");
151 }
152 }
153
154 for(HashMap<Data, Socket>::iterator socketIterator = mSocket6s.begin();
155 socketIterator != mSocket6s.end(); socketIterator++)
156 {
157 if (socketIterator->second != INVALID_SOCKET)
158 {
159 closeSocket(socketIterator->second);
160 DebugLog(<< "Closing TransportSelector::mSocket6[" << socketIterator->first << "]");
161 }
162 }
163
164 setPollGrp(0);
165}
166
167void
169{
170 for(TransportKeyMap::iterator it = mTransports.begin(); it != mTransports.end(); it++)
171 {
172 it->second->shutdown();
173 }
174}
175
176bool
178{
179 for(TransportKeyMap::const_iterator it = mTransports.begin(); it != mTransports.end(); it++)
180 {
181 if (!it->second->isFinished())
182 {
183 return false;
184 }
185 }
186 return true;
187}
188
189void
190TransportSelector::addTransport(std::unique_ptr<Transport> autoTransport, bool isStackRunning)
191{
192 Transport* transport = autoTransport.release();
193
194 // !bwc! This is a multimap from TransportType/IpVersion to Transport*.
195 // Make _extra_ sure that no garbage goes in here.
196 if(transport->transport()==TCP)
197 {
198 resip_assert(dynamic_cast<TcpTransport*>(transport));
199 }
200#ifdef USE_SSL
201 else if(transport->transport()==TLS)
202 {
203 resip_assert(dynamic_cast<TlsTransport*>(transport));
204 }
205#endif
206 else if(transport->transport()==UDP)
207 {
208 resip_assert(dynamic_cast<UdpTransport*>(transport));
209 }
210#ifdef USE_DTLS
211#ifdef USE_SSL
212 else if(transport->transport()==DTLS)
213 {
214 resip_assert(dynamic_cast<DtlsTransport*>(transport));
215 }
216#endif
217#endif
218 else if(transport->transport()==WS)
219 {
220 resip_assert(dynamic_cast<WsTransport*>(transport));
221 }
222#ifdef USE_SSL
223 else if(transport->transport()==WSS)
224 {
225 resip_assert(dynamic_cast<WssTransport*>(transport));
226 }
227#endif
228 else
229 {
230 resip_assert(0);
231 }
232
233 Tuple tuple(transport->interfaceName(), transport->port(),
234 transport->ipVersion(), transport->transport(),
235 Data::Empty, // Domain
236 transport->netNs());
237 tuple.mTransportKey = transport->getKey();
238
239 if(!isSecure(transport->transport()))
240 {
241 if(mExactTransports.find(tuple) == mExactTransports.end() &&
242 mAnyInterfaceTransports.find(tuple) == mAnyInterfaceTransports.end())
243 {
244 DebugLog (<< "Adding transport: " << tuple);
245
246 // Store the transport in the ANY interface maps if the tuple specifies ANY
247 // interface. Store the transport in the specific interface maps if the tuple
248 // specifies an interface. See TransportSelector::findTransport.
249 if (transport->interfaceName().empty() ||
250 transport->getTuple().isAnyInterface() ||
251 transport->hasSpecificContact() )
252 {
253 mAnyInterfaceTransports[tuple] = transport;
254 mAnyPortAnyInterfaceTransports[tuple] = transport;
255 }
256 else
257 {
258 mExactTransports[tuple] = transport;
259 mAnyPortTransports[tuple] = transport;
260 }
261 }
262 else
263 {
264 WarningLog (<< "Can't add transport, overlapping properties with existing transport: " << tuple);
265 resip_assert(false); // should never get here - checked in SipStack first
266 return;
267 }
268 }
269 else
270 {
271 TlsTransportKey key(transport->tlsDomain(), tuple);
272 if(mTlsTransports.find(key) == mTlsTransports.end())
273 {
274 mTlsTransports[key] = transport;
275 }
276 else
277 {
278 WarningLog (<< "Can't add transport, overlapping properties with existing transport: "
279 << tuple << " (domain=" << transport->tlsDomain() << ")");
280 resip_assert(false); // should never get here - checked in SipStack first
281 return;
282 }
283 }
284
285 if (transport->shareStackProcessAndSelect())
286 {
287 if ( mPollGrp )
288 {
289 transport->setPollGrp(mPollGrp);
290 }
291 if(isStackRunning)
292 {
293 // When stack is running we need to add to mSharedProcessTransports from
294 // within TransportSelectorThread in order to be thread safe
295 mTransportsToAddRemove.add(transport);
296 }
297 else
298 {
299 // Stack isn't running yet - just add to mSharedProcess directly - this
300 // ensures the mSharedProcessTransports list is propulated when the stack
301 // starts to run. When in multi-threaded stack mode the TransportSelectorThread
302 // will assume ownership (FdPollGrp control) of all the transports using this list.
303 mSharedProcessTransports.push_back(transport);
304 }
305 }
306 else
307 {
308 mHasOwnProcessTransports.push_back(transport);
309 mHasOwnProcessTransports.back()->startOwnProcessing();
310 }
311
312 mTypeToTransportMap.insert(TypeToTransportMap::value_type(tuple,transport));
313 mDns.addTransportType(transport->transport(), transport->ipVersion());
314 mTransports[transport->getKey()] = transport;
315
316 InfoLog(<< "TransportSelector::addTransport: added transport for tuple=" << tuple << ", key=" << transport->getKey());
317}
318
319void
320TransportSelector::removeTransport(unsigned int transportKey)
321{
322 Transport* transportToRemove = 0;
323
324 // Find transport in global map and remove it
325 // Note: it is important that this map is removed from before rebuildAnyPortTransportMaps is called,
326 // since rebuildAnyPortTransportMaps uses this map.
327 TransportKeyMap::iterator it = mTransports.find(transportKey);
328 if(it != mTransports.end())
329 {
330 transportToRemove = it->second;
331 mTransports.erase(it);
332 }
333
334 // If we found the transport - continue removal from other maps
335 if(transportToRemove)
336 {
337 // notify transport to shutdown
338 transportToRemove->shutdown();
339
340 if(!isSecure(transportToRemove->transport()))
341 {
342 // Ensure transport is removed from all containers
343 mExactTransports.erase(transportToRemove->getTuple());
344 mAnyInterfaceTransports.erase(transportToRemove->getTuple());
345
346 // In the AnyPort maps 2 transports can end up overwriting each other in these maps - then when we remove one, there may be none left - even though we should have an
347 // entry. The rebuilt method will dig through all transports again and rebuild these maps.
348 rebuildAnyPortTransportMaps();
349 }
350 else
351 {
352 TlsTransportKey tlsKey(transportToRemove->tlsDomain(), transportToRemove->getTuple());
353 mTlsTransports.erase(tlsKey);
354 }
355
356 // mTypeToTransportMap is a multimap - make sure to delete only this instance by looking up transportKey, instead of using
357 // mTypeToTransportMap.erase(transportToRemove->getTuple()); which might end up deleting more than 1 transport
358 for (TypeToTransportMap::iterator itTypeToTransport = mTypeToTransportMap.begin(); itTypeToTransport != mTypeToTransportMap.end(); itTypeToTransport++)
359 {
360 if (itTypeToTransport->second->getKey() == transportKey)
361 {
362 mTypeToTransportMap.erase(itTypeToTransport);
363 break;
364 }
365 }
366
367 // Remove transport types from Dns list of supported protocols
368 // Note: DNS tracks use counts so that we will only remove this transport type if this is the last of the type to be removed
369 mDns.removeTransportType(transportToRemove->transport(), transportToRemove->ipVersion());
370
371 if (transportToRemove->shareStackProcessAndSelect())
372 {
373 // Note: We called shutdown above, therefor the TransportSelectorThread can tell this
374 // is a remove transpot request vs an addTransport request, by calling isShuttingDown().
375 mTransportsToAddRemove.add(transportToRemove); // Need to add to mSharedProcessTransports from within TransportSelectorThread
376 // Note: Transport will be deleted from mTransportsToAddRemove handler (see checkTransportAddRemoveQueue)
377 }
378 else
379 {
380 for(TransportList::iterator it = mHasOwnProcessTransports.begin(); it != mHasOwnProcessTransports.end(); it++)
381 {
382 if((*it)->getKey() == transportKey)
383 {
384 mHasOwnProcessTransports.erase(it);
385 break;
386 }
387 }
388
389 // Delete transport
390 delete transportToRemove;
391 }
392 }
393}
394
395void
396TransportSelector::rebuildAnyPortTransportMaps()
397{
398 // These maps may contain less transports than what exists in the mTransports map, due to the fact that multiple transports can
399 // have the same index. In these cases the last transport added that matches the custom map compare function is the only one that ends
400 // up in these maps. Therefor we cannot just simply remove items and expect transprot selection to work as expected.
401 // We will clear these maps here. Iterate through the master transport list and rebuild them back up. This isn't very efficient,
402 // but it only occurs when a transport is removed.
403
404 mAnyPortTransports.clear();
405 mAnyPortAnyInterfaceTransports.clear();
406
407 for (TransportKeyMap::iterator it = mTransports.begin(); it != mTransports.end(); it++)
408 {
409 if (!isSecure(it->second->transport()))
410 {
411 // Store the transport in the ANY interface maps if the tuple specifies ANY
412 // interface. Store the transport in the specific interface maps if the tuple
413 // specifies an interface. See TransportSelector::findTransport.
414 if (it->second->interfaceName().empty() ||
415 it->second->getTuple().isAnyInterface() ||
416 it->second->hasSpecificContact())
417 {
418 mAnyPortAnyInterfaceTransports[it->second->getTuple()] = it->second;
419 }
420 else
421 {
422 mAnyPortTransports[it->second->getTuple()] = it->second;
423 }
424 }
425 }
426}
427
428void
430{
431 if(mPollGrp && mInterruptorHandle)
432 {
433 // unregister our select interruptor
434 mPollGrp->delPollItem(mInterruptorHandle);
435 mInterruptorHandle=0;
436 }
437
438 mPollGrp = grp;
439
440 if(mPollGrp && mSelectInterruptor.get())
441 {
442 mInterruptorHandle = mPollGrp->addPollItem(mSelectInterruptor->getReadSocket(), FPEM_Read, mSelectInterruptor.get());
443 }
444
445 for(TransportList::iterator t=mSharedProcessTransports.begin(); t!=mSharedProcessTransports.end(); ++t)
446 {
447 (*t)->setPollGrp(mPollGrp);
448 }
449}
450
451void
453{
454 if(!mSelectInterruptor.get())
455 {
456 mSelectInterruptor.reset(new SelectInterruptor);
457 if(mPollGrp)
458 {
459 mInterruptorHandle = mPollGrp->addPollItem(mSelectInterruptor->getReadSocket(), FPEM_Read, mSelectInterruptor.get());
460 }
461 }
462}
463
464void
466{
467 for(TransportList::iterator it = mSharedProcessTransports.begin(); it != mSharedProcessTransports.end(); it++)
468 {
469 (*it)->buildFdSet(fdset);
470 }
471 if(mSelectInterruptor.get())
472 {
473 mSelectInterruptor->buildFdSet(fdset);
474 }
475}
476
477void
479{
480 checkTransportAddRemoveQueue();
481
482 for(TransportList::iterator it = mSharedProcessTransports.begin(); it != mSharedProcessTransports.end(); it++)
483 {
484 try
485 {
486 (*it)->process(fdset);
487 }
488 catch (BaseException& e)
489 {
490 ErrLog (<< "Exception thrown from Transport::process: " << e);
491 }
492 }
493
494 if(mSelectInterruptor.get())
495 {
496 mSelectInterruptor->process(fdset);
497 }
498}
499
500void
502{
503 // This function will only be sufficient if these Transports are hooked into a FdPollGrp.
504 checkTransportAddRemoveQueue();
505
506 for(TransportList::iterator it = mSharedProcessTransports.begin(); it != mSharedProcessTransports.end(); it++)
507 {
508 try
509 {
510 (*it)->process();
511 }
512 catch (BaseException& e)
513 {
514 ErrLog (<< "Exception thrown from Transport::process: " << e);
515 }
516 }
517}
518
519void
520TransportSelector::checkTransportAddRemoveQueue()
521{
522 // This method ensures we add/remove from/to the mSharedProcessTransports
523 // list from the TransportSelectorThread
524
525 Transport* t(mTransportsToAddRemove.getNext(-1));
526 while(t)
527 {
528 if(!t->isShuttingDown())
529 {
530 // If not shutting down then this is an addTransport request
531 mSharedProcessTransports.push_back(t);
532 }
533 else
534 {
535 // If shutting down then this is a removeTransport request
536 for(TransportList::iterator it = mSharedProcessTransports.begin(); it != mSharedProcessTransports.end(); it++)
537 {
538 if((*it)->getKey() == t->getKey())
539 {
540 mSharedProcessTransports.erase(it);
541 break;
542 }
543 }
544
545 // Now delete the transport
546 delete t;
547 }
548
549 t = mTransportsToAddRemove.getNext(-1);
550 }
551}
552
553void
555{
556 for(TransportList::iterator it = mHasOwnProcessTransports.begin(); it != mHasOwnProcessTransports.end(); it++)
557 {
558 try
559 {
560 (*it)->poke();
561 }
562 catch (BaseException& e)
563 {
564 ErrLog (<< "Exception thrown from Transport::process: " << e);
565 }
566 }
567
568 if(mSelectInterruptor.get() /* && hasDataToSend() */) // removed hasDataToSend call since it is not thread safe
569 {
570 mSelectInterruptor->handleProcessNotification();
571 }
572}
573
574bool
576{
577 for(TransportList::const_iterator it = mSharedProcessTransports.begin(); it != mSharedProcessTransports.end(); it++)
578 {
579 if ((*it)->hasDataToSend())
580 {
581 return true;
582 }
583 }
584 return false;
585}
586
588//mDns.lookup() but this can result in a synchronous call to handle() which
589//assumes that dnsresult has been assigned to the TransactionState
593{
594 return mDns.createDnsResult(handler);
595}
596
597void
599 SipMessage* msg)
600{
601 // Picking the target destination:
602 // - for request, use forced target if set
603 // otherwise use loose routing behaviour (route or, if none, request-uri)
604 // - for response, use forced target if set, otherwise look at via
605
606 if (msg->isRequest())
607 {
608 // If this is an ACK we need to fix the tid to reflect that
609 if (msg->hasForceTarget())
610 {
611 DebugLog(<< "Looking up dns entries (from forceTarget) for " << msg->getForceTarget());
612 mDns.lookup(result, msg->getForceTarget());
613 }
614 else if (msg->exists(h_Routes) && !msg->const_header(h_Routes).empty())
615 {
616 // put this into the target, in case the send later fails, so we don't
617 // lose the target
618 msg->setForceTarget(msg->const_header(h_Routes).front().uri());
619 DebugLog (<< "Looking up dns entries (from route) for " << msg->getForceTarget());
620 mDns.lookup(result, msg->getForceTarget());
621 }
622 else
623 {
624 DebugLog (<< "Looking up dns entries for " << msg->const_header(h_RequestLine).uri());
625 mDns.lookup(result, msg->const_header(h_RequestLine).uri());
626 }
627 }
628 else if (msg->isResponse())
629 {
630 ErrLog(<<"unimplemented response dns");
631 resip_assert(0);
632 }
633 else
634 {
635 resip_assert(0);
636 }
637}
638
639// FIXME: maybe this should move to rutil/TransportType?
641{
642 static const bool unknown_transport = false;
643 switch(type)
644 {
645 case UDP:
646 case DTLS:
647 case DCCP:
648 case SCTP:
649 return true;
650
651 case TCP:
652 case TLS:
653 case WS:
654 case WSS:
655 return false;
656
657 default:
658 resip_assert(unknown_transport);
659 return unknown_transport; // !kh! just to make it compile wo/warning.
660 }
661}
662
663Tuple
665{
666// !kh! both getaddrinfo() and IPv6 are not supported by cygwin, yet.
667#ifdef __CYGWIN__
668 resip_assert(0);
669 return Tuple();
670#else
671 // !kh!
672 // 1. Query local hostname.
673 char hostname[256] = "";
674 if(gethostname(hostname, sizeof(hostname)) != 0)
675 {
676 int e = getErrno();
677 Transport::error( e );
678 InfoLog(<< "Can't query local hostname : [" << e << "] " << strError(e) );
679 throw Transport::Exception("Can't query local hostname", __FILE__, __LINE__);
680 }
681 InfoLog(<< "Local hostname is [" << hostname << "]");
682
683 // !kh!
684 // 2. Resolve address(es) of local hostname for specified transport.
685 const bool is_dgram = isDgramTransport(type);
686 addrinfo hint;
687 memset(&hint, 0, sizeof(hint));
688 hint.ai_family = is_v4 ? PF_INET : PF_INET6;
689 hint.ai_flags = AI_PASSIVE;
690 hint.ai_socktype = is_dgram ? SOCK_DGRAM : SOCK_STREAM;
691
692 addrinfo* results;
693 int ret = getaddrinfo(
694 hostname,
695 0,
696 &hint,
697 &results);
698
699 if(ret != 0)
700 {
701 Transport::error( ret ); // !kh! is this the correct sematics? ret is not errno.
702 InfoLog(<< "Can't resolve " << hostname << "'s address : [" << ret << "] " << gai_strerror(ret) );
703 throw Transport::Exception("Can't resolve hostname", __FILE__,__LINE__);
704 }
705
706 // !kh!
707 // 3. Use first address resolved if there are more than one.
708 // What should I do if there are more than one address?
709 // i.e. results->ai_next != 0.
710 Tuple source(*(results->ai_addr), type);
711 InfoLog(<< "Local address is " << source);
712 addrinfo* ai = results->ai_next;
713 for(; ai; ai = ai->ai_next)
714 {
715 Tuple addr(*(ai->ai_addr), type);
716 InfoLog(<<"Additional address " << addr);
717 }
718 freeaddrinfo(results);
719
720 return source;
721#endif
722}
723
724
731TransportSelector::findTransportByVia(SipMessage* msg, const Tuple& target, Tuple& source) const
732{
733 resip_assert(msg->exists(h_Vias));
734 resip_assert(!msg->const_header(h_Vias).empty());
735 const Via& via = msg->const_header(h_Vias).front();
736
737 if (via.sentHost().empty() && via.transport().empty())
738 {
739 return 0;
740 }
741
742 // XXX: Is there better way to do below (without the copy)?
743 source = Tuple(via.sentHost(), via.sentPort(), target.ipVersion(),
744 via.transport().empty() ? target.getType() : toTransportType(via.transport()), // Transport type is pre-populated in via, lock to it
745 Data::Empty, target.getNetNs());
746 DebugLog(<< "TransportSelector::findTransportByVia: source: " << source);
747
748 if (target.mFlowKey != 0 && (source.getPort() == 0 || source.isAnyInterface()))
749 {
750 WarningLog(<< "Sending request with incomplete Via header and FlowKey."
751 << " This code no smart enough to pick the correct Transport."
752 << " Via=" << via);
753 resip_assert(0);
754 }
755 if (source.isAnyInterface())
756 {
757 // INADDR_ANY cannot go out on the wire, so remove it from
758 // via header now.
759 // transmit() will later use determineSourceInterface() to
760 // get the actual interface to populate the Contact & Via headers.
761 // Not sure if we should support this case or just assert.
762 // .gh. This should be supported, in case only the transport part of the Via is set
763 msg->header(h_Vias).front().sentHost().clear();
764 }
765
766 Transport* trans;
767 if ((trans = findTransportBySource(source, msg)) == NULL)
768 {
769 return NULL;
770 }
771 if (source.getPort() == 0)
772 {
773 source.setPort(trans->port());
774 }
775 return trans;
776}
777
778Tuple
779TransportSelector::determineSourceInterface(SipMessage* msg, const Tuple& target) const
780{
781 resip_assert(msg->exists(h_Vias));
782 resip_assert(!msg->header(h_Vias).empty());
783 const Via& via = msg->header(h_Vias).front();
784
785 // this case should be handled already for UDP and TCP targets
786 resip_assert((!(msg->isRequest() && !via.sentHost().empty())) || isSecure(target.getType()));
787 if (1)
788 {
789 Tuple source(target);
790#if defined(WIN32) && !defined(NO_IPHLPAPI)
791 try
792 {
793 GenericIPAddress addr = WinCompat::determineSourceInterface(target.toGenericIPAddress());
794 source.setSockaddr(addr);
795 }
796 catch (WinCompat::Exception& ex)
797 {
798 ErrLog (<< "Can't find source interface to use: " << ex);
799 throw Transport::Exception("Can't find source interface", __FILE__, __LINE__);
800 }
801#else
802 // !kh!
803 // The connected UDP technique doesn't work all the time.
804 // 1. Might not work on all implementaions as stated in UNP vol.1 8.14.
805 // 2. Might not work under unspecified condition on Windows,
806 // search "getsockname" in MSDN library.
807 // 3. We've experienced this issue on our production software.
808
809 // this process will determine which interface the kernel would use to
810 // send a packet to the target by making a connect call on a udp socket.
812 Data netNs = target.getNetNs();
813 // One IPV4 and IPV6 socket per namespace. Even if we do not support netns,
814 // we still have the default namespace of "" (empty string).
815 if (target.isV4())
816 {
817 // If socket does not exist for namespace, create one
818 if (mSockets.find(netNs) == mSockets.end() || mSockets[netNs] == INVALID_SOCKET)
819 {
820#ifdef USE_NETNS
821 NetNs::setNs(netNs);
822#endif
823 mSockets[netNs] = InternalTransport::socket(UDP, V4); // may throw
824 }
825 tmp = mSockets[netNs];
826 }
827 else
828 {
829 // If socket does not exist for namespace, create one
830 if (mSocket6s.find(netNs) == mSocket6s.end() || mSocket6s[netNs] == INVALID_SOCKET)
831 {
832#ifdef USE_NETNS
833 NetNs::setNs(netNs);
834#endif
835 mSocket6s[netNs] = InternalTransport::socket(UDP, V6); // may throw
836 }
837 tmp = mSocket6s[netNs];
838 }
839
840#ifdef USE_NETNS
841 // Not sure if connect has to be done in netns context or just the socket create
842 NetNs::setNs(netNs);
843#endif
844
845 int ret = connect(tmp,&target.getSockaddr(), target.length());
846 if (ret < 0)
847 {
848 int e = getErrno();
849 Transport::error( e );
850 InfoLog(<< "Unable to route to " << target << " : [" << e << "] " << strError(e) );
851 throw Transport::Exception("Can't find source address for Via", __FILE__,__LINE__);
852 }
853
854 socklen_t len = source.length();
855 ret = getsockname(tmp,&source.getMutableSockaddr(), &len);
856 if (ret < 0)
857 {
858 int e = getErrno();
860 InfoLog(<< "Can't determine name of socket " << target << " : " << strError(e) );
861 throw Transport::Exception("Can't find source address for Via", __FILE__,__LINE__);
862 }
863
864 // !kh! test if connected UDP technique results INADDR_ANY, i.e. 0.0.0.0.
865 // if it does, assume the first avaiable interface.
866 if(source.isV4())
867 {
868 long src = (reinterpret_cast<const sockaddr_in*>(&source.getSockaddr())->sin_addr.s_addr);
869 if(src == INADDR_ANY)
870 {
871 InfoLog(<< "Connected UDP failed to determine source address, use first address instaed.");
872 source = getFirstInterface(true, target.getType());
873 }
874 }
875 else // IPv6
876 {
877//should never reach here in WIN32 w/ V6 support
878#if defined(USE_IPV6) && !defined(WIN32)
879 if (source.isAnyInterface())
880 {
881 source = getFirstInterface(false, target.getType());
882 }
883# endif
884 }
885 // Unconnect.
886 // !jf! This is necessary, but I am not sure what we can do if this
887 // fails. I'm not sure the stack can recover from this error condition.
888 if (target.isV4())
889 {
890 ret = connect(mSockets[netNs],
891 (struct sockaddr*)&mUnspecified.v4Address,
892 sizeof(mUnspecified.v4Address));
893 }
894#ifdef USE_IPV6
895 else
896 {
897 ret = connect(mSocket6s[netNs],
898 (struct sockaddr*)&mUnspecified6.v6Address,
899 sizeof(mUnspecified6.v6Address));
900 }
901#else
902 else
903 {
904 resip_assert(0);
905 }
906#endif
907
908 if ( ret<0 )
909 {
910 int e = getErrno();
911 //.dcm. OS X 10.5 workaround, we could #ifdef for specific OS X version.
912 if (!(e ==EAFNOSUPPORT || e == EADDRNOTAVAIL))
913 {
914 ErrLog(<< "Can't disconnect socket : " << strError(e) );
916 throw Transport::Exception("Can't disconnect socket", __FILE__,__LINE__);
917 }
918 }
919#endif
920
921 // This is the port that the request will get sent out from. By default,
922 // this value will be 0, since the Helper that creates the request will not
923 // assign it. In this case, the stack will pick an arbitrary (but appropriate)
924 // transport. If it is non-zero, it will only match transports that are bound to
925 // the specified port (and fail if none are available)
926
927 if(msg->isRequest())
928 {
929 source.setPort(via.sentPort());
930 }
931 else
932 {
933 source.setPort(0);
934 }
935
936 DebugLog (<< "Looked up source for destination: " << target
937 << " -> " << source
938 << " sent-by=" << via.sentHost()
939 << " sent-port=" << via.sentPort());
940
941 return source;
942 }
943}
944
945// TransportSelector::transmit (pseudo code)
946// If Request
947// findTransportByVia - check top via for matching source host and port that matches a transport
948// If not found->findTransportByDest
949// If key set then select matching transport
950// If target matches(AnyPortAnyInterfaceCompare) exactly one transport by type, ie: if target is TCP and we only have one TCP transport it is selected
951// If not found->determineSourceInterface - takes the destination and queries the OS for the best interface to use to send to that destination
952// ->findTransportBySource - find a transport that matches the source interface found above
953// If Response
954// findTransportByDest
955// If key set then select matching transport
956// If target matches(AnyPortAnyInterfaceCompare) exactly one transport type
959{
960 resip_assert(msg);
961
962 // Rollback outbound decorators if needed
964
965 try
966 {
968
969 // !ah! You NEED to do this for responses too -- the transport doesn't
970 // !ah! know it's IP addres(es) in all cases, AND it's function of the dest.
971 // (imagine a synthetic message...)
972
973 Tuple source;
974 // .bwc. We need 3 things here:
975 // 1) A Transport* to call send() on.
976 // 2) A complete Tuple to pass in this call (target).
977 // 3) A host, port, and protocol for filling out the topmost via, and
978 // possibly stuff like Contact, Referred-By, and other headers that
979 // must specify a hostname that the TU was unable to supply, because
980 // it didn't know what interface/port the message would be sent on.
981 // (source)
982 /*
983 Our Transport* might be found in target. If so, we can skip this block
984 of stuff.
985 Alternatively, we might not have the transport to start with. However,
986 given a connection id, we will be able to find the Connection we
987 should use, we can get the Transport we want. If we have no connection
988 id, but we know we are using TLS, DTLS or WSS and have a tls hostname, we
989 can use the hostname to find the appropriate transport. If all else
990 fails, we must resort to the connected UDP trick to fill out source,
991 which in turn is used to look up a matching transport.
992
993 Given the transport, it is always possible to get the port/protocol,
994 and usually possible to get the host (if it is bound to INADDR_ANY, we
995 can't tell). However, if we had to fill out source in order to find
996 the transport in the first place, this is not an issue.
997 */
998
999 Data remoteSigcompId;
1000
1001 Transport* transport=0;
1002
1003 if (msg->isRequest())
1004 {
1005 // TODO SLG - If target has mTransportKey set, can't we skip findTransportByVia and go right to findTransportByDest?
1006 transport = findTransportByVia(msg, target, source);
1007 if (!transport)
1008 {
1009 if ((transport = findTransportByDest(target)) != NULL)
1010 {
1011 source = transport->getTuple();
1012 DebugLog(<< "Found (request) " << *transport << " by target=" << target);
1013 }
1014 }
1015 else
1016 {
1017 DebugLog(<< "Found (request) " << *transport << " by via, source=" << source << " for target=" << target);
1018 }
1019
1020 if(!transport && target.mFlowKey && target.onlyUseExistingConnection)
1021 {
1022 // .bwc. Connection info was specified, and use of this connection
1023 // was mandatory, but connection is no longer around. We fail.
1024 transportFailureReason = TransportFailure::TransportNoExistConn;
1025 }
1026 else if (transport)// .bwc. Here we use transport to find source.
1027 {
1028 resip_assert( source.getType()!=0 );
1029
1030 // .bwc. If the transport has an ambiguous interface, we need to
1031 //look a little closer.
1032 if(source.isAnyInterface())
1033 {
1034 Tuple temp = determineSourceInterface(msg,target);
1035
1036 // .bwc. determineSourceInterface() can give us a port, if the TU
1037 // put one in the topmost Via.
1038 resip_assert(source.ipVersion()==temp.ipVersion() &&
1039 source.getType()==temp.getType());
1040 source=temp;
1041
1042 /* determineSourceInterface might return an arbitrary port
1043 here, so use the port specified in transport->port().
1044 */
1045 if(source.getPort()==0)
1046 {
1047 source.setPort(transport->port());
1048 }
1049 }
1050 }
1051 // .bwc. Here we use source to find transport.
1052 else
1053 {
1054 source = determineSourceInterface(msg, target);
1055 transport = findTransportBySource(source, msg);
1056
1057 // .bwc. determineSourceInterface might give us a port
1058 if(transport)
1059 {
1060 DebugLog(<< "Found (request) " << *transport << " by source=" << source << " for target=" << target);
1061 if (source.getPort()==0)
1062 {
1063 source.setPort(transport->port());
1064 }
1065 }
1066 }
1067
1068 target.mTransportKey = transport ? transport->getKey() : 0;
1069
1070 // .bwc. Topmost Via is only filled out in the request case. Also, if
1071 // we don't have a transport at this point, we're going to fail,
1072 // so don't bother doing the work.
1073 if(target.mTransportKey)
1074 {
1075 Via& topVia(msg->header(h_Vias).front());
1076 topVia.remove(p_maddr); // !jf! why do this?
1077
1078 // insert the via
1079 if (topVia.transport().empty())
1080 {
1081 topVia.transport() = Tuple::toData(source.getType());
1082 }
1083 if (!topVia.sentHost().size())
1084 {
1085 topVia.sentHost() = Tuple::inet_ntop(source);
1086 }
1087 if (!topVia.sentPort())
1088 {
1089 topVia.sentPort() = source.getPort();
1090 }
1091
1092 if (mCompression.isEnabled())
1093 {
1094 // Indicate support for SigComp, if appropriate.
1095 if (!topVia.exists(p_comp))
1096 {
1097 topVia.param(p_comp) = "sigcomp";
1098 }
1099 if (!topVia.exists(p_sigcompId))
1100 {
1101 topVia.param(p_sigcompId) = mCompression.getSigcompId();
1102 }
1103
1104 // Figure out remote identifier (from Route header
1105 // field, if present; otherwise, from Request-URI).
1106 // XXX rohc-sip-sigcomp-03 says to use +sip.instance,
1107 // but this is impossible to actually do if you're
1108 // not actually the registrar, and really hard even
1109 // if you are.
1110 Uri destination;// (*reinterpret_cast<Uri*>(0)); // !bwc! What?
1111
1112 if(msg->exists(h_Routes) &&
1113 !msg->const_header(h_Routes).empty())
1114 {
1115 destination = msg->const_header(h_Routes).front().uri();
1116 }
1117 else
1118 {
1119 destination = msg->const_header(h_RequestLine).uri();
1120 }
1121
1122 if (destination.exists(p_comp) &&
1123 destination.param(p_comp) == "sigcomp")
1124 {
1125 if (destination.exists(p_sigcompId))
1126 {
1127 remoteSigcompId = destination.param(p_sigcompId);
1128 }
1129 else
1130 {
1131 remoteSigcompId = destination.host();
1132 }
1133 }
1134 // Squirrel the compartment away in the branch so that
1135 // we can figure out (pre-transaction-matching) what
1136 // compartment incoming requests are associated with.
1137 topVia.param(p_branch).setSigcompCompartment(remoteSigcompId);
1138 }
1139 }
1140 }
1141 else if (msg->isResponse())
1142 {
1143 // We assume that all stray responses have been discarded, so we always
1144 // know the transport that the corresponding request was received on
1145 // and this has been copied by TransactionState::sendToWire into transport
1146 transport=findTransportByDest(target);
1147
1148 // Transport used to receive the request, may have been removed.
1149 if(transport)
1150 {
1151 source = transport->getTuple();
1152 DebugLog(<< "Found (response) " << *transport << " by target=" << target);
1153
1154 // .bwc. If the transport has an ambiguous interface, we need to
1155 //look a little closer.
1156 if(source.isAnyInterface())
1157 {
1158 Tuple temp = source;
1159 source = determineSourceInterface(msg,target);
1160 resip_assert(source.ipVersion()==temp.ipVersion() &&
1161 source.getType()==temp.getType());
1162
1163 /* determineSourceInterface might return an arbitrary port here,
1164 so use the port specified in transport->port().
1165 */
1166 if(source.getPort()==0)
1167 {
1168 source.setPort(transport->port());
1169 }
1170 }
1171 if (mCompression.isEnabled())
1172 {
1173 // Figure out remote identifier (from Via header field).
1174 Via& topVia(msg->header(h_Vias).front());
1175
1176 if(topVia.exists(p_comp) && topVia.param(p_comp) == "sigcomp")
1177 {
1178 if (topVia.exists(p_sigcompId))
1179 {
1180 remoteSigcompId = topVia.param(p_sigcompId);
1181 }
1182 else
1183 {
1184 // XXX rohc-sigcomp-sip-03 says "sent-by", but this should probably
1185 // be "received" if present, and "sent-by" otherwise.
1186 // XXX Also, the spec is ambiguous about whether to include the port
1187 // in this identifier.
1188 remoteSigcompId = topVia.sentHost();
1189 }
1190 }
1191 }
1192 }
1193 }
1194 else
1195 {
1196 resip_assert(0);
1197 }
1198
1199 // .bwc. At this point, source, transport, and target should be
1200 // _fully_ specified.
1201
1202 if (transport)
1203 {
1204 Data sourceAddress = Tuple::inet_ntop(source);
1205 // !bwc! TODO This filling in of stuff really should be handled with
1206 // the callOutboundDecorators() callback. (Or, at the very least,
1207 // we should allow this code to be turned off through configuration.
1208 // There are plenty of cases where this stuff is not at all necessary.)
1209 // There is a contact header and it contains exactly one entry
1210 if (msg->exists(h_Contacts) && msg->header(h_Contacts).size()==1)
1211 {
1212 for (NameAddrs::iterator i=msg->header(h_Contacts).begin(); i != msg->header(h_Contacts).end(); i++)
1213 {
1214 const NameAddr& c_contact = *i;
1215 NameAddr& contact = *i;
1216 // No host specified, so use the ip address and port of the
1217 // transport used. Otherwise, leave it as is.
1218 if (c_contact.uri().host().empty())
1219 {
1220 contact.uri().host() = (transport->hasSpecificContact() ?
1221 transport->interfaceName() :
1222 sourceAddress);
1223 contact.uri().port() = transport->port();
1224
1225 if (transport->transport() != UDP && !contact.uri().exists(p_gr))
1226 {
1227 contact.uri().param(p_transport) = Tuple::toDataLower(transport->transport());
1228 }
1229
1230 // Add comp=sigcomp to contact URI
1231 // Also, If no +sip.instance on contact HEADER,
1232 // add sigcomp-id="<urn>" to contact URI.
1233 if (mCompression.isEnabled())
1234 {
1235 if (!contact.uri().exists(p_comp))
1236 {
1237 contact.uri().param(p_comp) = "sigcomp";
1238 }
1239 if (!contact.exists(p_Instance) &&
1240 !contact.uri().exists(p_sigcompId))
1241 {
1242 contact.uri().param(p_sigcompId) = mCompression.getSigcompId();
1243 }
1244 }
1245 }
1246 else
1247 {
1248 if (c_contact.uri().exists(p_addTransport))
1249 {
1250 if (target.getType() != UDP)
1251 {
1252 contact.uri().param(p_transport) = Tuple::toDataLower(target.getType());
1253 }
1254 contact.uri().remove(p_addTransport);
1255 }
1256 }
1257 }
1258 }
1259
1260 // !bwc! TODO make this configurable
1261 // Fix the Referred-By header if no host specified.
1262 // If malformed, leave it alone.
1263 if (msg->exists(h_ReferredBy)
1264 && msg->const_header(h_ReferredBy).isWellFormed())
1265 {
1266 if (msg->const_header(h_ReferredBy).uri().host().empty())
1267 {
1268 msg->header(h_ReferredBy).uri().host() = sourceAddress;
1269 msg->header(h_ReferredBy).uri().port() = transport->port();
1270 }
1271 }
1272
1273 // !bwc! TODO make this configurable
1274 // .bwc. Only try fiddling with this is if the Record-Route is well-
1275 // formed. If the topmost Record-Route is malformed, we have no idea
1276 // whether it came from something the TU synthesized or from the wire.
1277 // We shouldn't touch it. Frankly, I take issue with the method we have
1278 // chosen to signal to the stack that we want it to fill out various
1279 // header-field-values.
1280 if (msg->exists(h_RecordRoutes)
1281 && !msg->const_header(h_RecordRoutes).empty()
1282 && msg->const_header(h_RecordRoutes).front().isWellFormed())
1283 {
1284 const NameAddr& c_rr = msg->const_header(h_RecordRoutes).front();
1285 NameAddr& rr = msg->header(h_RecordRoutes).front();
1286 if (c_rr.uri().host().empty())
1287 {
1288 rr.uri().host() = sourceAddress;
1289 rr.uri().port() = transport->port();
1290 if (target.getType() != UDP && !rr.uri().exists(p_transport))
1291 {
1292 rr.uri().param(p_transport) = Tuple::toDataLower(target.getType());
1293 }
1294 // Add comp=sigcomp and sigcomp-id="<urn>" to Record-Route
1295 // XXX This isn't quite right -- should be set only
1296 // on routes facing the client. Doing this correctly
1297 // requires double-record-routing
1298 if (mCompression.isEnabled())
1299 {
1300 if (!rr.uri().exists(p_comp))
1301 {
1302 rr.uri().param(p_comp) = "sigcomp";
1303 }
1304 if (!rr.uri().exists(p_sigcompId))
1305 {
1306 rr.uri().param(p_sigcompId) = mCompression.getSigcompId();
1307 }
1308 }
1309 }
1310 }
1311
1312 // !bwc! TODO make this configurable
1313 // See draft-ietf-sip-identity
1314 // TODO !SLG! RFC4474 has been deprecated by RFC8224 (Authenticated Identity Management). We should remove/adjust this code.
1315 if (mSecurity && msg->exists(h_Identities) &&
1316 msg->header(h_Identities).size() > 0 && msg->const_header(h_Identities).front().value().empty())
1317 {
1318 DateCategory now;
1319 msg->header(h_Date) = now;
1320#if defined(USE_SSL)
1321 try
1322 {
1323 const Data& domain = msg->const_header(h_From).uri().host();
1324 msg->header(h_Identities).front().value() = mSecurity->computeIdentity(domain, msg->getCanonicalIdentityString());
1325 }
1326 catch (Security::Exception& e)
1327 {
1328 InfoLog (<< "Couldn't add identity header: " << e);
1329 msg->remove(h_Identities);
1330 if (msg->exists(h_IdentityInfo))
1331 {
1332 msg->remove(h_IdentityInfo);
1333 }
1334 }
1335#endif
1336 }
1337
1338 resip_assert(target.mTransportKey == transport->getKey());
1339
1340 // Call back anyone who wants to perform outbound decoration
1341 msg->callOutboundDecorators(source, target, remoteSigcompId);
1342
1343 for(auto& handler : transport->getSipMessageLoggingHandlers())
1344 {
1345 handler->outboundMessage(source, target, *msg);
1346 }
1347
1348 std::unique_ptr<SendData> send(new SendData(target,
1350 msg->getTransactionId(),
1351 remoteSigcompId));
1352
1353 send->data.reserve(mAvgBufferSize + mAvgBufferSize/4);
1354
1355 DataStream str(send->data);
1356 msg->encode(str);
1357 str.flush();
1358
1359 // !bwc! Moving average of message size. (Used to intelligently
1360 // predict how much space to reserve in the buffer, to minimize
1361 // dynamic resizing.)
1362 mAvgBufferSize = (255*mAvgBufferSize + (int)send->data.size()+128)/256;
1363
1364 resip_assert(!send->data.empty());
1365 DebugLog (<< "Transmitting to " << target
1366 << " tlsDomain=" << msg->getTlsDomain()
1367 << " via " << source
1368 << std::endl << std::endl << send->data.escaped()
1369 << "sigcomp id=" << remoteSigcompId);
1370
1371 if(sendData)
1372 {
1373 *sendData = *send;
1374 }
1375
1376 transport->send(std::move(send));
1377 return Sent;
1378 }
1379 else
1380 {
1381 InfoLog (<< "tid=" << msg->getTransactionId() << " failed to find a transport to " << target);
1382 mStateMacFifo.add(new TransportFailure(msg->getTransactionId(), transportFailureReason));
1383 return Unsent;
1384 }
1385 }
1386 catch (Transport::Exception& )
1387 {
1388 InfoLog (<< "tid=" << msg->getTransactionId() << " no route to target: " << target);
1389 mStateMacFifo.add(new TransportFailure(msg->getTransactionId(), TransportFailure::NoRoute));
1390 return Unsent;
1391 }
1392}
1393
1394void
1396{
1398 Transport* transport = findTransportByDest(data.destination);
1399
1400 // !jf! The previous call to transmit may have blocked or failed (It seems to
1401 // block in windows when the network is disconnected - don't know why just
1402 // yet.
1403
1404 // !kh!
1405 // My speculation for blocking is; when network is disconnected, WinSock sets
1406 // a timer (to give up) and tries to defer reporting the error, in hope of
1407 // the network would be recovered. Before the timer fires, applications could
1408 // still select() (or the likes) their socket descriptors and find they are
1409 // writable if there is still room in buffer (per socket). If the send()s or
1410 // sendto()s made during this time period overflows the buffer, it blocks.
1411 // But I somewhat doubt this would be noticed, because block would be brief,
1412 // once the timer fires, the blocked call would return error.
1413 // Note that the block applies to both UDP and TCP sockets.
1414 // Quote from Linux man page:
1415 // When the message does not fit into the send buffer of the socket, send
1416 // normally blocks, unless the socket has been placed in non-blocking I/O
1417 // mode. In non-blocking mode it would return EAGAIN in this case.
1418 // Quote from MSDN library:
1419 // If no buffer space is available within the transport system to hold the
1420 // data to be transmitted, sendto will block unless the socket has been
1421 // placed in a nonblocking mode.
1422
1423 if(transport)
1424 {
1425 // If this is not true, it means the transport has been removed.
1426 for(auto& handler : transport->getSipMessageLoggingHandlers())
1427 {
1428 handler->outboundRetransmit(transport->getTuple(), data.destination, data);
1429 }
1430
1431 transport->send(std::unique_ptr<SendData>(data.clone()));
1432 }
1433}
1434
1435void
1437{
1438 Transport* t = findTransportByDest(peer);
1439 if(t)
1440 {
1441 SendData* close=new SendData(peer,
1446 t->send(std::unique_ptr<SendData>(close));
1447 }
1448}
1449
1450unsigned int
1452{
1453 unsigned int sum = 0;
1454 for(TransportKeyMap::const_iterator it = mTransports.begin(); it != mTransports.end(); it++)
1455 {
1456 sum += it->second->getFifoSize();
1457 }
1458 return sum;
1459}
1460
1461void
1466
1467void
1469{
1470 Transport* t = findTransportByDest(flow);
1471 if(t)
1472 {
1478 t->send(std::unique_ptr<SendData>(enableFlowTimer));
1479 }
1480}
1481
1482void
1484{
1485 for (TransportKeyMap::iterator it = mTransports.begin(); it != mTransports.end(); it++)
1486 {
1487 if (type == UNKNOWN_TRANSPORT || type == it->second->transport())
1488 {
1489 it->second->invokeAfterSocketCreationFunc();
1490 }
1491 }
1492 if (type == UNKNOWN_TRANSPORT)
1493 {
1494 // !slg! TODO - invoke for DNS?
1495 //mDns.
1496 }
1497}
1498
1499Transport*
1501{
1502 if(target.mTransportKey)
1503 {
1504 TransportKeyMap::iterator it = mTransports.find(target.mTransportKey);
1505 if(it != mTransports.end())
1506 {
1507 return it->second;
1508 }
1509 }
1510 else
1511 {
1512 std::pair<TypeToTransportMap::iterator, TypeToTransportMap::iterator> range(mTypeToTransportMap.equal_range(target));
1513
1514 if(range.first != range.second) // At least one match
1515 {
1516 TypeToTransportMap::iterator i=range.first;
1517 ++i;
1518 if(i==range.second) // Exactly one match
1519 {
1520 return range.first->second;
1521 }
1522 }
1523 }
1524
1525 // .bwc. No luck here. Maybe findTransportBySource will end up working.
1526 return 0;
1527}
1528
1533Transport*
1534TransportSelector::findLoopbackTransportBySource(bool ignorePort, Tuple& search) const
1535{
1536 //When we are sending to a loopback address, the kernel makes an
1537 //(effectively) arbitrary choice of which loopback address to send
1538 //from. (Since any loopback address can be used to send to any other
1539 //loopback address) This choice may not agree with our idea of what
1540 //address we should be sending from, so we need to just choose the
1541 //loopback address we like, and ignore what the kernel told us to do.
1542 ExactTupleMap::const_iterator i;
1543 for (i=mExactTransports.begin();i != mExactTransports.end();i++)
1544 {
1545 DebugLog(<<"search: " << search << " elem: " << i->first);
1546 if(i->first.ipVersion()==V4)
1547 {
1548 //Compare only the first byte (the 127)
1549 if(i->first.isEqualWithMask(search,8,ignorePort))
1550 {
1551 // Not sure if this should go here or in Tuple::isEqualWithMask
1552 if(i->first.getNetNs() == search.getNetNs())
1553 {
1554 search=i->first;
1555 DebugLog(<<"Match!");
1556 return i->second;
1557 }
1558 }
1559 }
1560#ifdef USE_IPV6
1561 else if(i->first.ipVersion()==V6)
1562 {
1563 //What to do?
1564 }
1565#endif
1566 else
1567 {
1568 resip_assert(0);
1569 }
1570 }
1571
1572 return 0;
1573} // of findLoopbackTransport
1574
1575Transport*
1577{
1578 DebugLog(<< "findTransportBySource(" << search << ")");
1579
1580 if(msg && isSecure(search.getType()))
1581 {
1582 // We should not be willing to attempt sending on a TLS/DTLS/WSS transport
1583 // that does not have the cert we're attempting to use, even if the
1584 // IP/port/proto match. If we have not specified which identity we want
1585 // to use, then proceed with the code below.
1586 return findTlsTransport(msg->getTlsDomain(), search);
1587 }
1588
1589 bool ignorePort = (search.getPort() == 0);
1590 DebugLog(<< "should port be ignored: " << ignorePort);
1591
1592 if (!ignorePort)
1593 {
1594 // 1. search for matching port on a specific interface
1595 {
1596 ExactTupleMap::const_iterator i = mExactTransports.find(search);
1597 if (i != mExactTransports.end())
1598 {
1599 DebugLog(<< "findTransport (exact) => " << *(i->second));
1600 return i->second;
1601 }
1602 }
1603
1604 // 2. search for matching port on any loopback interface
1605 if (search.isLoopback())
1606 {
1607 Transport *trans = findLoopbackTransportBySource( /*ignorePort*/false, search);
1608 if (trans)
1609 {
1610 DebugLog(<< "findLoopbackTransportBySource(" << search << ")");
1611 return trans;
1612 }
1613 }
1614
1615 // 3. search for specific port on ANY interface
1616 {
1617 AnyInterfaceTupleMap::const_iterator i = mAnyInterfaceTransports.find(search);
1618 if (i != mAnyInterfaceTransports.end())
1619 {
1620 DebugLog(<< "findTransport (any interface) => " << *(i->second));
1621 return i->second;
1622 }
1623 }
1624 }
1625 else
1626 {
1627 // 1. search for ANY port on specific interface
1628 {
1629 AnyPortTupleMap::const_iterator i = mAnyPortTransports.find(search);
1630 if (i != mAnyPortTransports.end())
1631 {
1632 DebugLog(<< "findTransport (any port, specific interface) => " << *(i->second) << " key: " << (i->first) << " search: " << search);
1633 return i->second;
1634 }
1635 }
1636
1637 // 2. search for ANY port on any loopback interface
1638 if (search.isLoopback())
1639 {
1640 Transport *trans = findLoopbackTransportBySource( /*ignorePort*/true, search);
1641 if (trans)
1642 {
1643 return trans;
1644 }
1645 }
1646
1647 // 3. search for ANY port on ANY interface
1648 {
1649 //CerrLog(<< "Trying AnyPortAnyInterfaceTupleMap " << mAnyPortAnyInterfaceTransports.size());
1650 AnyPortAnyInterfaceTupleMap::const_iterator i = mAnyPortAnyInterfaceTransports.find(search);
1651 if (i != mAnyPortAnyInterfaceTransports.end())
1652 {
1653 DebugLog(<< "findTransport (any port, any interface) => " << *(i->second));
1654 return i->second;
1655 }
1656 }
1657 }
1658
1659 DebugLog (<< "Exact interface / Specific port: " << Inserter(mExactTransports));
1660 DebugLog (<< "Any interface / Specific port: " << Inserter(mAnyInterfaceTransports));
1661 DebugLog (<< "Exact interface / Any port: " << Inserter(mAnyPortTransports));
1662 DebugLog (<< "Any interface / Any port: " << Inserter(mAnyPortAnyInterfaceTransports));
1663
1664 WarningLog(<< "Can't find matching transport " << search);
1665 return 0;
1666}
1667
1668Transport*
1669TransportSelector::findTlsTransport(const Data& domainname, const Tuple& search) const
1670{
1671 resip_assert(isSecure(search.getType()));
1672
1673 // Basically, the 'search' Tuple has a member called mTargetDomain.
1674 // However, it is not used due to the current TransportSelector implementation logic.
1675 // So we build a new tuple here with the domain and use it as the search key.
1676 // First search for exact match
1677 TlsTransportKey searchKey(domainname, search);
1678 const auto& i = mTlsTransports.find(searchKey);
1679
1680 if (i != mTlsTransports.end())
1681 {
1682 DebugLog(<< "findTlsTransport: numSecureTransports=" << mTlsTransports.size() <<", search=" << searchKey << " (exact match) => " << *(i->second));
1683 return i->second;
1684 }
1685
1686 // Perform relaxed search
1687 for (const auto& tlsTransport : mTlsTransports)
1688 {
1689 const TlsTransportKey& key = tlsTransport.first;
1690
1691 // Transport type and ipVersion must match
1692 if (key.mTuple.getType() != search.getType() || key.mTuple.ipVersion() != search.ipVersion())
1693 {
1694 continue;
1695 }
1696
1697 // If port is specified, it must match.
1698 if ((search.getPort() != 0) && (key.mTuple.getPort() != search.getPort()))
1699 {
1700 continue;
1701 }
1702
1703 // If address isn't INADDR_ANY on both, it must match
1704 if (!search.isAnyInterface() && !key.mTuple.isAnyInterface() &&
1705 !search.isEqual(key.mTuple, true /* ignorePort? */))
1706 {
1707 continue;
1708 }
1709
1710 // If TLS domain is set on both, it must match
1711 if (!domainname.empty() && !key.mTuple.getTargetDomain().empty() &&
1712 domainname != key.mTuple.getTargetDomain())
1713 {
1714 continue;
1715 }
1716
1717 DebugLog(<< "findTlsTransport: numSecureTransports=" << mTlsTransports.size() << ", search=" << searchKey << " (relaxed match) => " << *tlsTransport.second);
1718
1719 return tlsTransport.second;
1720 }
1721
1722 DebugLog(<< "findTlsTransport: No TLS transport found, numSecureTransports=" << mTlsTransports.size() << ", search=" << searchKey);
1723 return nullptr;
1724}
1725
1726unsigned int
1728{
1729 return hasDataToSend() ? 0 : INT_MAX;
1730}
1731
1732void
1737
1742
1744resip::operator<<(EncodeStream& ostrm, const TransportSelector::TlsTransportKey& tlsTransportKey)
1745{
1746 ostrm << tlsTransportKey.mTuple;
1747
1748 return ostrm;
1749}
1750
1751/* ====================================================================
1752 * The Vovida Software License, Version 1.0
1753 *
1754 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
1755 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1756 *
1757 * Redistribution and use in source and binary forms, with or without
1758 * modification, are permitted provided that the following conditions
1759 * are met:
1760 *
1761 * 1. Redistributions of source code must retain the above copyright
1762 * notice, this list of conditions and the following disclaimer.
1763 *
1764 * 2. Redistributions in binary form must reproduce the above copyright
1765 * notice, this list of conditions and the following disclaimer in
1766 * the documentation and/or other materials provided with the
1767 * distribution.
1768 *
1769 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1770 * and "Vovida Open Communication Application Library (VOCAL)" must
1771 * not be used to endorse or promote products derived from this
1772 * software without prior written permission. For written
1773 * permission, please contact vocal@vovida.org.
1774 *
1775 * 4. Products derived from this software may not be called "VOCAL", nor
1776 * may "VOCAL" appear in their name, without prior written
1777 * permission of Vovida Networks, Inc.
1778 *
1779 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1780 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1781 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1782 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1783 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1784 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1785 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1786 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1787 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1788 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1789 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1790 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1791 * DAMAGE.
1792 *
1793 * ====================================================================
1794 *
1795 * This software consists of voluntary contributions made by Vovida
1796 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1797 * Inc. For more information on Vovida Networks, Inc., please see
1798 * <http://www.vovida.org/>.
1799 *
1800 * vi: set shiftwidth=3 expandtab:
1801 */
#define FPEM_Read
Definition FdPoll.hxx:29
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
Handles cross-platform sockets compatibility.
#define INVALID_SOCKET
Definition Socket.hxx:95
bool isDgramTransport(TransportType type)
int Socket
The abstract base-class for all exceptions thrown by resip code.
Data computeIdentity(const Data &signerDomain, const Data &in) const
void addCompressorsToStack(osc::Stack *)
const Data & getSigcompId()
osc::StateHandler & getStateHandler()
An iostream that operates on an existing Data.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
static const Data Empty
Constant that represents a zero-length data.
Definition Data.hxx:882
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
Represents the "SIP-date" element in the RFC 3261 grammar.
void addTransportType(TransportType type, IpVersion version)
void lookup(DnsResult *res, const Uri &uri)
TupleMarkManager & getMarkManager()
void removeTransportType(TransportType type, IpVersion version)
DnsResult * createDnsResult(DnsHandler *handler=0)
virtual FdPollItemHandle addPollItem(Socket sock, FdPollEventMask newMask, FdPollItemIf *item)=0
virtual void delPollItem(FdPollItemHandle handle)=0
Object-oriented wrapper for your platform's file-descriptor set.
Definition Socket.hxx:162
A templated, threadsafe message-queue class.
Definition Fifo.hxx:16
static Socket socket(TransportType type, IpVersion ipVer)
bool isWellFormed() const
Returns true iff this element was parsed successfully, according to the implementation of parse().
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
Definition NameAddr.hxx:19
bool exists(const Param< NameAddr > &paramType) const
Definition NameAddr.cxx:477
const Uri & uri() const
Used to 'artificially' interrupt a select call.
SendDataCommand command
Definition SendData.hxx:80
SendData * clone() const
Definition SendData.hxx:55
Represents a SIP message.
bool isRequest() const
Returns true if message is a request, false otherwise.
bool hasForceTarget() const
void callOutboundDecorators(const Tuple &src, const Tuple &dest, const Data &sigcompId)
void setForceTarget(const Uri &uri)
deal with a notion of an "out-of-band" forced target for SIP routing
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
virtual const Data & getTransactionId() const
Returns the transaction id from the branch or if 2543, the computed hash.
bool isResponse() const
Returns true if message is a response, false otherwise.
const RequestLine & const_header(const RequestLineType &l) const
const Uri & getForceTarget() const
bool exists(const HeaderBase &headerType) const
Returns true if the given header field is present, false otherwise.
void remove(const HeaderBase &headerType)
Prevents a header field from being present when the message is prepared for sending to a transport.
void rollbackOutboundDecorators()
const Data & getTlsDomain() const
Data getCanonicalIdentityString() const
virtual EncodeStream & encode(EncodeStream &str) const
Generate a string from the SipMessage object.
This message is used to indicate that the TransportSelector has sent a sip message using either relia...
bool operator<(const TlsTransportKey &rhs) const
void registerMarkListener(MarkListener *listener)
unsigned int sumTransportFifoSizes() const
static Tuple getFirstInterface(bool is_v4, TransportType type)
Transport * findTransportBySource(Tuple &src, const SipMessage *msg) const
void closeConnection(const Tuple &peer)
void poke()
Causes transport process loops to be interrupted if there is stuff in their transmit fifos.
void createSelectInterruptor()
Called when the TransportSelector will be running in a different.
TransportSelector(Fifo< TransactionMessage > &fifo, Security *security, DnsStub &dnsStub, Compression &compression, bool useDnsVip)
void buildFdSet(FdSet &fdset)
Builds an FdSet comprised of all FDs from all suitable Transports.
Transport * findTransportByDest(const Tuple &dest)
bool isFinished() const
Returns true if all Transports have their buffers cleared, false otherwise.
void removeTransport(unsigned int transportKey)
void addTransport(std::unique_ptr< Transport > transport, bool isStackRunning)
Add/Remove a transport.
DnsResult * createDnsResult(DnsHandler *handler)
DNS Resolution.
void setPollGrp(FdPollGrp *pollGrp)
Configure a PollGrp to use (instead of buildFdSet/process) Must be called before adding any transport...
TransmitState
transmit results in msg->resolve() being called to either kick off dns resolution or to pick the next...
void enableFlowTimer(const resip::Tuple &flow)
void shutdown()
Shuts down all transports.
void unregisterMarkListener(MarkListener *listener)
void terminateFlow(const resip::Tuple &flow)
TransmitState transmit(SipMessage *msg, Tuple &target, SendData *sendData=0)
void invokeAfterSocketCreationFunc(TransportType type)
void dnsResolve(DnsResult *result, SipMessage *msg)
void retransmit(const SendData &msg)
Resend to the same transport as last time.
General exception class for Transport.
The base class for Transport classes.
Definition Transport.hxx:83
const Tuple & getTuple() const
virtual bool hasSpecificContact() const
IpVersion ipVersion() const
const TransportType transport() const
virtual void shutdown()
This starts shutting-down procedures for this Transport.
unsigned int getKey() const
int port() const
SipMessageLoggingHandlerList getSipMessageLoggingHandlers() const noexcept
Definition Transport.cxx:63
const Data & interfaceName() const
const Data & netNs() const
virtual void setPollGrp(FdPollGrp *grp)=0
const Data & tlsDomain() const
virtual bool shareStackProcessAndSelect() const =0
Returns true if this Transport should be included in the FdSet processing loop, false if the Transpor...
static void error(int e)
Generates a generic log for the platform specific socket error number.
virtual void send(std::unique_ptr< SendData > data)=0
void registerMarkListener(MarkListener *)
void unregisterMarkListener(MarkListener *)
Represents a network address.
Definition Tuple.hxx:59
IpVersion ipVersion() const
Returns V4 or V6 as appropriate.
Definition Tuple.cxx:537
sockaddr & getMutableSockaddr()
Retrieve the binary representation of the socket address for this tuple.
Definition Tuple.hxx:113
void setTargetDomain(const Data &target)
Set the domain name this address tuple intends to represent.
Definition Tuple.hxx:252
void setPort(int port)
Definition Tuple.cxx:449
static const Data & toData(TransportType)
Converts the TransportType to a string representation of the transport type, e.g.
Definition Tuple.cxx:927
bool isLoopback() const
Definition Tuple.cxx:503
const Data & getNetNs() const
Get the netns for this Tuple.
Definition Tuple.hxx:271
GenericIPAddress toGenericIPAddress() const
Definition Tuple.cxx:1169
bool isAnyInterface() const
TRUE if this address is equal to the "INADDR_ANY" value for this address family.
Definition Tuple.cxx:485
bool onlyUseExistingConnection
Definition Tuple.hxx:192
FlowKey mFlowKey
This is a (largely) opaque key that subclasses of Transport will use to help record/find flows.
Definition Tuple.hxx:189
static const Data & toDataLower(TransportType type)
Definition Tuple.cxx:933
bool isEqual(const Tuple &tuple, bool ignorePort=false, bool ignoreTransport=false) const
compares this tuple with the one passed in for family, port and address equality.
Definition Tuple.cxx:680
socklen_t length() const
Definition Tuple.cxx:655
bool isV4() const
Definition Tuple.cxx:531
int getPort() const
Definition Tuple.cxx:466
TransportType getType() const
Definition Tuple.hxx:122
static Data inet_ntop(const Tuple &tuple)
Converts the binary socket address to presentation format, via the DnsUtil::inet_ntop() method.
Definition Tuple.cxx:939
const sockaddr & getSockaddr() const
Retrieve a const binary representation of the socket address for this tuple.
Definition Tuple.hxx:109
void setSockaddr(const GenericIPAddress &)
Set the internal binary representation of the socket address from the GenericIPAddress.
Definition Tuple.cxx:239
const Data & getTargetDomain() const
Get the domain name this address tuple intends to represent.
Definition Tuple.hxx:259
TransportKey mTransportKey
Definition Tuple.hxx:190
A Transport based on UDP.
Represents the "SIP-URI" and "SIPS-URI" elements in the RFC 3261 grammar.
Definition Uri.hxx:22
int & port()
Definition Uri.hxx:145
bool exists(const Param< Uri > &paramType) const
Definition Uri.cxx:1701
void remove(const Param< Uri > &paramType)
Definition Uri.cxx:1709
Data & host()
Definition Uri.hxx:40
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
Represents the "via-parm" element in the RFC 3261 grammar.
Definition Via.hxx:18
void remove(const Param< Via > &paramType)
Definition Via.cxx:274
Data & transport()
Definition Via.cxx:121
bool exists(const Param< Via > &paramType) const
Definition Via.cxx:266
int & sentPort()
Definition Via.cxx:149
Data & sentHost()
Definition Via.cxx:135
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
dcm! – add UnusedChecking(_enum) below;
InserterClass< T > Inserter(const T &t)
Templatized function to construct an instance of InserterClass for a container to be inserted.
Definition Inserter.hxx:312
TransportType toTransportType(const resip::Data &transportName)
bool isSecure(TransportType type)
Returns true if passed in transport type is a secure transport protocol.
int closeSocket(Socket fd)
Definition Socket.cxx:134
TransportType
An enumeration of transport protocols.
@ UNKNOWN_TRANSPORT
H_Identitys h_Identities
Definition Headers.cxx:315
RequestLineType h_RequestLine
Definition Headers.cxx:347
int getErrno()
Definition Socket.hxx:97
std::string strError(int err)
Definition compat.hxx:274
EncodeStream & operator<<(EncodeStream &strm, const BaseException &e)
#define EncodeStream
Represents an IP-address and port (V4 or V6).