Fixed DevStudio 2003 build with memory check code.
[pwlib.git] / src / ptclib / psockbun.cxx
blob67fb88e531867ad98e9915772de20251c32783a6
1 /*
2 * psockbun.cxx
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
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Post Increment
24 * Contributor(s): ______________________________________.
26 * $Log$
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().
44 * Added more logging.
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
67 * Fix compilation
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 //////////////////////////////////////////////////
79 #ifdef __GNUC__
80 #pragma implementation "psockbun.h"
81 #endif
83 #include <ptlib.h>
84 #include <ptclib/psockbun.h>
85 #include <ptclib/pstun.h>
88 #define new PNEW
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)
124 , updateThread(NULL)
125 , interfaceFilter(NULL)
127 PInterfaceMonitorInstanceMutex.Wait();
128 PAssert(PInterfaceMonitorInstance == NULL, PLogicError);
129 PInterfaceMonitorInstance = this;
130 PInterfaceMonitorInstanceMutex.Signal();
134 PInterfaceMonitor::~PInterfaceMonitor()
136 Stop();
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)
159 return FALSE;
161 PIPSocket::GetInterfaceTable(currentInterfaces);
162 updateThread = new PThreadObj<PInterfaceMonitor>(*this, &PInterfaceMonitor::UpdateThreadMain);
163 return TRUE;
167 void PInterfaceMonitor::Stop()
169 mutex.Wait();
171 // shutdown the update thread
172 if (updateThread != NULL) {
173 threadRunning.Signal();
175 mutex.Signal();
176 updateThread->WaitForTermination();
177 mutex.Wait();
179 delete updateThread;
180 updateThread = NULL;
183 mutex.Signal();
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()))
193 return TRUE;
195 return FALSE;
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))
205 return FALSE;
208 return TRUE;
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())
217 return FALSE;
219 // ensure every element in list1 is in list2
220 if (!InterfaceListIsSubsetOf(list1, list2))
221 return FALSE;
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
243 PINDEX i;
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
265 do {
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,
281 PString & name)
283 if (iface.IsEmpty())
284 return FALSE;
286 PINDEX percent = iface.Find('%');
287 switch (percent) {
288 case 0 :
289 address = PIPSocket::GetDefaultIpAny();
290 name = iface.Mid(1);
291 return !name.IsEmpty();
293 case P_MAX_INDEX :
294 address = iface;
295 name = PString::Empty();
296 return !address.IsAny();
299 if (iface[0] == '*')
300 address = PIPSocket::GetDefaultIpAny();
301 else
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);
319 PStringArray names;
321 names.SetSize(ifaces.GetSize());
322 PINDEX count = 0;
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);
332 return names;
336 BOOL PInterfaceMonitor::GetInterfaceInfo(const PString & iface, PIPSocket::InterfaceEntry & info)
338 PIPSocket::Address addr;
339 PString name;
340 if (!SplitInterfaceDescription(iface, addr, name))
341 return FALSE;
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)) {
349 info = entry;
350 return TRUE;
354 return FALSE;
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())
372 Start();
373 currentClients.push_back(client);
377 void PInterfaceMonitor::RemoveClient(PInterfaceMonitorClient * client)
379 mutex.Wait();
380 currentClients.remove(client);
381 bool stop = currentClients.empty();
382 mutex.Signal();
383 if (stop)
384 Stop();
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)
419 : localPort(0)
420 , reuseAddress(reuseAddr)
421 , stun(stunClient)
426 BOOL PMonitoredSockets::CreateSocket(SocketInfo & info, const PIPSocket::Address & binding)
428 delete info.socket;
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());
434 return TRUE;
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());
440 return true;
443 delete info.socket;
444 return false;
448 BOOL PMonitoredSockets::DestroySocket(SocketInfo & info)
450 if (info.socket == NULL)
451 return FALSE;
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
458 while (info.inUse) {
459 UnlockReadWrite();
460 PThread::Sleep(20);
461 if (!LockReadWrite())
462 return FALSE;
463 if (--failSafe == 0) {
464 PTRACE(1, "UDP\tClose of bundled socket " << info.socket << " taking too long.");
465 break;
469 delete info.socket;
470 info.socket = NULL;
472 return result;
476 BOOL PMonitoredSockets::GetSocketAddress(const SocketInfo & info,
477 PIPSocket::Address & address,
478 WORD & port,
479 BOOL usingNAT) const
481 if (info.socket == NULL)
482 return FALSE;
484 return usingNAT ? info.socket->GetLocalAddress(address, port)
485 : info.socket->PUDPSocket::GetLocalAddress(address, port);
489 BOOL PMonitoredSockets::WriteToSocket(const void * buf,
490 PINDEX len,
491 const PIPSocket::Address & addr,
492 WORD port,
493 const SocketInfo & info,
494 PINDEX & lastWriteCount)
496 #ifndef __BEOS__
497 if (addr.IsBroadcast()) {
498 if (!info.socket->SetOption(SO_BROADCAST, 1)) {
499 PTRACE(2, "UDP\tError allowing broadcast: " << info.socket->GetErrorText());
500 return FALSE;
503 #else
504 PTRACE(3, "RAS\tBroadcast option under BeOS is not implemented yet");
505 #endif
507 BOOL ok = info.socket->WriteTo(buf, len, addr, port);
509 #ifndef __BEOS__
510 if (addr.IsBroadcast())
511 info.socket->SetOption(SO_BROADCAST, 0);
512 #endif
514 lastWriteCount = info.socket->GetLastWriteCount();
515 return ok;
519 BOOL PMonitoredSockets::ReadFromSocket(SocketInfo & info,
520 void * buf,
521 PINDEX len,
522 PIPSocket::Address & addr,
523 WORD & port,
524 PINDEX & lastReadCount,
525 const PTimeInterval & timeout)
527 // Assume is already locked
529 if (info.inUse) {
530 PTRACE(2, "UDP\tCannot read from multiple threads.");
531 return FALSE;
534 info.inUse = true;
536 info.socket->SetReadTimeout(timeout);
538 UnlockReadWrite();
539 BOOL ok = info.socket->ReadFrom(buf, len, addr, port);
540 if (!LockReadWrite())
541 return FALSE;
543 lastReadCount = info.socket->GetLastReadCount();
545 info.inUse = false;
547 PTRACE_IF(2, !ok, "UDP\tSocket read failure: " << info.socket->GetErrorText(PChannel::LastReadError));
549 return ok;
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);
557 else
558 return new PSingleMonitoredSocket(iface, reuseAddr, stunClient);
562 //////////////////////////////////////////////////
564 PMonitoredSocketChannel::PMonitoredSocketChannel(const PMonitoredSocketsPtr & sock)
565 : socketBundle(sock)
566 , promiscuousReads(false)
567 , closing(FALSE)
568 , remotePort(0)
569 , lastReceivedAddress(PIPSocket::GetDefaultIpAny())
570 , lastReceivedPort(0)
575 BOOL PMonitoredSocketChannel::IsOpen() const
577 return !closing && socketBundle != NULL && socketBundle->IsOpen();
581 BOOL PMonitoredSocketChannel::Close()
583 closing = TRUE;
584 return TRUE;
588 BOOL PMonitoredSocketChannel::Read(void * buffer, PINDEX length)
590 if (!IsOpen())
591 return FALSE;
593 do {
594 PString iface = GetInterface();
595 if (!socketBundle->ReadFrom(buffer, length, lastReceivedAddress, lastReceivedPort, iface, lastReadCount, readTimeout))
596 return FALSE;
598 if (promiscuousReads)
599 return TRUE;
601 if (remoteAddress.IsAny())
602 remoteAddress = lastReceivedAddress;
603 if (remotePort == 0)
604 remotePort = lastReceivedPort;
606 } while (remoteAddress != lastReceivedAddress || remotePort != lastReceivedPort);
607 return TRUE;
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);
622 else
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;
645 remotePort = port;
649 void PMonitoredSocketChannel::SetRemote(const PString & hostAndPort)
651 PINDEX colon = hostAndPort.Find(':');
652 if (colon == P_MAX_INDEX)
653 remoteAddress = hostAndPort;
654 else {
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)
665 , closing(FALSE)
670 PMonitoredSocketBundle::~PMonitoredSocketBundle()
672 Close();
676 BOOL PMonitoredSocketBundle::Open(WORD port)
678 PSafeLockReadWrite guard(*this);
680 if (IsOpen() && localPort != 0 && localPort == port)
681 return TRUE;
683 closing = FALSE;
685 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())
709 return FALSE;
711 closing = TRUE;
713 while (!socketInfoMap.empty())
714 CloseSocket(socketInfoMap.begin());
716 UnlockReadWrite();
718 return TRUE;
722 BOOL PMonitoredSocketBundle::GetAddress(const PString & iface,
723 PIPSocket::Address & address,
724 WORD & port,
725 BOOL usingNAT) const
727 PSafeLockReadOnly guard(*this);
729 if (!guard.IsLocked())
730 return FALSE;
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;
740 PString name;
741 SplitInterfaceDescription(iface, binding, name);
743 SocketInfo info;
744 if (CreateSocket(info, binding)) {
745 if (localPort == 0)
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())
757 return;
759 DestroySocket(iterSocket->second);
760 socketInfoMap.erase(iterSocket);
764 BOOL PMonitoredSocketBundle::WriteTo(const void * buf,
765 PINDEX len,
766 const PIPSocket::Address & addr,
767 WORD port,
768 const PString & iface,
769 PINDEX & lastWriteCount)
771 if (!LockReadWrite())
772 return FALSE;
774 BOOL ok = TRUE;
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))
779 ok = FALSE;
782 else {
783 SocketInfoMap_T::iterator iter = socketInfoMap.find(iface);
784 ok = iter != socketInfoMap.end() && WriteToSocket(buf, len, addr, port, iter->second, lastWriteCount);
787 UnlockReadWrite();
789 return ok;
793 BOOL PMonitoredSocketBundle::ReadFrom(void * buf,
794 PINDEX len,
795 PIPSocket::Address & addr,
796 WORD & port,
797 PString & iface,
798 PINDEX & lastReadCount,
799 const PTimeInterval & timeout)
801 BOOL ok = FALSE;
803 if (!LockReadWrite())
804 return FALSE;
806 if (iface.IsEmpty()) {
807 for (;;) {
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.");
814 UnlockReadWrite();
815 return FALSE;
817 if (iter->second.socket->IsOpen()) {
818 readers += *iter->second.socket;
819 iter->second.inUse = true;
822 readers += interfaceAddedSignal;
824 UnlockReadWrite();
825 PChannel::Errors errorCode = PSocket::Select(readers, timeout);
826 if (!LockReadWrite())
827 return FALSE;
829 PUDPSocket * socket = NULL;
830 if (errorCode != PChannel::NoError) {
831 PTRACE(2, "UDP\tMulti-interface read select failure: " << errorCode);
833 else {
834 socket = (PUDPSocket *)&readers[0];
835 ok = socket->ReadFrom(buf, len, addr, port);
836 if (ok)
837 lastReadCount = socket->GetLastReadCount();
838 else {
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)
847 iface = iter->first;
848 iter->second.inUse = false;
851 if (interfaceAddedSignal.IsOpen())
852 break;
854 interfaceAddedSignal.Listen();
857 else {
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);
864 UnlockReadWrite();
866 return ok;
870 void PMonitoredSocketBundle::OnAddInterface(const InterfaceEntry & entry)
872 // Already locked
873 if (closing)
874 return;
876 OpenSocket(MakeInterfaceDescription(entry));
877 PTRACE(3, "UDP\tSocket bundle has added interface " << entry);
878 interfaceAddedSignal.Close();
882 void PMonitoredSocketBundle::OnRemoveInterface(const InterfaceEntry & entry)
884 // Already locked
885 if (closing)
886 return;
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);
912 PStringList names;
913 if (!theEntry.GetAddress().IsAny())
914 names.AppendString(MakeInterfaceDescription(theEntry));
915 return names;
919 BOOL PSingleMonitoredSocket::Open(WORD port)
921 PSafeLockReadWrite guard(*this);
923 if (theEntry.GetAddress().IsAny()) {
924 if (!GetInterfaceInfo(theInterface, theEntry))
925 return FALSE;
928 localPort = port;
929 if (!CreateSocket(theInfo, theEntry.GetAddress()))
930 return FALSE;
932 localPort = theInfo.socket->PUDPSocket::GetPort();
933 return TRUE;
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,
955 WORD & port,
956 BOOL usingNAT) const
958 PSafeLockReadOnly guard(*this);
960 return guard.IsLocked() && IsInterface(iface) && GetSocketAddress(theInfo, address, port, usingNAT);
964 BOOL PSingleMonitoredSocket::WriteTo(const void * buf,
965 PINDEX len,
966 const PIPSocket::Address & addr,
967 WORD port,
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);
976 return FALSE;
980 BOOL PSingleMonitoredSocket::ReadFrom(void * buf,
981 PINDEX len,
982 PIPSocket::Address & addr,
983 WORD & port,
984 PString & iface,
985 PINDEX & lastReadCount,
986 const PTimeInterval & timeout)
988 if (!LockReadWrite())
989 return FALSE;
991 BOOL ok = FALSE;
993 if (IsInterface(iface))
994 ok = ReadFromSocket(theInfo, buf, len, addr, port, lastReadCount, timeout);
996 UnlockReadWrite();
998 return ok;
1002 void PSingleMonitoredSocket::OnAddInterface(const InterfaceEntry & entry)
1004 // Already locked
1006 PIPSocket::Address addr;
1007 PString name;
1008 if (!SplitInterfaceDescription(theInterface, addr, name))
1009 return;
1011 if (entry.GetAddress() == addr && entry.GetName().NumCompare(name) == EqualTo) {
1012 theEntry = entry;
1013 if (!Open(localPort))
1014 theEntry = InterfaceEntry();
1015 else {
1016 PTRACE(3, "UDP\tBound socket UP on interface " << theEntry);
1022 void PSingleMonitoredSocket::OnRemoveInterface(const InterfaceEntry & entry)
1024 // Already locked
1026 if (entry != theEntry)
1027 return;
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())
1038 return TRUE;
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));