4 * Socket and interface bundle code
6 * Portable Windows Library
8 * Copyright (C) 2007 Post Increment
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Post Increment
24 * Contributor(s): ______________________________________.
27 * Revision 1.14 2007/09/08 11:34:28 rjongbloed
28 * Improved memory checking (leaks etc), especially when using MSVC debug library.
30 * Revision 1.13 2007/08/26 20:01:58 hfriederich
31 * Allow to filter interfaces based on remote address
33 * Revision 1.12 2007/08/22 05:08:26 rjongbloed
34 * Fixed issue where if a bundled socket using STUN to be on specific local address,
35 * eg sip an port 5060 can still accept calls from local network on that port.
37 * Revision 1.11 2007/07/22 04:03:32 rjongbloed
38 * Fixed issues with STUN usage in socket bundling, now OpalTransport indicates
39 * if it wants local or NAT address/port for inclusion to outgoing PDUs.
41 * Revision 1.10 2007/07/03 08:55:18 rjongbloed
42 * Fixed various issues with handling interfaces going up, eg not being added
43 * to currently active ReadFrom().
46 * Revision 1.9 2007/07/01 15:23:00 dsandras
47 * Removed accidental log message.
49 * Revision 1.7 2007/06/25 05:44:01 rjongbloed
50 * Fixed numerous issues with "bound" managed socket, ie associating
51 * listeners to a specific named interface.
53 * Revision 1.6 2007/06/22 04:51:40 rjongbloed
54 * Fixed missing mutex release in socket bundle interface monitor thread shut down.
56 * Revision 1.5 2007/06/17 03:17:52 rjongbloed
57 * Added using empty interface string as "just use predefined fixed interface"
59 * Revision 1.4 2007/06/10 06:26:54 rjongbloed
60 * Major enhancements to the "socket bundling" feature:
61 * singleton thread for monitoring network interfaces
62 * a generic API for anything to be informed of interface changes
63 * PChannel derived class for reading/writing to bundled sockets
64 * many new API functions
66 * Revision 1.3 2007/05/28 11:26:50 hfriederich
69 * Revision 1.2 2007/05/22 11:50:57 csoutheren
70 * Further implementation of socket bundle
72 * Revision 1.1 2007/05/21 06:07:17 csoutheren
73 * Add new socket bundle code to be used to OpalUDPListener
77 //////////////////////////////////////////////////
80 #pragma implementation "psockbun.h"
84 #include <ptclib/psockbun.h>
85 #include <ptclib/pstun.h>
91 //////////////////////////////////////////////////
93 PInterfaceMonitorClient::PInterfaceMonitorClient()
95 PInterfaceMonitor::GetInstance().AddClient(this);
99 PInterfaceMonitorClient::~PInterfaceMonitorClient()
101 PInterfaceMonitor::GetInstance().RemoveClient(this);
105 PStringArray
PInterfaceMonitorClient::GetInterfaces(BOOL includeLoopBack
, const PIPSocket::Address
& destination
)
107 return PInterfaceMonitor::GetInstance().GetInterfaces(includeLoopBack
, destination
);
111 BOOL
PInterfaceMonitorClient::GetInterfaceInfo(const PString
& iface
, InterfaceEntry
& info
)
113 return PInterfaceMonitor::GetInstance().GetInterfaceInfo(iface
, info
);
117 //////////////////////////////////////////////////
119 static PMutex PInterfaceMonitorInstanceMutex
;
120 static PInterfaceMonitor
* PInterfaceMonitorInstance
;
122 PInterfaceMonitor::PInterfaceMonitor(unsigned refresh
)
123 : refreshInterval(refresh
)
125 , interfaceFilter(NULL
)
127 PInterfaceMonitorInstanceMutex
.Wait();
128 PAssert(PInterfaceMonitorInstance
== NULL
, PLogicError
);
129 PInterfaceMonitorInstance
= this;
130 PInterfaceMonitorInstanceMutex
.Signal();
134 PInterfaceMonitor::~PInterfaceMonitor()
138 delete interfaceFilter
;
142 PInterfaceMonitor
& PInterfaceMonitor::GetInstance()
144 PInterfaceMonitorInstanceMutex
.Wait();
145 if (PInterfaceMonitorInstance
== NULL
) {
146 static PInterfaceMonitor theInstance
;
148 PInterfaceMonitorInstanceMutex
.Signal();
150 return *PInterfaceMonitorInstance
;
154 BOOL
PInterfaceMonitor::Start()
156 PWaitAndSignal
m(mutex
);
158 if (updateThread
!= NULL
)
161 PIPSocket::GetInterfaceTable(currentInterfaces
);
162 updateThread
= new PThreadObj
<PInterfaceMonitor
>(*this, &PInterfaceMonitor::UpdateThreadMain
);
167 void PInterfaceMonitor::Stop()
171 // shutdown the update thread
172 if (updateThread
!= NULL
) {
173 threadRunning
.Signal();
176 updateThread
->WaitForTermination();
187 static BOOL
IsInterfaceInList(const PIPSocket::InterfaceEntry
& entry
,
188 const PIPSocket::InterfaceTable
& list
)
190 for (PINDEX i
= 0; i
< list
.GetSize(); ++i
) {
191 PIPSocket::InterfaceEntry
& listEntry
= list
[i
];
192 if ((entry
.GetName() == listEntry
.GetName()) && (entry
.GetAddress() == listEntry
.GetAddress()))
199 static BOOL
InterfaceListIsSubsetOf(const PIPSocket::InterfaceTable
& subset
,
200 const PIPSocket::InterfaceTable
& set
)
202 for (PINDEX i
= 0; i
< subset
.GetSize(); ++i
) {
203 PIPSocket::InterfaceEntry
& entry
= subset
[i
];
204 if (!IsInterfaceInList(entry
, set
))
212 static BOOL
CompareInterfaceLists(const PIPSocket::InterfaceTable
& list1
,
213 const PIPSocket::InterfaceTable
& list2
)
215 // if the sizes are different, then the list has changed.
216 if (list1
.GetSize() != list2
.GetSize())
219 // ensure every element in list1 is in list2
220 if (!InterfaceListIsSubsetOf(list1
, list2
))
223 // ensure every element in list1 is in list2
224 return InterfaceListIsSubsetOf(list2
, list1
);
228 void PInterfaceMonitor::RefreshInterfaceList()
230 // get a new interface list
231 PIPSocket::InterfaceTable newInterfaces
;
232 PIPSocket::GetInterfaceTable(newInterfaces
);
234 PWaitAndSignal
m(mutex
);
236 // if changed, then update the internal list
237 if (!CompareInterfaceLists(currentInterfaces
, newInterfaces
)) {
239 PIPSocket::InterfaceTable oldInterfaces
= currentInterfaces
;
240 currentInterfaces
= newInterfaces
;
242 // look for interfaces to add that are in new list that are not in the old list
244 for (i
= 0; i
< newInterfaces
.GetSize(); ++i
) {
245 PIPSocket::InterfaceEntry
& newEntry
= newInterfaces
[i
];
246 if (!newEntry
.GetAddress().IsLoopback() && !IsInterfaceInList(newEntry
, oldInterfaces
))
247 OnAddInterface(newEntry
);
250 // look for interfaces to remove that are in old list that are not in the new list
251 for (i
= 0; i
< oldInterfaces
.GetSize(); ++i
) {
252 PIPSocket::InterfaceEntry
& oldEntry
= oldInterfaces
[i
];
253 if (!oldEntry
.GetAddress().IsLoopback() && !IsInterfaceInList(oldEntry
, newInterfaces
))
254 OnRemoveInterface(oldEntry
);
260 void PInterfaceMonitor::UpdateThreadMain()
262 PTRACE(4, "UDP\tStarted interface monitor thread.");
264 // check for interface changes periodically
266 RefreshInterfaceList();
267 } while (!threadRunning
.Wait(refreshInterval
));
269 PTRACE(4, "UDP\tFinished interface monitor thread.");
273 static PString
MakeInterfaceDescription(const PIPSocket::InterfaceEntry
& entry
)
275 return entry
.GetAddress().AsString() + '%' + entry
.GetName();
279 static BOOL
SplitInterfaceDescription(const PString
& iface
,
280 PIPSocket::Address
& address
,
286 PINDEX percent
= iface
.Find('%');
289 address
= PIPSocket::GetDefaultIpAny();
291 return !name
.IsEmpty();
295 name
= PString::Empty();
296 return !address
.IsAny();
300 address
= PIPSocket::GetDefaultIpAny();
302 address
= iface
.Left(percent
);
303 name
= iface
.Mid(percent
+1);
304 return !name
.IsEmpty();
308 PStringArray
PInterfaceMonitor::GetInterfaces(BOOL includeLoopBack
,
309 const PIPSocket::Address
& destination
)
311 PWaitAndSignal
guard(mutex
);
313 PIPSocket::InterfaceTable ifaces
= currentInterfaces
;
315 if (interfaceFilter
!= NULL
&& !destination
.IsAny()) {
316 ifaces
= interfaceFilter
->FilterInterfaces(destination
, ifaces
);
321 names
.SetSize(ifaces
.GetSize());
324 for (PINDEX i
= 0; i
< ifaces
.GetSize(); ++i
) {
325 PIPSocket::InterfaceEntry
& entry
= ifaces
[i
];
326 if (includeLoopBack
|| !entry
.GetAddress().IsLoopback())
327 names
[count
++] = MakeInterfaceDescription(entry
);
330 names
.SetSize(count
);
336 BOOL
PInterfaceMonitor::GetInterfaceInfo(const PString
& iface
, PIPSocket::InterfaceEntry
& info
)
338 PIPSocket::Address addr
;
340 if (!SplitInterfaceDescription(iface
, addr
, name
))
343 PWaitAndSignal
m(mutex
);
345 for (PINDEX i
= 0; i
< currentInterfaces
.GetSize(); ++i
) {
346 PIPSocket::InterfaceEntry
& entry
= currentInterfaces
[i
];
347 if ((addr
.IsAny() || entry
.GetAddress() == addr
) &&
348 (name
.IsEmpty() || entry
.GetName().NumCompare(name
) == EqualTo
)) {
358 void PInterfaceMonitor::SetInterfaceFilter(PInterfaceFilter
* filter
)
360 PWaitAndSignal
m(mutex
);
362 delete interfaceFilter
;
363 interfaceFilter
= filter
;
367 void PInterfaceMonitor::AddClient(PInterfaceMonitorClient
* client
)
369 PWaitAndSignal
m(mutex
);
371 if (currentClients
.empty())
373 currentClients
.push_back(client
);
377 void PInterfaceMonitor::RemoveClient(PInterfaceMonitorClient
* client
)
380 currentClients
.remove(client
);
381 bool stop
= currentClients
.empty();
388 void PInterfaceMonitor::OnAddInterface(const PIPSocket::InterfaceEntry
& entry
)
390 PWaitAndSignal
m(mutex
);
392 for (ClientList_T::iterator iter
= currentClients
.begin(); iter
!= currentClients
.end(); ++iter
) {
393 PInterfaceMonitorClient
* client
= *iter
;
394 if (client
->LockReadWrite()) {
395 client
->OnAddInterface(entry
);
396 client
->UnlockReadWrite();
402 void PInterfaceMonitor::OnRemoveInterface(const PIPSocket::InterfaceEntry
& entry
)
404 PWaitAndSignal
m(mutex
);
406 for (ClientList_T::iterator iter
= currentClients
.begin(); iter
!= currentClients
.end(); ++iter
) {
407 PInterfaceMonitorClient
* client
= *iter
;
408 if (client
->LockReadWrite()) {
409 client
->OnRemoveInterface(entry
);
410 client
->UnlockReadWrite();
416 //////////////////////////////////////////////////
418 PMonitoredSockets::PMonitoredSockets(BOOL reuseAddr
, PSTUNClient
* stunClient
)
420 , reuseAddress(reuseAddr
)
426 BOOL
PMonitoredSockets::CreateSocket(SocketInfo
& info
, const PIPSocket::Address
& binding
)
430 if (stun
!= NULL
&& stun
->CreateSocket(info
.socket
, binding
, localPort
)) {
431 PTRACE(4, "UDP\tCreated bundled socket via STUN internal="
432 << binding
<< ':' << info
.socket
->PUDPSocket::GetPort()
433 << " external=" << info
.socket
->GetLocalAddress());
437 info
.socket
= new PUDPSocket
;
438 if (info
.socket
->Listen(binding
, 0, localPort
, reuseAddress
?PIPSocket::CanReuseAddress
:PIPSocket::AddressIsExclusive
)) {
439 PTRACE(4, "UDP\tCreated bundled socket " << binding
<< ':' << info
.socket
->GetPort());
448 BOOL
PMonitoredSockets::DestroySocket(SocketInfo
& info
)
450 if (info
.socket
== NULL
)
453 BOOL result
= info
.socket
->Close();
454 PTRACE_IF(4, result
, "UDP\tClosed bundled socket " << info
.socket
);
456 // This is pretty ugly, but needed to make sure multi-threading doesn't crash
457 unsigned failSafe
= 100; // Approx. two seconds
461 if (!LockReadWrite())
463 if (--failSafe
== 0) {
464 PTRACE(1, "UDP\tClose of bundled socket " << info
.socket
<< " taking too long.");
476 BOOL
PMonitoredSockets::GetSocketAddress(const SocketInfo
& info
,
477 PIPSocket::Address
& address
,
481 if (info
.socket
== NULL
)
484 return usingNAT
? info
.socket
->GetLocalAddress(address
, port
)
485 : info
.socket
->PUDPSocket::GetLocalAddress(address
, port
);
489 BOOL
PMonitoredSockets::WriteToSocket(const void * buf
,
491 const PIPSocket::Address
& addr
,
493 const SocketInfo
& info
,
494 PINDEX
& lastWriteCount
)
497 if (addr
.IsBroadcast()) {
498 if (!info
.socket
->SetOption(SO_BROADCAST
, 1)) {
499 PTRACE(2, "UDP\tError allowing broadcast: " << info
.socket
->GetErrorText());
504 PTRACE(3, "RAS\tBroadcast option under BeOS is not implemented yet");
507 BOOL ok
= info
.socket
->WriteTo(buf
, len
, addr
, port
);
510 if (addr
.IsBroadcast())
511 info
.socket
->SetOption(SO_BROADCAST
, 0);
514 lastWriteCount
= info
.socket
->GetLastWriteCount();
519 BOOL
PMonitoredSockets::ReadFromSocket(SocketInfo
& info
,
522 PIPSocket::Address
& addr
,
524 PINDEX
& lastReadCount
,
525 const PTimeInterval
& timeout
)
527 // Assume is already locked
530 PTRACE(2, "UDP\tCannot read from multiple threads.");
536 info
.socket
->SetReadTimeout(timeout
);
539 BOOL ok
= info
.socket
->ReadFrom(buf
, len
, addr
, port
);
540 if (!LockReadWrite())
543 lastReadCount
= info
.socket
->GetLastReadCount();
547 PTRACE_IF(2, !ok
, "UDP\tSocket read failure: " << info
.socket
->GetErrorText(PChannel::LastReadError
));
553 PMonitoredSockets
* PMonitoredSockets::Create(const PString
& iface
, BOOL reuseAddr
, PSTUNClient
* stunClient
)
555 if (iface
.IsEmpty() || iface
== "*" || PIPSocket::Address(iface
).IsAny())
556 return new PMonitoredSocketBundle(reuseAddr
, stunClient
);
558 return new PSingleMonitoredSocket(iface
, reuseAddr
, stunClient
);
562 //////////////////////////////////////////////////
564 PMonitoredSocketChannel::PMonitoredSocketChannel(const PMonitoredSocketsPtr
& sock
)
566 , promiscuousReads(false)
569 , lastReceivedAddress(PIPSocket::GetDefaultIpAny())
570 , lastReceivedPort(0)
575 BOOL
PMonitoredSocketChannel::IsOpen() const
577 return !closing
&& socketBundle
!= NULL
&& socketBundle
->IsOpen();
581 BOOL
PMonitoredSocketChannel::Close()
588 BOOL
PMonitoredSocketChannel::Read(void * buffer
, PINDEX length
)
594 PString iface
= GetInterface();
595 if (!socketBundle
->ReadFrom(buffer
, length
, lastReceivedAddress
, lastReceivedPort
, iface
, lastReadCount
, readTimeout
))
598 if (promiscuousReads
)
601 if (remoteAddress
.IsAny())
602 remoteAddress
= lastReceivedAddress
;
604 remotePort
= lastReceivedPort
;
606 } while (remoteAddress
!= lastReceivedAddress
|| remotePort
!= lastReceivedPort
);
611 BOOL
PMonitoredSocketChannel::Write(const void * buffer
, PINDEX length
)
613 return socketBundle
!= NULL
&& socketBundle
->WriteTo(buffer
, length
, remoteAddress
, remotePort
, GetInterface(), lastWriteCount
);
617 void PMonitoredSocketChannel::SetInterface(const PString
& iface
)
619 PIPSocket::InterfaceEntry info
;
620 if (socketBundle
!= NULL
&& socketBundle
->GetInterfaceInfo(iface
, info
))
621 currentInterface
= MakeInterfaceDescription(info
);
623 currentInterface
= iface
;
627 const PString
& PMonitoredSocketChannel::GetInterface()
629 if (currentInterface
.Find('%') == P_MAX_INDEX
)
630 SetInterface(currentInterface
);
632 return currentInterface
;
636 BOOL
PMonitoredSocketChannel::GetLocal(PIPSocket::Address
& address
, WORD
& port
, BOOL usingNAT
)
638 return socketBundle
->GetAddress(GetInterface(), address
, port
, usingNAT
);
642 void PMonitoredSocketChannel::SetRemote(const PIPSocket::Address
& address
, WORD port
)
644 remoteAddress
= address
;
649 void PMonitoredSocketChannel::SetRemote(const PString
& hostAndPort
)
651 PINDEX colon
= hostAndPort
.Find(':');
652 if (colon
== P_MAX_INDEX
)
653 remoteAddress
= hostAndPort
;
655 remoteAddress
= hostAndPort
.Left(colon
);
656 remotePort
= PIPSocket::GetPortByService("udp", hostAndPort
.Mid(colon
+1));
661 //////////////////////////////////////////////////
663 PMonitoredSocketBundle::PMonitoredSocketBundle(BOOL reuseAddr
, PSTUNClient
* stunClient
)
664 : PMonitoredSockets(reuseAddr
, stunClient
)
670 PMonitoredSocketBundle::~PMonitoredSocketBundle()
676 BOOL
PMonitoredSocketBundle::Open(WORD port
)
678 PSafeLockReadWrite
guard(*this);
680 if (IsOpen() && localPort
!= 0 && localPort
== port
)
687 // Close and re-open all sockets
688 while (!socketInfoMap
.empty())
689 CloseSocket(socketInfoMap
.begin());
691 PStringArray interfaces
= GetInterfaces();
692 for (PINDEX i
= 0; i
< interfaces
.GetSize(); ++i
)
693 OpenSocket(interfaces
[i
]);
695 return !socketInfoMap
.empty();
699 BOOL
PMonitoredSocketBundle::IsOpen() const
701 PSafeLockReadOnly
guard(*this);
702 return !closing
&& !socketInfoMap
.empty();
706 BOOL
PMonitoredSocketBundle::Close()
708 if (!LockReadWrite())
713 while (!socketInfoMap
.empty())
714 CloseSocket(socketInfoMap
.begin());
722 BOOL
PMonitoredSocketBundle::GetAddress(const PString
& iface
,
723 PIPSocket::Address
& address
,
727 PSafeLockReadOnly
guard(*this);
729 if (!guard
.IsLocked())
732 SocketInfoMap_T::const_iterator iter
= socketInfoMap
.find(iface
);
733 return iter
!= socketInfoMap
.end() && GetSocketAddress(iter
->second
, address
, port
, usingNAT
);
737 void PMonitoredSocketBundle::OpenSocket(const PString
& iface
)
739 PIPSocket::Address binding
;
741 SplitInterfaceDescription(iface
, binding
, name
);
744 if (CreateSocket(info
, binding
)) {
746 localPort
= info
.socket
->GetPort();
747 socketInfoMap
[iface
] = info
;
752 void PMonitoredSocketBundle::CloseSocket(const SocketInfoMap_T::iterator
& iterSocket
)
754 //Already locked by caller
756 if (iterSocket
== socketInfoMap
.end())
759 DestroySocket(iterSocket
->second
);
760 socketInfoMap
.erase(iterSocket
);
764 BOOL
PMonitoredSocketBundle::WriteTo(const void * buf
,
766 const PIPSocket::Address
& addr
,
768 const PString
& iface
,
769 PINDEX
& lastWriteCount
)
771 if (!LockReadWrite())
776 if (iface
.IsEmpty()) {
777 for (SocketInfoMap_T::iterator iter
= socketInfoMap
.begin(); iter
!= socketInfoMap
.end(); ++iter
) {
778 if (!WriteToSocket(buf
, len
, addr
, port
, iter
->second
, lastWriteCount
))
783 SocketInfoMap_T::iterator iter
= socketInfoMap
.find(iface
);
784 ok
= iter
!= socketInfoMap
.end() && WriteToSocket(buf
, len
, addr
, port
, iter
->second
, lastWriteCount
);
793 BOOL
PMonitoredSocketBundle::ReadFrom(void * buf
,
795 PIPSocket::Address
& addr
,
798 PINDEX
& lastReadCount
,
799 const PTimeInterval
& timeout
)
803 if (!LockReadWrite())
806 if (iface
.IsEmpty()) {
808 // If interface is empty, then grab the next datagram on any of the interfaces
809 PSocket::SelectList readers
;
811 for (SocketInfoMap_T::iterator iter
= socketInfoMap
.begin(); iter
!= socketInfoMap
.end(); ++iter
) {
812 if (iter
->second
.inUse
) {
813 PTRACE(2, "UDP\tCannot read from multiple threads.");
817 if (iter
->second
.socket
->IsOpen()) {
818 readers
+= *iter
->second
.socket
;
819 iter
->second
.inUse
= true;
822 readers
+= interfaceAddedSignal
;
825 PChannel::Errors errorCode
= PSocket::Select(readers
, timeout
);
826 if (!LockReadWrite())
829 PUDPSocket
* socket
= NULL
;
830 if (errorCode
!= PChannel::NoError
) {
831 PTRACE(2, "UDP\tMulti-interface read select failure: " << errorCode
);
834 socket
= (PUDPSocket
*)&readers
[0];
835 ok
= socket
->ReadFrom(buf
, len
, addr
, port
);
837 lastReadCount
= socket
->GetLastReadCount();
839 PTRACE(2, "UDP\tSocket read failure: " << socket
->GetErrorText(PChannel::LastReadError
));
840 if (socket
->GetErrorCode(PChannel::LastReadError
) == PChannel::NotOpen
)
841 socket
->Close(); // If interface goes down, socket is not open to OS, but still is to us. Make them agree.
845 for (SocketInfoMap_T::iterator iter
= socketInfoMap
.begin(); iter
!= socketInfoMap
.end(); ++iter
) {
846 if (iter
->second
.socket
== socket
)
848 iter
->second
.inUse
= false;
851 if (interfaceAddedSignal
.IsOpen())
854 interfaceAddedSignal
.Listen();
858 // if interface is not empty, use that specific interface
859 SocketInfoMap_T::iterator iter
= socketInfoMap
.find(iface
);
860 if (iter
!= socketInfoMap
.end())
861 ok
= ReadFromSocket(iter
->second
, buf
, len
, addr
, port
, lastReadCount
, timeout
);
870 void PMonitoredSocketBundle::OnAddInterface(const InterfaceEntry
& entry
)
876 OpenSocket(MakeInterfaceDescription(entry
));
877 PTRACE(3, "UDP\tSocket bundle has added interface " << entry
);
878 interfaceAddedSignal
.Close();
882 void PMonitoredSocketBundle::OnRemoveInterface(const InterfaceEntry
& entry
)
888 CloseSocket(socketInfoMap
.find(MakeInterfaceDescription(entry
)));
889 PTRACE(3, "UDP\tSocket bundle has removed interface " << entry
);
893 //////////////////////////////////////////////////
895 PSingleMonitoredSocket::PSingleMonitoredSocket(const PString
& _theInterface
, BOOL reuseAddr
, PSTUNClient
* stunClient
)
896 : PMonitoredSocketBundle(reuseAddr
, stunClient
)
897 , theInterface(_theInterface
)
902 PSingleMonitoredSocket::~PSingleMonitoredSocket()
904 DestroySocket(theInfo
);
908 PStringArray
PSingleMonitoredSocket::GetInterfaces(BOOL
/*includeLoopBack*/)
910 PSafeLockReadOnly
guard(*this);
913 if (!theEntry
.GetAddress().IsAny())
914 names
.AppendString(MakeInterfaceDescription(theEntry
));
919 BOOL
PSingleMonitoredSocket::Open(WORD port
)
921 PSafeLockReadWrite
guard(*this);
923 if (theEntry
.GetAddress().IsAny()) {
924 if (!GetInterfaceInfo(theInterface
, theEntry
))
929 if (!CreateSocket(theInfo
, theEntry
.GetAddress()))
932 localPort
= theInfo
.socket
->PUDPSocket::GetPort();
937 BOOL
PSingleMonitoredSocket::IsOpen() const
939 PSafeLockReadOnly
guard(*this);
941 return theInfo
.socket
!= NULL
&& theInfo
.socket
->IsOpen();
945 BOOL
PSingleMonitoredSocket::Close()
947 PSafeLockReadWrite
guard(*this);
949 return DestroySocket(theInfo
);
953 BOOL
PSingleMonitoredSocket::GetAddress(const PString
& iface
,
954 PIPSocket::Address
& address
,
958 PSafeLockReadOnly
guard(*this);
960 return guard
.IsLocked() && IsInterface(iface
) && GetSocketAddress(theInfo
, address
, port
, usingNAT
);
964 BOOL
PSingleMonitoredSocket::WriteTo(const void * buf
,
966 const PIPSocket::Address
& addr
,
968 const PString
& iface
,
969 PINDEX
& lastWriteCount
)
971 PSafeLockReadWrite
guard(*this);
973 if (guard
.IsLocked() && theInfo
.socket
!= NULL
&& IsInterface(iface
))
974 return WriteToSocket(buf
, len
, addr
, port
, theInfo
, lastWriteCount
);
980 BOOL
PSingleMonitoredSocket::ReadFrom(void * buf
,
982 PIPSocket::Address
& addr
,
985 PINDEX
& lastReadCount
,
986 const PTimeInterval
& timeout
)
988 if (!LockReadWrite())
993 if (IsInterface(iface
))
994 ok
= ReadFromSocket(theInfo
, buf
, len
, addr
, port
, lastReadCount
, timeout
);
1002 void PSingleMonitoredSocket::OnAddInterface(const InterfaceEntry
& entry
)
1006 PIPSocket::Address addr
;
1008 if (!SplitInterfaceDescription(theInterface
, addr
, name
))
1011 if (entry
.GetAddress() == addr
&& entry
.GetName().NumCompare(name
) == EqualTo
) {
1013 if (!Open(localPort
))
1014 theEntry
= InterfaceEntry();
1016 PTRACE(3, "UDP\tBound socket UP on interface " << theEntry
);
1022 void PSingleMonitoredSocket::OnRemoveInterface(const InterfaceEntry
& entry
)
1026 if (entry
!= theEntry
)
1029 PTRACE(3, "UDP\tBound socket DOWN on interface " << theEntry
);
1030 theEntry
= InterfaceEntry();
1031 DestroySocket(theInfo
);
1035 BOOL
PSingleMonitoredSocket::IsInterface(const PString
& iface
) const
1037 if (iface
.IsEmpty())
1040 PINDEX percent1
= iface
.Find('%');
1041 PINDEX percent2
= theInterface
.Find('%');
1043 if (percent1
!= P_MAX_INDEX
&& percent2
!= P_MAX_INDEX
)
1044 return iface
.Mid(percent1
+1).NumCompare(theInterface
.Mid(percent2
+1)) == EqualTo
;
1046 return PIPSocket::Address(iface
.Left(percent1
)) == PIPSocket::Address(theInterface
.Left(percent2
));