/*
 * sockets.cxx
 *
 * Berkley sockets classes.
 *
 * Portable Windows Library
 *
 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
 *
 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
 * All Rights Reserved.
 *
 * Contributor(s): ______________________________________.
 *
 * $Log: sockets.cxx,v $
 * Revision 1.197.2.3  2007/01/03 22:37:37  dsandras
 * Backports from HEAD.
 *
 * Revision 1.197.2.2  2006/02/13 07:11:23  csoutheren
 * Backported fix from CVS head
 *
 * Revision 1.197.2.1  2006/02/11 12:09:33  csoutheren
 * Backport from CVS head
 *
 * Revision 1.201  2006/02/13 06:56:26  csoutheren
 * Fixed problem on Windows
 *
 * Revision 1.200  2006/02/10 23:56:58  csoutheren
 * Fixed compile problems on Debian and Windows
 *
 * Revision 1.199  2006/02/10 22:50:11  csoutheren
 * Fixed error in last commit
 *
 * Revision 1.198  2006/02/10 22:47:01  csoutheren
 * Ensure IPV6 addresses are not returned by IPSocket::GetHostAddress when
 * IPV4 has been forced using PIPSocket::SetDefaultIpAddressFamilyV4
 * This is intended to address the following Ekiga bug:
 * Bug 330388 . Cannot make calls to host with IPv6 address
 *
 * Revision 1.197  2005/11/21 11:49:36  shorne
 * Changed disableQos to disableGQoS to better reflect what it does
 *
 * Revision 1.196  2005/10/21 06:01:30  csoutheren
 * Fixed warning on VS.NET 2005
 *
 * Revision 1.195  2005/09/25 10:51:23  dominance
 * almost complete the mingw support. We'll be there soon. ;)
 *
 * Revision 1.194  2005/08/18 06:24:30  shorne
 * Reversed Last Patch
 *
 * Revision 1.193  2005/08/18 03:42:53  shorne
 * Chaeck for RSVP QoS Thx Zdenek
 *
 * Revision 1.192  2005/08/09 12:46:00  rjongbloed
 * Fixed some platforms where in6addr_any does not exist.
 *
 * Revision 1.191  2005/07/17 09:25:40  csoutheren
 * Fixed problem in IPV6 variant of PIPSocket::Address::IsLoopback
 * Thanks to Roger Hardiman
 *
 * Revision 1.190  2005/07/13 12:08:09  csoutheren
 * Fixed QoS patches to be more consistent with PWLib style and to allow Unix compatibility
 *
 * Revision 1.189  2005/07/13 11:48:54  csoutheren
 * Backported QOS changes from isvo branch
 *
 * Revision 1.188  2005/06/21 22:28:32  rjongbloed
 * Assured IP is set to zero, so if parse of dotted decimal fails is not random IP address.
 *
 * Revision 1.187.2.1  2005/04/25 13:42:28  shorne
 * Extended QoS support for per-call negotiation
 *
 * Revision 1.187  2005/03/22 07:29:30  csoutheren
 * Fixed problem where PStrings sometimes get case into
 * PIPSocket::Address when outputting to an ostream
 *
 * Revision 1.186  2005/02/13 23:01:36  csoutheren
 * Fixed problem with not detecting mapped IPV6 addresses within the RFC1918
 * address range as RFC1918
 *
 * Revision 1.185  2005/02/07 12:12:30  csoutheren
 * Expanded interface list routines to include IPV6 addresses
 * Added IPV6 to GetLocalAddress
 *
 * Revision 1.184  2005/02/07 00:47:18  csoutheren
 * Changed IPV6 code to use standard IPV6 macros
 *
 * Revision 1.183  2005/01/26 05:38:01  csoutheren
 * Added ability to remove config file support
 *
 * Revision 1.182  2005/01/16 21:27:07  csoutheren
 * Changed PIPSocket::IsAny to be const
 *
 * Revision 1.181  2005/01/16 20:35:41  csoutheren
 * Fixed problem with IPv6 INADDR_ANY
 *
 * Revision 1.180  2005/01/15 19:23:39  csoutheren
 * Fixed problem in operator *= for IP V6
 *
 * Revision 1.179  2004/12/14 14:24:20  csoutheren
 * Added PIPSocket::Address::operator*= to compare IPV4 addresses
 * to IPV4-compatible IPV6 addresses. More documentation needed
 * once this is tested as working
 *
 * Revision 1.178  2004/12/14 06:20:29  csoutheren
 * Added function to get address of network interface
 *
 * Revision 1.177  2004/11/16 00:31:44  csoutheren
 * Added Cygwin support (needs to have gethostbyname_r fixed)
 *
 * Revision 1.176  2004/10/26 18:27:28  ykiryanov
 * Added another for of SetOption to set SO_REUSEADDR for BeOS
 *
 * Revision 1.175  2004/08/24 07:08:11  csoutheren
 * Added use of recvmsg to determine which interface UDP packets arrive on
 *
 * Revision 1.174  2004/07/11 07:56:36  csoutheren
 * Applied jumbo VxWorks patch, thanks to Eize Slange
 *
 * Revision 1.173  2004/04/27 04:37:51  rjongbloed
 * Fixed ability to break of a PSocket::Select call under linux when a socket
 *   is closed by another thread.
 *
 * Revision 1.172  2004/04/22 12:27:24  rjongbloed
 * Fixed selection of QoS to use more flexible #if rather than #ifdef
 *
 * Revision 1.171  2004/04/18 04:33:38  rjongbloed
 * Changed all operators that return BOOL to return standard type bool. This is primarily
 *   for improved compatibility with std STL usage removing many warnings.
 *
 * Revision 1.170  2004/04/18 00:50:14  ykiryanov
 * Removed BE_BONELESS ifdefs. Be is boned now. More funcitonality
 *
 * Revision 1.169  2004/04/03 08:22:22  csoutheren
 * Remove pseudo-RTTI and replaced with real RTTI
 *
 * Revision 1.168  2004/02/04 02:32:31  csoutheren
 * Changed #ifdef to #if to ensure flags are tested correctly
 *
 * Revision 1.167  2004/01/23 07:07:35  csoutheren
 * Fixed compile warning under Linux
 *
 * Revision 1.166  2003/11/12 05:16:48  csoutheren
 * Fixed compiling problem on systems without QoS or IPV6
 *
 * Revision 1.165  2003/10/30 11:32:57  rjongbloed
 * Added bullet proofing for converting from inaddr
 *
 * Revision 1.164  2003/10/27 09:48:47  csoutheren
 * Changed use of P_HAS_QOS to ensure that setsockopt is still used
 * for diffserv if available. Thanks to Henry Harrison
 *
 * Revision 1.163  2003/10/27 03:46:15  csoutheren
 * Added ifdef to disable QoS code on systems that do not support it
 *
 * Revision 1.162  2003/10/27 03:22:44  csoutheren
 * Added handling for QoS
 *   Thanks to Henry Harrison of AliceStreet
 *
 * Revision 1.161  2003/05/27 08:53:11  dsandras
 *
 * Added test error case when the host lookup fails for IPv6.
 *
 * Revision 1.160  2003/05/21 09:34:44  rjongbloed
 * Name lookup support for IPv6, thanks again Sébastien Josset
 *
 * Revision 1.159  2003/04/28 02:55:50  robertj
 * Added function to see at run time if IPv6 available, thanks Sebastien Josset
 *
 * Revision 1.158  2003/04/15 07:40:08  robertj
 * Removed redundent variable.
 * Fixed IPv6 support for multiple IP address DNS lookups.
 *
 * Revision 1.157  2003/04/08 01:12:35  robertj
 * Latest patch for IPv6 operation, thanks Sebastien Josset
 *
 * Revision 1.156  2003/04/07 23:31:33  robertj
 * Fixed incorrect host to network byte order function, should be long!
 *
 * Revision 1.155  2003/04/07 23:22:08  robertj
 * Fixed GNU compatibility issue.
 *
 * Revision 1.154  2003/04/07 11:57:56  robertj
 * Allowed for full integer numeric form of IP address read from a stream.
 *
 * Revision 1.153  2003/04/03 08:43:23  robertj
 * Added IPv4 mapping into IPv6, thanks Sebastien Josset
 *
 * Revision 1.152  2003/03/26 05:36:38  robertj
 * More IPv6 support (INADDR_ANY handling), thanks Sébastien Josset
 *
 * Revision 1.151  2003/02/11 06:49:12  craigs
 * Added missing OpenSocket function
 *
 * Revision 1.150  2003/02/03 11:23:32  robertj
 * Added function to get pointer to IP address data.
 *
 * Revision 1.149  2003/02/03 10:27:55  robertj
 * Cleaned up the gethostbyX functions for various platforms
 *
 * Revision 1.148  2003/02/03 08:43:01  robertj
 * Fixed correct scoping of local variables in gethostbyX functions.
 *
 * Revision 1.147  2003/01/14 04:36:08  robertj
 * Fixed v6 conversion of numeric string to binary so does not internally
 *   doa DNS lookup, confuses other parts of the system.
 *
 * Revision 1.146  2003/01/11 05:10:51  robertj
 * Fixed Win CE compatibility issues, thanks Joerg Schoemer
 *
 * Revision 1.145  2002/12/16 08:04:46  robertj
 * Fixed correct error check for gethostbyname_r, thanks Vladimir Toncar
 *
 * Revision 1.144  2002/12/13 04:01:46  robertj
 * Initialised error code in gethostbyname usage.
 *
 * Revision 1.143  2002/12/04 00:52:59  robertj
 * Fixed GNU warning
 *
 * Revision 1.142  2002/12/02 12:25:08  craigs
 * Fixed problem with error code from gethostbyname_r not being checked correctly
 *
 * Revision 1.141  2002/11/24 23:47:01  robertj
 * Fixed MSVC v5 compatibility
 *
 * Revision 1.140  2002/11/13 02:15:25  craigs
 * Fixed problem with GetLocalHostName
 *
 * Revision 1.139  2002/11/12 11:47:53  rogerh
 * Add a missing memset in the Psockaddr constructor
 *
 * Revision 1.138  2002/11/02 00:32:21  robertj
 * Further fixes to VxWorks (Tornado) port, thanks Andreas Sikkema.
 *
 * Revision 1.137  2002/11/01 23:56:11  robertj
 * Fixed GNu compatibility isse with IPv6
 *
 * Revision 1.136  2002/11/01 03:32:08  robertj
 * More IPv6 fixes, thanks Sébastien Josset.
 *
 * Revision 1.135  2002/10/31 07:55:33  robertj
 * Put sizeof ipv6 structure back as magic number 28 is explained by
 *   mismatched header file and running implementation.
 *
 * Revision 1.134  2002/10/29 08:04:44  robertj
 * Changed in_addr6 to more universally used in6_addr.
 * Changed size of ipv6 address to be 28 under win32, Why? I don't know!
 *
 * Revision 1.133  2002/10/19 06:12:20  robertj
 * Moved P_fd_set::Zero() from platform independent to platform dependent
 *   code as Win32 implementation is completely different from Unix.
 *
 * Revision 1.132  2002/10/18 08:07:41  robertj
 * Fixed use of FD_ZERO as (strangely) crashes on some paltforms and would
 *   not have cleared enough of an enlarges fd_set anyway.
 *
 * Revision 1.131  2002/10/17 13:44:27  robertj
 * Port to RTEMS, thanks Vladimir Nesic.
 *
 * Revision 1.130  2002/10/17 08:17:28  robertj
 * Fixed incomplete changes for expandable fd_set
 *
 * Revision 1.129  2002/10/17 07:17:43  robertj
 * Added ability to increase maximum file handles on a process.
 *
 * Revision 1.128  2002/10/17 01:24:11  robertj
 * Fixed so internal sockaddr classes GetSize() returns correct size for
 *   particular sockaddr it represents, thanks Sébastien Josset.
 *
 * Revision 1.127  2002/10/16 06:19:36  robertj
 * Rewrite of IPv6 sockaddr code to use intelligent class to automatically
 *   know if it is sockaddr_in or aockaddr_in6.
 * Fixed Connect() function to work correctly on unopened socket.
 *
 * Revision 1.126  2002/10/10 11:38:56  robertj
 * Added close of socket if not open in correct ip version, thanks Sébastien Josset
 *
 * Revision 1.125  2002/10/10 04:43:44  robertj
 * VxWorks port, thanks Martijn Roest
 *
 * Revision 1.124  2002/10/09 05:37:52  robertj
 * Fixed IPv6 version of ReadFrom() and WriteTo().
 *
 * Revision 1.123  2002/10/08 23:31:44  robertj
 * Added missing GetSize() implementation in ip address.
 *
 * Revision 1.122  2002/10/08 12:41:52  robertj
 * Changed for IPv6 support, thanks Sébastien Josset.
 *
 * Revision 1.121  2002/09/23 07:17:24  robertj
 * Changes to allow winsock2 to be included.
 *
 * Revision 1.120  2002/05/22 07:18:46  robertj
 * Fixed bug where SO_RESUSEADDR wsa being turned ON instead of OFF when
 *   making an outgoing connection, should only be ON for listener sockets.
 *
 * Revision 1.119  2002/04/12 01:42:41  robertj
 * Changed return value on os_connect() and os_accept() to make sure
 *   get the correct error codes propagated up under unix.
 *
 * Revision 1.118  2002/01/28 01:27:03  robertj
 * Removed previous change that actually has nothing to do with GCC 3 compatibility,
 *   setting default timeout for all sockets to 10 seconds is NOT a sensible thing to do!
 *
 * Revision 1.117  2002/01/26 23:57:45  craigs
 * Changed for GCC 3.0 compatibility, thanks to manty@manty.net
 *
 * Revision 1.116  2002/01/07 05:37:32  robertj
 * Changed to allow for a service name that starts with a number.
 *
 * Revision 1.115  2002/01/02 04:55:31  craigs
 * Fixed problem when PSocket::GetPortByService called with a number
 * that is a substring of a valid service name
 *
 * Revision 1.114  2001/12/13 09:18:07  robertj
 * Added function to convert PString to IP address with error checking that can
 *   distinguish between 0.0.0.0 or 255.255.255.255 and illegal address.
 * Added ability to decode bracketed IP addresss [10.1.2.3] as host name.
 *
 * Revision 1.113  2001/09/14 08:00:38  robertj
 * Added new versions of Conenct() to allow binding to a specific local interface.
 *
 * Revision 1.112  2001/09/10 02:51:23  robertj
 * Major change to fix problem with error codes being corrupted in a
 *   PChannel when have simultaneous reads and writes in threads.
 *
 * Revision 1.111  2001/06/30 06:59:07  yurik
 * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
 *
 * Revision 1.110  2001/05/24 02:07:31  yurik
 * ::setsockopt on WinCE is now not called if option is not supported
 *
 * Revision 1.109  2001/05/23 19:48:55  yurik
 * Fix submitted by Dave Cassel, dcassel@cyberfone.com,
 * allowing a connection between a client and a gatekeeper.
 *
 * Revision 1.108  2001/03/20 06:44:25  robertj
 * Lots of changes to fix the problems with terminating threads that are I/O
 *   blocked, especially when doing orderly shutdown of service via SIGTERM.
 *
 * Revision 1.107  2001/03/05 04:18:27  robertj
 * Added net mask to interface info returned by GetInterfaceTable()
 *
 * Revision 1.106  2001/01/29 06:41:32  robertj
 * Added printing of entry of interface table.
 *
 * Revision 1.105  2001/01/28 01:15:01  yurik
 * WinCE port-related
 *
 * Revision 1.104  2001/01/24 06:32:17  yurik
 * Windows CE port-related changes
 *
 * Revision 1.103  2000/06/26 11:17:21  robertj
 * Nucleus++ port (incomplete).
 *
 * Revision 1.102  2000/06/21 01:01:22  robertj
 * AIX port, thanks Wolfgang Platzer (wolfgang.platzer@infonova.at).
 *
 * Revision 1.101  2000/05/02 08:14:40  craigs
 * Fixed problem with "memory leak" reporting under Unix
 *
 * Revision 1.100  2000/04/27 02:43:45  robertj
 * Fixed warning about signedness mismatch.
 *
 * Revision 1.99  2000/04/19 00:13:52  robertj
 * BeOS port changes.
 *
 * Revision 1.98  2000/02/18 09:55:21  robertj
 * Added parameter so get/setsockopt can have other levels to SOL_SOCKET.
 *
 * Revision 1.97  1999/10/27 01:21:44  robertj
 * Improved portability of copy from host_info struct to IP address.
 *
 * Revision 1.96  1999/08/30 02:21:03  robertj
 * Added ability to listen to specific interfaces for IP sockets.
 *
 * Revision 1.95  1999/08/27 08:18:52  robertj
 * Added ability to get the host/port of the the last packet read/written to UDP socket.
 *
 * Revision 1.94  1999/08/08 09:04:01  robertj
 * Added operator>> for PIPSocket::Address class.
 *
 * Revision 1.93  1999/07/11 13:42:13  craigs
 * pthreads support for Linux
 *
 * Revision 1.92  1999/06/01 08:04:35  robertj
 * Fixed mistake from previous fix.
 *
 * Revision 1.91  1999/06/01 07:39:23  robertj
 * Added retries to DNS lookup if get temporary error.
 *
 * Revision 1.90  1999/03/09 08:13:52  robertj
 * Fixed race condition in doing Select() on closed sockets. Could go into infinite wait.
 *
 * Revision 1.89  1999/03/02 05:41:58  robertj
 * More BeOS changes
 *
 * Revision 1.88  1999/02/26 04:10:39  robertj
 * More BeOS port changes
 *
 * Revision 1.87  1999/02/25 03:43:35  robertj
 * Fixed warning when PINDEX is unsigned.
 *
 * Revision 1.86  1999/02/23 07:19:22  robertj
 * Added [] operator PIPSocket::Address to get the bytes out of an IP address.
 *
 * Revision 1.85  1999/02/16 08:08:06  robertj
 * MSVC 6.0 compatibility changes.
 *
 * Revision 1.84  1999/01/08 01:29:47  robertj
 * Support for pthreads under FreeBSD
 *
 * Revision 1.83  1999/01/06 10:58:01  robertj
 * Fixed subtle mutex bug in returning string hostname from DNS cache.
 *
 * Revision 1.82  1998/12/22 10:25:01  robertj
 * Added clone() function to support SOCKS in FTP style protocols.
 * Fixed internal use of new operator in IP cache.
 *
 * Revision 1.81  1998/12/18 04:34:37  robertj
 * PPC Linux GNU C compatibility.
 *
 * Revision 1.80  1998/11/30 04:47:52  robertj
 * New directory structure
 *
 * Revision 1.79  1998/11/14 06:28:36  robertj
 * Changed senatics of os_sendto to return TRUE if ANY bytes are sent.
 *
 * Revision 1.78  1998/11/08 12:05:04  robertj
 * Fixed multiple thread access problem with DNS aliases array.
 *
 * Revision 1.77  1998/10/01 09:05:35  robertj
 * Added check that port number is between 1 and 65535.
 *
 * Revision 1.76  1998/09/23 06:22:44  robertj
 * Added open source copyright license.
 *
 * Revision 1.75  1998/08/31 13:00:34  robertj
 * Prevented dependency on snmpapi.dll for all ptlib apps.
 *
 * Revision 1.74  1998/08/27 00:58:42  robertj
 * Resolved signedness problems with various GNU libraries.
 *
 * Revision 1.73  1998/08/25 14:07:43  robertj
 * Added getprotobyxxx wrapper functions.
 *
 * Revision 1.72  1998/08/25 11:09:20  robertj
 * Fixed parsing of 802.x header on ethernet frames.
 * Changed DNS cache to not cache temporary lookup failures, only an authoratative 'no such host'.
 *
 * Revision 1.71  1998/08/21 05:26:10  robertj
 * Fixed bug where write streams out to non-stream socket.
 * Added ethernet socket.
 *
 * Revision 1.70  1998/05/07 05:20:25  robertj
 * Fixed DNS lookup so only works around bug in old Win95 and not OSR2
 *
 * Revision 1.69  1998/03/20 03:18:21  robertj
 * Added special classes for specific sepahores, PMutex and PSyncPoint.
 *
 * Revision 1.68  1998/03/05 12:45:48  robertj
 * DNS cache and NT bug fix attempts.
 *
 * Revision 1.67  1998/01/26 02:49:22  robertj
 * GNU support.
 *
 * Revision 1.66  1998/01/26 00:49:28  robertj
 * Fixed bug in detecting local host on NT, 95 bug kludge was interfering with it.
 *
 * Revision 1.65  1998/01/06 12:43:23  craigs
 * Added definition of REENTRANT_BUFFER_LEN
 *
 * Revision 1.64  1998/01/04 07:25:09  robertj
 * Added pthreads compatible calls for gethostbyx functions.
 *
 * Revision 1.63  1997/12/18 05:06:13  robertj
 * Moved IsLocalHost() to platform dependent code.
 *
 * Revision 1.62  1997/12/11 10:30:35  robertj
 * Added operators for IP address to DWORD conversions.
 *
 * Revision 1.61  1997/10/03 13:33:22  robertj
 * Added workaround for NT winsock bug with RAS and DNS lookups.
 *
 * Revision 1.60  1997/09/27 00:58:39  robertj
 * Fixed race condition on socket close in Select() function.
 *
 * Revision 1.59  1997/06/06 10:56:36  craigs
 * Added new functions for connectionless UDP writes
 *
 * Revision 1.58  1997/01/04 07:42:18  robertj
 * Fixed GCC Warnings.
 *
 * Revision 1.57  1997/01/04 06:54:38  robertj
 * Added missing canonical name to alias list.
 *
 * Revision 1.56  1996/12/17 11:07:05  robertj
 * Added clear of name cache.
 *
 * Revision 1.55  1996/12/12 09:23:27  robertj
 * Fixed name cache to cache missing names as well.
 * Fixed new connect with specific local port so can be re-used (simultaneous FTP session bug)
 *
 * Revision 1.54  1996/12/05 11:46:39  craigs
 * Fixed problem with Win95 recvfrom not having timeouts
 *
 * Revision 1.53  1996/11/30 12:08:17  robertj
 * Added Connect() variant so can set the local port number on link.
 *
 * Revision 1.52  1996/11/16 10:49:03  robertj
 * Fixed missing const in PIPSocket::Address stream output operator..
 *
 * Revision 1.51  1996/11/16 01:43:49  craigs
 * Fixed problem with ambiguous DNS cache keys
 *
 * Revision 1.50  1996/11/10 21:08:31  robertj
 * Added host name caching.
 *
 * Revision 1.49  1996/11/04 03:40:22  robertj
 * Moved address printer from inline to source.
 *
 * Revision 1.48  1996/10/26 01:41:09  robertj
 * Compensated for Win'95 gethostbyaddr bug.
 *
 * Revision 1.47  1996/09/14 13:09:40  robertj
 * Major upgrade:
 *   rearranged sockets to help support IPX.
 *   added indirect channel class and moved all protocols to descend from it,
 *   separating the protocol from the low level byte transport.
 *
 * Revision 1.46  1996/08/25 09:33:32  robertj
 * Added function to detect "local" host name.
 *
 * Revision 1.45  1996/07/30 12:24:53  robertj
 * Fixed incorrect conditional stopping Select() from working.
 *
 * Revision 1.44  1996/07/27 04:10:35  robertj
 * Changed Select() calls to return error codes.
 *
 * Revision 1.43  1996/06/10 09:58:21  robertj
 * Fixed win95 compatibility with looking up zero address (got a response and shouldn't).
 *
 * Revision 1.42  1996/05/26 03:47:03  robertj
 * Compatibility to GNU 2.7.x
 *
 * Revision 1.39  1996/04/29 12:20:01  robertj
 * Fixed GetHostAliases() so doesn't overwrite names with IP numbers.
 *
 * Revision 1.38  1996/04/15 10:59:41  robertj
 * Opened socket on UDP sockets so ReadFrom/WriteTo work when no Connect/Listen.
 *
 * Revision 1.37  1996/03/31 09:06:41  robertj
 * Added socket shutdown function.
 *
 * Revision 1.35  1996/03/18 13:33:18  robertj
 * Fixed incompatibilities to GNU compiler where PINDEX != int.
 *
 * Revision 1.34  1996/03/17 05:51:18  robertj
 * Fixed strange bug in accept cant have NULL address.
 *
 * Revision 1.33  1996/03/16 04:52:20  robertj
 * Changed all the get host name and get host address functions to be more consistent.
 *
 * Revision 1.32  1996/03/04 12:21:00  robertj
 * Split file into telnet.cxx
 *
 * Revision 1.31  1996/03/03 07:38:45  robertj
 * Added Reusability clause to the Listen() function on sockets.
 *
 * Revision 1.30  1996/03/02 03:25:13  robertj
 * Added Capability to get and set Berkeley socket options.
 *
 * Revision 1.29  1996/02/25 11:30:08  robertj
 * Changed Listen so can do a listen on a socket that is connected.
 *
 * Revision 1.28  1996/02/25 03:10:55  robertj
 * Moved some socket functions to platform dependent code.
 *
 * Revision 1.27  1996/02/19 13:30:15  robertj
 * Fixed bug in getting port by service name when specifying service by string number.
 * Added SO_LINGER option to socket to stop data loss on close.
 *
 * Revision 1.26  1996/02/15 14:46:44  robertj
 * Added Select() function to PSocket.
 *
 * Revision 1.25  1996/02/13 13:08:09  robertj
 * Fixed usage of sock_addr structure, not being cleared correctly.
 *
 * Revision 1.24  1996/02/08 12:27:22  robertj
 * Added function to get peer port as well as IP number..
 *
 * Revision 1.23  1996/02/03 11:07:37  robertj
 * Fixed buf in assuring error when converting string to IP number and string is empty.
 *
 * Revision 1.22  1996/01/28 14:08:13  robertj
 * Changed service parameter to PString for ease of use in GetPortByService function
 * Fixed up comments.
 * Added default value in string for service name.
 *
 * Revision 1.21  1996/01/23 13:19:13  robertj
 * Moved Accept() function to platform dependent code.
 *
 * Revision 1.20  1995/12/23 03:42:53  robertj
 * Unix portability issues.
 *
 * Revision 1.19  1995/12/10 11:42:23  robertj
 * Numerous fixes for sockets.
 *
 * Revision 1.18  1995/10/14 15:11:31  robertj
 * Added internet address to string conversion functionality.
 *
 * Revision 1.17  1995/07/02 01:21:23  robertj
 * Added static functions to get the current host name/address.
 *
 * Revision 1.16  1995/06/17 00:47:01  robertj
 * Changed overloaded Open() calls to 3 separate function names.
 * More logical design of port numbers and service names.
 *
 * Revision 1.15  1995/06/04 12:45:33  robertj
 * Added application layer protocol sockets.
 * Slight redesign of port numbers on sockets.
 *
 * Revision 1.14  1995/04/25 11:12:44  robertj
 * Fixed functions hiding ancestor virtuals.
 *
 * Revision 1.13  1995/04/01 08:31:54  robertj
 * Finally got a working TELNET.
 *
 * Revision 1.12  1995/03/18 06:27:49  robertj
 * Rewrite of telnet socket protocol according to RFC1143.
 *
 * Revision 1.11  1995/03/12  04:46:29  robertj
 * Added more functionality.
 *
 * Revision 1.10  1995/02/21  11:25:29  robertj
 * Further implementation of telnet socket, feature complete now.
 *
 * Revision 1.9  1995/01/27  11:16:16  robertj
 * Fixed missing cast in function, required by some platforms.
 *
 * Revision 1.8  1995/01/15  04:55:47  robertj
 * Moved all Berkley socket functions inside #ifdef.
 *
 * Revision 1.7  1995/01/04  10:57:08  robertj
 * Changed for HPUX and GNU2.6.x
 *
 * Revision 1.6  1995/01/03  09:37:52  robertj
 * Added constructor to open TCP socket.
 *
 * Revision 1.5  1995/01/02  12:28:25  robertj
 * Documentation.
 * Added more socket functions.
 *
 * Revision 1.4  1995/01/01  01:06:58  robertj
 * More implementation.
 *
 * Revision 1.3  1994/11/28  12:38:49  robertj
 * Added DONT and WONT states.
 *
 * Revision 1.2  1994/08/21  23:43:02  robertj
 * Some implementation.
 *
 * Revision 1.1  1994/08/01  03:39:05  robertj
 * Initial revision
 *
 */

#ifdef __NUCLEUS_PLUS__
#include <ConfigurationClass.h>
#endif


#include <ptlib.h>

#include <ptlib/sockets.h>

#include <ctype.h>

#ifdef P_VXWORKS
// VxWorks variant of inet_ntoa() allocates INET_ADDR_LEN bytes via malloc
// BUT DOES NOT FREE IT !!!  Use inet_ntoa_b() instead.
#define INET_ADDR_LEN      18
extern "C" void inet_ntoa_b(struct in_addr inetAddress, char *pString);
#endif // P_VXWORKS

#if P_HAS_QOS

#ifdef _WIN32
#include <winbase.h>
#include <winreg.h>

#ifndef _WIN32_WCE

void CALLBACK CompletionRoutine(DWORD dwError,
                                DWORD cbTransferred,
                                LPWSAOVERLAPPED lpOverlapped,
                                DWORD dwFlags);
                                

#endif  // _WIN32_WCE
#endif  // _WIN32
#endif // P_HAS_QOS

///////////////////////////////////////////////////////////////////////////////
// PIPSocket::Address

static int defaultIpAddressFamily = PF_INET;  // PF_UNSPEC;   // default to IPV4

static PIPSocket::Address loopback4(127,0,0,1);
static PIPSocket::Address broadcast4(INADDR_BROADCAST);
static PIPSocket::Address any4(INADDR_ANY);
static in_addr inaddr_empty;
#if P_HAS_IPV6
static PIPSocket::Address loopback6(16,(const BYTE *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\001");
static PIPSocket::Address any6(16,(const BYTE *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); 
#endif


int PIPSocket::GetDefaultIpAddressFamily()
{
  return defaultIpAddressFamily;
}


void PIPSocket::SetDefaultIpAddressFamily(int ipAdressFamily)
{
  defaultIpAddressFamily = ipAdressFamily;
}


void PIPSocket::SetDefaultIpAddressFamilyV4()
{
  SetDefaultIpAddressFamily(PF_INET);
}


#if P_HAS_IPV6

void PIPSocket::SetDefaultIpAddressFamilyV6()
{
  SetDefaultIpAddressFamily(PF_INET6);
}


BOOL PIPSocket::IsIpAddressFamilyV6Supported()
{
  int s = ::socket(PF_INET6, SOCK_DGRAM, 0);
  if (s < 0)
    return FALSE;

  ::close(s);
  return TRUE;
}

#endif


PIPSocket::Address PIPSocket::GetDefaultIpAny()
{
#if P_HAS_IPV6
  if (defaultIpAddressFamily != PF_INET)
    return any6;
#endif

  return any4;
}


#if P_HAS_IPV6

class Psockaddr
{
  public:
    Psockaddr() { memset(&storage, 0, sizeof(storage)); }
    Psockaddr(const PIPSocket::Address & ip, WORD port);
    sockaddr* operator->() const { return (sockaddr *)&storage; }
    operator sockaddr*()   const { return (sockaddr *)&storage; }
    socklen_t GetSize() const;
    PIPSocket::Address GetIP() const;
    WORD GetPort() const;
  private:
    sockaddr_storage storage;
};


Psockaddr::Psockaddr(const PIPSocket::Address & ip, WORD port)
{
  memset(&storage, 0, sizeof(storage));

  if (ip.GetVersion() == 6) {
    sockaddr_in6 * addr6 = (sockaddr_in6 *)&storage;
    addr6->sin6_family = AF_INET6;
    addr6->sin6_addr = ip;
    addr6->sin6_port = htons(port);
    addr6->sin6_flowinfo = 0;
    addr6->sin6_scope_id = 0; // Should be set to the right interface....
  }
  else {
    sockaddr_in * addr4 = (sockaddr_in *)&storage;
    addr4->sin_family = AF_INET;
    addr4->sin_addr = ip;
    addr4->sin_port = htons(port);
  }
}


socklen_t Psockaddr::GetSize() const
{
  switch (((sockaddr *)&storage)->sa_family) {
    case AF_INET :
      return sizeof(sockaddr_in);
    case AF_INET6 :
      // RFC 2133 (Old IPv6 spec) size is 24
      // RFC 2553 (New IPv6 spec) size is 28
      return sizeof(sockaddr_in6);
    default :
      return sizeof(storage);
  }
}


PIPSocket::Address Psockaddr::GetIP() const
{
  switch (((sockaddr *)&storage)->sa_family) {
    case AF_INET :
      return ((sockaddr_in *)&storage)->sin_addr;
    case AF_INET6 :
      return ((sockaddr_in6 *)&storage)->sin6_addr;
    default :
      return 0;
  }
}


WORD Psockaddr::GetPort() const
{
  switch (((sockaddr *)&storage)->sa_family) {
    case AF_INET :
      return ntohs(((sockaddr_in *)&storage)->sin_port);
    case AF_INET6 :
      return ntohs(((sockaddr_in6 *)&storage)->sin6_port);
    default :
      return 0;
  }
}

#endif


#if (defined(_WIN32) || defined(WINDOWS)) && !defined(__NUCLEUS_MNT__)
static PWinSock dummyForWinSock; // Assure winsock is initialised
#endif

#if (defined(P_PTHREADS) && !defined(P_THREAD_SAFE_CLIB)) || defined(__NUCLEUS_PLUS__)
#define REENTRANT_BUFFER_LEN 1024
#endif


class PIPCacheData : public PObject
{
  PCLASSINFO(PIPCacheData, PObject)
  public:
    PIPCacheData(struct hostent * ent, const char * original);
#if P_HAS_IPV6
    PIPCacheData(struct addrinfo  * addr_info, const char * original);
    void AddEntry(struct addrinfo  * addr_info);
#endif
    const PString & GetHostName() const { return hostname; }
    const PIPSocket::Address & GetHostAddress() const { return address; }
    const PStringList & GetHostAliases() const { return aliases; }
    BOOL HasAged() const;
  private:
    PString            hostname;
    PIPSocket::Address address;
    PStringList        aliases;
    PTime              birthDate;
};



PDICTIONARY(PHostByName_private, PCaselessString, PIPCacheData);

class PHostByName : PHostByName_private
{
  public:
    BOOL GetHostName(const PString & name, PString & hostname);
    BOOL GetHostAddress(const PString & name, PIPSocket::Address & address);
    BOOL GetHostAliases(const PString & name, PStringArray & aliases);
  private:
    PIPCacheData * GetHost(const PString & name);
    PMutex mutex;
  friend void PIPSocket::ClearNameCache();
};

PMutex creationMutex;
static PHostByName & pHostByName()
{
  PWaitAndSignal m(creationMutex);
  static PHostByName t;
  return t;
}

class PIPCacheKey : public PObject
{
  PCLASSINFO(PIPCacheKey, PObject)
  public:
    PIPCacheKey(const PIPSocket::Address & a)
      { addr = a; }

    PObject * Clone() const
      { return new PIPCacheKey(*this); }

    PINDEX HashFunction() const
      { return (addr[1] + addr[2] + addr[3])%41; }

  private:
    PIPSocket::Address addr;
};

PDICTIONARY(PHostByAddr_private, PIPCacheKey, PIPCacheData);

class PHostByAddr : PHostByAddr_private
{
  public:
    BOOL GetHostName(const PIPSocket::Address & addr, PString & hostname);
    BOOL GetHostAddress(const PIPSocket::Address & addr, PIPSocket::Address & address);
    BOOL GetHostAliases(const PIPSocket::Address & addr, PStringArray & aliases);
  private:
    PIPCacheData * GetHost(const PIPSocket::Address & addr);
    PMutex mutex;
  friend void PIPSocket::ClearNameCache();
};

static PHostByAddr & pHostByAddr()
{
  PWaitAndSignal m(creationMutex);
  static PHostByAddr t;
  return t;
}

#define new PNEW


//////////////////////////////////////////////////////////////////////////////
// IP Caching

PIPCacheData::PIPCacheData(struct hostent * host_info, const char * original)
{
  if (host_info == NULL) {
    address = 0;
    return;
  }

  hostname = host_info->h_name;
  if (host_info->h_addr != NULL)
#ifndef _WIN32_WCE
    address = *(DWORD *)host_info->h_addr;
#else
    address = PIPSocket::Address(host_info->h_length, (const BYTE *)host_info->h_addr);
#endif
  aliases.AppendString(host_info->h_name);

  PINDEX i;
  for (i = 0; host_info->h_aliases[i] != NULL; i++)
    aliases.AppendString(host_info->h_aliases[i]);

  for (i = 0; host_info->h_addr_list[i] != NULL; i++) {
#ifndef _WIN32_WCE
    PIPSocket::Address ip(*(DWORD *)host_info->h_addr_list[i]);
#else
    PIPSocket::Address ip(host_info->h_length, (const BYTE *)host_info->h_addr_list[i]);
#endif
    aliases.AppendString(ip.AsString());
  }

  for (i = 0; i < aliases.GetSize(); i++)
    if (aliases[i] *= original)
      return;

  aliases.AppendString(original);
}


#if P_HAS_IPV6

PIPCacheData::PIPCacheData(struct addrinfo * addr_info, const char * original)
{
  PINDEX i;
  if (addr_info == NULL) {
    address = 0;
    return;
  }

  // Fill Host primary informations
  hostname = addr_info->ai_canonname; // Fully Qualified Domain Name (FQDN)
  if (addr_info->ai_addr != NULL)
    address = PIPSocket::Address(addr_info->ai_family, addr_info->ai_addrlen, addr_info->ai_addr);

  // Next entries
  while (addr_info != NULL) {
    AddEntry(addr_info);
    addr_info = addr_info->ai_next;
  }

  // Add original as alias or allready added ?
  for (i = 0; i < aliases.GetSize(); i++) {
    if (aliases[i] *= original)
      return;
  }

  aliases.AppendString(original);
}


void PIPCacheData::AddEntry(struct addrinfo * addr_info)
{
  PINDEX i;

  if (addr_info == NULL)
    return;

  // Add canonical name
  BOOL add_it = TRUE;
  for (i = 0; i < aliases.GetSize(); i++) {
    if (addr_info->ai_canonname != NULL && (aliases[i] *= addr_info->ai_canonname)) {
      add_it = FALSE;
      break;
    }
  }

  if (add_it && addr_info->ai_canonname != NULL)
    aliases.AppendString(addr_info->ai_canonname);

  // Add IP address
  PIPSocket::Address ip(addr_info->ai_family, addr_info->ai_addrlen, addr_info->ai_addr);
  add_it = TRUE;
  for (i = 0; i < aliases.GetSize(); i++) {
    if (aliases[i] *= ip.AsString()) {
      add_it = FALSE;
      break;
    }
  }

  if (add_it)
    aliases.AppendString(ip.AsString());
}

#endif


static PTimeInterval GetConfigTime(const char * /*key*/, DWORD dflt)
{
  //PConfig cfg("DNS Cache");
  //return cfg.GetInteger(key, dflt);
  return dflt;
}


BOOL PIPCacheData::HasAged() const
{
  static PTimeInterval retirement = GetConfigTime("Age Limit", 300000); // 5 minutes
  PTime now;
  PTimeInterval age = now - birthDate;
  return age > retirement;
}


BOOL PHostByName::GetHostName(const PString & name, PString & hostname)
{
  PIPCacheData * host = GetHost(name);

  if (host != NULL) {
    hostname = host->GetHostName();
    hostname.MakeUnique();
  }

  mutex.Signal();

  return host != NULL;
}


BOOL PHostByName::GetHostAddress(const PString & name, PIPSocket::Address & address)
{
  PIPCacheData * host = GetHost(name);

  if (host != NULL)
    address = host->GetHostAddress();

  mutex.Signal();

  return host != NULL;
}


BOOL PHostByName::GetHostAliases(const PString & name, PStringArray & aliases)
{
  PIPCacheData * host = GetHost(name);

  if (host != NULL) {
    const PStringList & a = host->GetHostAliases();
    aliases.SetSize(a.GetSize());
    for (PINDEX i = 0; i < a.GetSize(); i++)
      aliases[i] = a[i];
  }

  mutex.Signal();
  return host != NULL;
}


PIPCacheData * PHostByName::GetHost(const PString & name)
{
  mutex.Wait();

  PCaselessString key = name;
  PIPCacheData * host = GetAt(key);
  int localErrNo = NETDB_SUCCESS;

  if (host != NULL && host->HasAged()) {
    SetAt(key, NULL);
    host = NULL;
  }

  if (host == NULL) {
    mutex.Signal();

#if P_HAS_IPV6
    struct addrinfo *res;
    struct addrinfo hints = { AI_CANONNAME, PF_UNSPEC };
    hints.ai_family = defaultIpAddressFamily;

    localErrNo = getaddrinfo((const char *)name, NULL , &hints, &res);
    mutex.Wait();

    if (localErrNo != NETDB_SUCCESS)
      return NULL;
    host = new PIPCacheData(res, name);
    freeaddrinfo(res);
#else // P_HAS_IPV6

    int retry = 3;
    struct hostent * host_info;

#ifdef P_AIX

    struct hostent_data ht_data;
    memset(&ht_data, 0, sizeof(ht_data));
    struct hostent hostEnt;
    do {
      host_info = &hostEnt;
      ::gethostbyname_r(name,
                        host_info,
                        &ht_data);
      localErrNo = h_errno;
    } while (localErrNo == TRY_AGAIN && --retry > 0);

#elif defined(P_RTEMS) || defined(P_CYGWIN) || defined(P_MINGW)

    host_info = ::gethostbyname(name);
    localErrNo = h_errno;

#elif defined P_VXWORKS

    struct hostent hostEnt;
    host_info = Vx_gethostbyname((char *)name, &hostEnt);
    localErrNo = h_errno;

#elif defined P_LINUX

    char buffer[REENTRANT_BUFFER_LEN];
    struct hostent hostEnt;
    do {
      if (::gethostbyname_r(name,
                            &hostEnt,
                            buffer, REENTRANT_BUFFER_LEN,
                            &host_info,
                            &localErrNo) == 0)
        localErrNo = NETDB_SUCCESS;
    } while (localErrNo == TRY_AGAIN && --retry > 0);

#elif (defined(P_PTHREADS) && !defined(P_THREAD_SAFE_CLIB)) || defined(__NUCLEUS_PLUS__)

    char buffer[REENTRANT_BUFFER_LEN];
    struct hostent hostEnt;
    do {
      host_info = ::gethostbyname_r(name,
                                    &hostEnt,
                                    buffer, REENTRANT_BUFFER_LEN,
                                    &localErrNo);
    } while (localErrNo == TRY_AGAIN && --retry > 0);

#else

    host_info = ::gethostbyname(name);
    localErrNo = h_errno;

#endif

    mutex.Wait();

    if (localErrNo != NETDB_SUCCESS || retry == 0)
      return NULL;
    host = new PIPCacheData(host_info, name);

#endif //P_HAS_IPV6

    SetAt(key, host);
  }

  if (host->GetHostAddress() == 0)
    return NULL;

  return host;
}


BOOL PHostByAddr::GetHostName(const PIPSocket::Address & addr, PString & hostname)
{
  PIPCacheData * host = GetHost(addr);

  if (host != NULL) {
    hostname = host->GetHostName();
    hostname.MakeUnique();
  }

  mutex.Signal();
  return host != NULL;
}


BOOL PHostByAddr::GetHostAddress(const PIPSocket::Address & addr, PIPSocket::Address & address)
{
  PIPCacheData * host = GetHost(addr);

  if (host != NULL)
    address = host->GetHostAddress();

  mutex.Signal();
  return host != NULL;
}


BOOL PHostByAddr::GetHostAliases(const PIPSocket::Address & addr, PStringArray & aliases)
{
  PIPCacheData * host = GetHost(addr);

  if (host != NULL) {
    const PStringList & a = host->GetHostAliases();
    aliases.SetSize(a.GetSize());
    for (PINDEX i = 0; i < a.GetSize(); i++)
      aliases[i] = a[i];
  }

  mutex.Signal();
  return host != NULL;
}

PIPCacheData * PHostByAddr::GetHost(const PIPSocket::Address & addr)
{
  mutex.Wait();

  PIPCacheKey key = addr;
  PIPCacheData * host = GetAt(key);

  if (host != NULL && host->HasAged()) {
    SetAt(key, NULL);
    host = NULL;
  }

  if (host == NULL) {
    mutex.Signal();

    int retry = 3;
    int localErrNo = NETDB_SUCCESS;
    struct hostent * host_info;

#ifdef P_AIX

    struct hostent_data ht_data;
    struct hostent hostEnt;
    do {
      host_info = &hostEnt;
      ::gethostbyaddr_r((char *)addr.GetPointer(), addr.GetSize(),
                        PF_INET, 
                        host_info,
                        &ht_data);
      localErrNo = h_errno;
    } while (localErrNo == TRY_AGAIN && --retry > 0);

#elif defined P_RTEMS || defined P_CYGWIN || defined P_MINGW

    host_info = ::gethostbyaddr(addr.GetPointer(), addr.GetSize(), PF_INET);
    localErrNo = h_errno;

#elif defined P_VXWORKS

    struct hostent hostEnt;
    host_info = Vx_gethostbyaddr(addr.GetPointer(), &hostEnt);

#elif defined P_LINUX

    char buffer[REENTRANT_BUFFER_LEN];
    struct hostent hostEnt;
    do {
      ::gethostbyaddr_r(addr.GetPointer(), addr.GetSize(),
                        PF_INET, 
                        &hostEnt,
                        buffer, REENTRANT_BUFFER_LEN,
                        &host_info,
                        &localErrNo);
    } while (localErrNo == TRY_AGAIN && --retry > 0);

#elif (defined(P_PTHREADS) && !defined(P_THREAD_SAFE_CLIB)) || defined(__NUCLEUS_PLUS__)

    char buffer[REENTRANT_BUFFER_LEN];
    struct hostent hostEnt;
    do {
      host_info = ::gethostbyaddr_r(addr.GetPointer(), addr.GetSize(),
                                    PF_INET, 
                                    &hostEnt,
                                    buffer, REENTRANT_BUFFER_LEN,
                                    &localErrNo);
    } while (localErrNo == TRY_AGAIN && --retry > 0);

#else

    host_info = ::gethostbyaddr(addr.GetPointer(), addr.GetSize(), PF_INET);
    localErrNo = h_errno;

#if defined(_WIN32) || defined(WINDOWS)  // Kludge to avoid strange 95 bug
    extern int P_IsOldWin95();
    if (P_IsOldWin95() && host_info != NULL && host_info->h_addr_list[0] != NULL)
      host_info->h_addr_list[1] = NULL;
#endif

#endif

    mutex.Wait();

    if (localErrNo != NETDB_SUCCESS || retry == 0)
      return NULL;

    host = new PIPCacheData(host_info, addr.AsString());

    SetAt(key, host);
  }

  if (host->GetHostAddress() == 0)
    return NULL;

  return host;
}


//////////////////////////////////////////////////////////////////////////////
// P_fd_set

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4127)
#endif

P_fd_set::P_fd_set()
{
  Construct();
  Zero();
}


P_fd_set::P_fd_set(SOCKET fd)
{
  Construct();
  Zero();
  FD_SET(fd, set);
}


P_fd_set & P_fd_set::operator=(SOCKET fd)
{
  PAssert(fd < max_fd, PInvalidParameter);
  Zero();
  FD_SET(fd, set);
  return *this;
}


P_fd_set & P_fd_set::operator+=(SOCKET fd)
{
  PAssert(fd < max_fd, PInvalidParameter);
  FD_SET(fd, set);
  return *this;
}


P_fd_set & P_fd_set::operator-=(SOCKET fd)
{
  PAssert(fd < max_fd, PInvalidParameter);
  FD_CLR(fd, set);
  return *this;
}

#ifdef _MSC_VER
#pragma warning(pop)
#endif


//////////////////////////////////////////////////////////////////////////////
// P_timeval

P_timeval::P_timeval()
{
  tval.tv_usec = 0;
  tval.tv_sec = 0;
  infinite = FALSE;
}


P_timeval & P_timeval::operator=(const PTimeInterval & time)
{
  infinite = time == PMaxTimeInterval;
  tval.tv_usec = (long)(time.GetMilliSeconds()%1000)*1000;
  tval.tv_sec = time.GetSeconds();
  return *this;
}


//////////////////////////////////////////////////////////////////////////////
// PSocket

PSocket::PSocket()
{
  port = 0;
#if P_HAS_RECVMSG
  catchReceiveToAddr = FALSE;
#endif
}


BOOL PSocket::Connect(const PString &)
{
  PAssertAlways("Illegal operation.");
  return FALSE;
}


BOOL PSocket::Listen(unsigned, WORD, Reusability)
{
  PAssertAlways("Illegal operation.");
  return FALSE;
}


BOOL PSocket::Accept(PSocket &)
{
  PAssertAlways("Illegal operation.");
  return FALSE;
}


BOOL PSocket::SetOption(int option, int value, int level)
{
#ifdef _WIN32_WCE
  if(option == SO_RCVBUF || option == SO_SNDBUF || option == IP_TOS)
    return TRUE;
#endif

  return ConvertOSError(::setsockopt(os_handle, level, option,
                                     (char *)&value, sizeof(value)));
}


BOOL PSocket::SetOption(int option, const void * valuePtr, PINDEX valueSize, int level)
{
  return ConvertOSError(::setsockopt(os_handle, level, option,
                                     (char *)valuePtr, valueSize));
}


BOOL PSocket::GetOption(int option, int & value, int level)
{
  socklen_t valSize = sizeof(value);
  return ConvertOSError(::getsockopt(os_handle, level, option,
                                     (char *)&value, &valSize));
}


BOOL PSocket::GetOption(int option, void * valuePtr, PINDEX valueSize, int level)
{
  return ConvertOSError(::getsockopt(os_handle, level, option,
                                     (char *)valuePtr, (socklen_t *)&valueSize));
}


BOOL PSocket::Shutdown(ShutdownValue value)
{
  return ConvertOSError(::shutdown(os_handle, value));
}


WORD PSocket::GetProtocolByName(const PString & name)
{
#if !defined(__NUCLEUS_PLUS__) && !defined(_WIN32_WCE) && !defined(P_VXWORKS)
  struct protoent * ent = getprotobyname(name);
  if (ent != NULL)
    return ent->p_proto;
#endif

  return 0;
}


PString PSocket::GetNameByProtocol(WORD proto)
{
#if !defined(__NUCLEUS_PLUS__) && !defined(_WIN32_WCE) && !defined(P_VXWORKS)
  struct protoent * ent = getprotobynumber(proto);
  if (ent != NULL)
    return ent->p_name;
#endif

  return psprintf("%u", proto);
}


WORD PSocket::GetPortByService(const PString & serviceName) const
{
  return GetPortByService(GetProtocolName(), serviceName);
}


WORD PSocket::GetPortByService(const char * protocol, const PString & service)
{
  // if the string is a valid integer, then use integer value
  // this avoids stupid problems like operating systems that match service
  // names to substrings (like "2000" to "taskmaster2000")
  if (strspn(service, "0123456789") == strlen(service))
    return (WORD)service.AsUnsigned();

#if defined( __NUCLEUS_PLUS__ )
  PAssertAlways("PSocket::GetPortByService: problem as no ::getservbyname in Nucleus NET");
  return 0;
#elif defined(_WIN32_WCE)
  PAssertAlways("PSocket::GetPortByService: problem for WindowsCE as no port given.");
  return 0;
#elif defined(P_VXWORKS)
  PAssertAlways("PSocket::GetPortByService: problem as no ::getservbyname in VxWorks");
  return 0;
#else
  PINDEX space = service.FindOneOf(" \t\r\n");
  struct servent * serv = ::getservbyname(service(0, space-1), protocol);
  if (serv != NULL)
    return ntohs(serv->s_port);

  long portNum;
  if (space != P_MAX_INDEX)
    portNum = atol(service(space+1, P_MAX_INDEX));
  else if (isdigit(service[0]))
    portNum = atoi(service);
  else
    portNum = -1;

  if (portNum < 0 || portNum > 65535)
    return 0;

  return (WORD)portNum;
#endif
}


PString PSocket::GetServiceByPort(WORD port) const
{
  return GetServiceByPort(GetProtocolName(), port);
}


PString PSocket::GetServiceByPort(const char * protocol, WORD port)
{
#if !defined(__NUCLEUS_PLUS__) && !defined(_WIN32_WCE) && !defined(P_VXWORKS)
  struct servent * serv = ::getservbyport(htons(port), protocol);
  if (serv != NULL)
    return PString(serv->s_name);
  else
#endif
    return PString(PString::Unsigned, port);
}


void PSocket::SetPort(WORD newPort)
{
  PAssert(!IsOpen(), "Cannot change port number of opened socket");
  port = newPort;
}


void PSocket::SetPort(const PString & service)
{
  PAssert(!IsOpen(), "Cannot change port number of opened socket");
  port = GetPortByService(service);
}


WORD PSocket::GetPort() const
{
  return port;
}


PString PSocket::GetService() const
{
  return GetServiceByPort(port);
}


int PSocket::Select(PSocket & sock1, PSocket & sock2)
{
  return Select(sock1, sock2, PMaxTimeInterval);
}


int PSocket::Select(PSocket & sock1,
                    PSocket & sock2,
                    const PTimeInterval & timeout)
{
  SelectList read, dummy1, dummy2;
  read += sock1;
  read += sock2;

  Errors lastError;
  int osError;
  if (!ConvertOSError(Select(read, dummy1, dummy2, timeout), lastError, osError))
    return lastError;

  switch (read.GetSize()) {
    case 0 :
      return 0;
    case 2 :
      return -3;
    default :
      return &read[0] == &sock1 ? -1 : -2;
  }
}


PChannel::Errors PSocket::Select(SelectList & read)
{
  SelectList dummy1, dummy2;
  return Select(read, dummy1, dummy2, PMaxTimeInterval);
}


PChannel::Errors PSocket::Select(SelectList & read, const PTimeInterval & timeout)
{
  SelectList dummy1, dummy2;
  return Select(read, dummy1, dummy2, timeout);
}


PChannel::Errors PSocket::Select(SelectList & read, SelectList & write)
{
  SelectList dummy1;
  return Select(read, write, dummy1, PMaxTimeInterval);
}


PChannel::Errors PSocket::Select(SelectList & read,
                                 SelectList & write,
                                 const PTimeInterval & timeout)
{
  SelectList dummy1;
  return Select(read, write, dummy1, timeout);
}


PChannel::Errors PSocket::Select(SelectList & read,
                                 SelectList & write,
                                 SelectList & except)
{
  return Select(read, write, except, PMaxTimeInterval);
}


//////////////////////////////////////////////////////////////////////////////
// PIPSocket

PIPSocket::PIPSocket()
{
}


void PIPSocket::ClearNameCache()
{
  pHostByName().mutex.Wait();
  pHostByAddr().mutex.Wait();
  pHostByName().RemoveAll();
  pHostByAddr().RemoveAll();
#if (defined(_WIN32) || defined(WINDOWS)) && !defined(__NUCLEUS_MNT__) // Kludge to avoid strange NT bug
  static PTimeInterval delay = GetConfigTime("NT Bug Delay", 0);
  if (delay != 0) {
    ::Sleep(delay.GetInterval());
    ::gethostbyname("www.microsoft.com");
  }
#endif
  pHostByName().mutex.Signal();
  pHostByAddr().mutex.Signal();
}


PString PIPSocket::GetName() const
{
#if P_HAS_IPV6

  Psockaddr sa;
  socklen_t size = sa.GetSize();
  if (getpeername(os_handle, sa, &size) == 0)
    return GetHostName(sa.GetIP()) + psprintf(":%u", sa.GetPort());

#else

  sockaddr_in address;
  socklen_t size = sizeof(address);
  if (getpeername(os_handle, (struct sockaddr *)&address, &size) == 0)
    return GetHostName(address.sin_addr) + psprintf(":%u", ntohs(address.sin_port));

#endif

  return PString::Empty();
}


PString PIPSocket::GetHostName()
{
  char name[100];
  if (gethostname(name, sizeof(name)-1) != 0)
    return "localhost";
  name[sizeof(name)-1] = '\0';
  return name;
}


PString PIPSocket::GetHostName(const PString & hostname)
{
  // lookup the host address using inet_addr, assuming it is a "." address
  Address temp = hostname;
  if (temp != 0)
    return GetHostName(temp);

  PString canonicalname;
  if (pHostByName().GetHostName(hostname, canonicalname))
    return canonicalname;

  return hostname;
}


PString PIPSocket::GetHostName(const Address & addr)
{
  if (addr == 0)
    return addr.AsString();

  PString hostname;
  if (pHostByAddr().GetHostName(addr, hostname))
    return hostname;

  return addr.AsString();
}


BOOL PIPSocket::GetHostAddress(Address & addr)
{
  return pHostByName().GetHostAddress(GetHostName(), addr);
}


BOOL PIPSocket::GetHostAddress(const PString & hostname, Address & addr)
{
  if (hostname.IsEmpty())
    return FALSE;

  // Check for special case of "[ipaddr]"
  if (hostname[0] == '[') {
    PINDEX end = hostname.Find(']');
    if (end != P_MAX_INDEX) {
      if (addr.FromString(hostname(1, end-1)))
        return TRUE;
    }
  }

  // Assuming it is a "." address and return if so
  if (addr.FromString(hostname))
    return TRUE;

  // otherwise lookup the name as a host name
  return pHostByName().GetHostAddress(hostname, addr);
}


PStringArray PIPSocket::GetHostAliases(const PString & hostname)
{
  PStringArray aliases;

  // lookup the host address using inet_addr, assuming it is a "." address
  Address addr = hostname;
  if (addr != 0)
    pHostByAddr().GetHostAliases(addr, aliases);
  else
    pHostByName().GetHostAliases(hostname, aliases);

  return aliases;
}


PStringArray PIPSocket::GetHostAliases(const Address & addr)
{
  PStringArray aliases;

  pHostByAddr().GetHostAliases(addr, aliases);

  return aliases;
}


BOOL PIPSocket::GetLocalAddress(Address & addr)
{
  WORD dummy;
  return GetLocalAddress(addr, dummy);
}




BOOL PIPSocket::GetLocalAddress(Address & addr, WORD & portNum)
{
#if P_HAS_IPV6
  Address   addrv4;
  Address   peerv4;
  Psockaddr sa;
  socklen_t size = sa.GetSize();
  if (!ConvertOSError(::getsockname(os_handle, sa, &size)))
    return FALSE;

  addr = sa.GetIP();
  portNum = sa.GetPort();

  // If the remote host is an IPv4 only host and our interface if an IPv4/IPv6 mapped
  // Then return an IPv4 address instead of an IPv6
  if (GetPeerAddress(peerv4)) {
    if ((peerv4.GetVersion()==4)||(peerv4.IsV4Mapped())) {
      if (addr.IsV4Mapped()) {
        addr = Address(addr[12], addr[13], addr[14], addr[15]);
      }
    }
  }
  
#else

  sockaddr_in address;
  socklen_t size = sizeof(address);
  if (!ConvertOSError(::getsockname(os_handle,(struct sockaddr*)&address,&size)))
    return FALSE;

  addr = address.sin_addr;
  portNum = ntohs(address.sin_port);

#endif

  return TRUE;
}


BOOL PIPSocket::GetPeerAddress(Address & addr)
{
  WORD portNum;
  return GetPeerAddress(addr, portNum);
}

BOOL PIPSocket::GetPeerAddress(Address & addr, WORD & portNum)
{
#if P_HAS_IPV6

  Psockaddr sa;
  socklen_t size = sa.GetSize();
  if (!ConvertOSError(::getpeername(os_handle, sa, &size)))
    return FALSE;

  addr = sa.GetIP();
  portNum = sa.GetPort();

#else

  sockaddr_in address;
  socklen_t size = sizeof(address);
  if (!ConvertOSError(::getpeername(os_handle,(struct sockaddr*)&address,&size)))
    return FALSE;

  addr = address.sin_addr;
  portNum = ntohs(address.sin_port);

#endif

  return TRUE;
}


PString PIPSocket::GetLocalHostName()
{
  Address addr;

  if (GetLocalAddress(addr))
    return GetHostName(addr);

  return PString::Empty();
}


PString PIPSocket::GetPeerHostName()
{
  Address addr;

  if (GetPeerAddress(addr))
    return GetHostName(addr);

  return PString::Empty();
}


BOOL PIPSocket::Connect(const PString & host)
{
  Address ipnum;
#if P_HAS_IPV6
  if (GetHostAddress(host, ipnum))
    return Connect(GetDefaultIpAny(), 0, ipnum);
#else
  if (GetHostAddress(host, ipnum))
    return Connect(INADDR_ANY, 0, ipnum);
#endif  
  return FALSE;
}


BOOL PIPSocket::Connect(const Address & addr)
{
#if P_HAS_IPV6
  return Connect(GetDefaultIpAny(), 0, addr);
#else
  return Connect(INADDR_ANY, 0, addr);
#endif
}


BOOL PIPSocket::Connect(WORD localPort, const Address & addr)
{
#if P_HAS_IPV6
  return Connect(GetDefaultIpAny(), localPort, addr);
#else
  return Connect(INADDR_ANY, localPort, addr);
#endif
}


BOOL PIPSocket::Connect(const Address & iface, const Address & addr)
{
  return Connect(iface, 0, addr);
}


BOOL PIPSocket::Connect(const Address & iface, WORD localPort, const Address & addr)
{
  // close the port if it is already open
  if (IsOpen())
    Close();

  // make sure we have a port
  PAssert(port != 0, "Cannot connect socket without setting port");

#if P_HAS_IPV6

  Psockaddr sa(addr, port);

  // attempt to create a socket with the right family
  if (!OpenSocket(sa->sa_family))
    return FALSE;

  if (localPort != 0 || iface.IsValid()) {
    Psockaddr bind_sa(iface, localPort);

    if (!SetOption(SO_REUSEADDR, 0)) {
      os_close();
      return FALSE;
    }
    
    if (!ConvertOSError(::bind(os_handle, bind_sa, bind_sa.GetSize()))) {
      os_close();
      return FALSE;
    }
  }
  
  // attempt to connect
  if (os_connect(sa, sa.GetSize()))
    return TRUE;
  
#else

  // attempt to create a socket
  if (!OpenSocket())
    return FALSE;

  // attempt to connect
  sockaddr_in sin;
  if (localPort != 0 || iface.IsValid()) {
    if (!SetOption(SO_REUSEADDR, 0)) {
      os_close();
      return FALSE;
    }
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = iface;
    sin.sin_port        = htons(localPort);       // set the port
    if (!ConvertOSError(::bind(os_handle, (struct sockaddr*)&sin, sizeof(sin)))) {
      os_close();
      return FALSE;
    }
  }

  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port   = htons(port);  // set the port
  sin.sin_addr   = addr;
  if (os_connect((struct sockaddr *)&sin, sizeof(sin)))
    return TRUE;

#endif

  os_close();
  return FALSE;
}


BOOL PIPSocket::Listen(unsigned queueSize, WORD newPort, Reusability reuse)
{
#if P_HAS_IPV6
  return Listen(GetDefaultIpAny(), queueSize, newPort, reuse);
#else
  return Listen(INADDR_ANY, queueSize, newPort, reuse);
#endif
}


BOOL PIPSocket::Listen(const Address & bindAddr,
                       unsigned,
                       WORD newPort,
                       Reusability reuse)
{
  // make sure we have a port
  if (newPort != 0)
    port = newPort;

#if P_HAS_IPV6
  Psockaddr bind_sa(bindAddr, port); 

  if (IsOpen()) {
    int socketType;
    if (!GetOption(SO_TYPE, socketType, SOL_SOCKET) || bind_sa->sa_family != socketType)
      Close();
  }
#endif

  if (!IsOpen()) {
    // attempt to create a socket
#if P_HAS_IPV6
    if (!OpenSocket(bind_sa->sa_family))
      return FALSE;
#else
    if (!OpenSocket())
      return FALSE;
#endif
  }
  
#ifndef __BEOS__
  // attempt to listen
  if (!SetOption(SO_REUSEADDR, reuse == CanReuseAddress ? 1 : 0)) {
    os_close();
    return FALSE;
  }
#else
  // attempt to listen
  int value = reuse == CanReuseAddress ? 1 : 0;
  if (!SetOption(SO_REUSEADDR, &value, sizeof(int))) {
    os_close();
    return FALSE;
  }
#endif // BEOS

#if P_HAS_IPV6

  if (ConvertOSError(::bind(os_handle, bind_sa, bind_sa.GetSize()))) {
    Psockaddr sa;
    socklen_t size = sa.GetSize();
    if (!ConvertOSError(::getsockname(os_handle, sa, &size)))
      return FALSE;

    port = sa.GetPort();
    return TRUE;
  }

#else

  // attempt to listen
  sockaddr_in sin;
  memset(&sin, 0, sizeof(sin));
  sin.sin_family      = AF_INET;
  sin.sin_addr.s_addr = bindAddr;
  sin.sin_port        = htons(port);       // set the port

#ifdef __NUCLEUS_NET__
  int bind_result;
  if (port == 0)
    bind_result = ::bindzero(os_handle, (struct sockaddr*)&sin, sizeof(sin));
  else
    bind_result = ::bind(os_handle, (struct sockaddr*)&sin, sizeof(sin));
  if (ConvertOSError(bind_result))
#else
  if (ConvertOSError(::bind(os_handle, (struct sockaddr*)&sin, sizeof(sin))))
#endif
  {
    socklen_t size = sizeof(sin);
    if (ConvertOSError(::getsockname(os_handle, (struct sockaddr*)&sin, &size))) {
      port = ntohs(sin.sin_port);
      return TRUE;
    }
  }

#endif

  os_close();
  return FALSE;
}


const PIPSocket::Address & PIPSocket::Address::GetLoopback()
{
  return loopback4;
}


#if P_HAS_IPV6

/// Check for v4 mapped i nv6 address ::ffff:a.b.c.d
BOOL PIPSocket::Address::IsV4Mapped() const
{
  if (version != 6)
    return FALSE;
  return IN6_IS_ADDR_V4MAPPED(&v.six) || IN6_IS_ADDR_V4COMPAT(&v.six);
}


const PIPSocket::Address & PIPSocket::Address::GetLoopback6()
{
  return loopback6;
}


const PIPSocket::Address & PIPSocket::Address::GetAny6()
{
  return any6;
}

#endif


BOOL PIPSocket::Address::IsAny() const
{
  return (!IsValid());
}


const PIPSocket::Address & PIPSocket::Address::GetBroadcast()
{
  return broadcast4;
}


PIPSocket::Address::Address()
{
  *this = loopback4;
}


PIPSocket::Address::Address(const PString & dotNotation)
{
  operator=(dotNotation);
}


PIPSocket::Address::Address(PINDEX len, const BYTE * bytes)
{
  switch (len) {
#if P_HAS_IPV6
    case 16 :
      version = 6;
      memcpy(&v.six, bytes, len);
      break;
#endif
    case 4 :
      version = 4;
      memcpy(&v.four, bytes, len);
      break;

    default :
      version = 0;
  }
}


PIPSocket::Address::Address(const in_addr & addr)
{
  version = 4;
  v.four = addr;
}


#if P_HAS_IPV6
PIPSocket::Address::Address(const in6_addr & addr)
{
  version = 6;
  v.six = addr;
}

// Create an IP (v4 or v6) address from a sockaddr (sockaddr_in, sockaddr_in6 or sockaddr_in6_old) structure
PIPSocket::Address::Address(const int ai_family, const int ai_addrlen, struct sockaddr *ai_addr)
{
  switch (ai_family) {
#if P_HAS_IPV6
    case AF_INET6:
      if (ai_addrlen < (int)sizeof(sockaddr_in6))
        break;

      version = 6;
      v.six = ((struct sockaddr_in6 *)ai_addr)->sin6_addr;
      //sin6_scope_id, should be taken into account for link local addresses
      return;
#endif
    case AF_INET: 
      if (ai_addrlen < (int)sizeof(sockaddr_in))
        break;

      version = 4;
      v.four = ((struct sockaddr_in  *)ai_addr)->sin_addr;
      return;
  }
  version = 0;
}

#endif


#ifdef __NUCLEUS_NET__
PIPSocket::Address::Address(const struct id_struct & addr)
{
  operator=(addr);
}


PIPSocket::Address & PIPSocket::Address::operator=(const struct id_struct & addr)
{
  s_addr = (((unsigned long)addr.is_ip_addrs[0])<<24) +
           (((unsigned long)addr.is_ip_addrs[1])<<16) +
           (((unsigned long)addr.is_ip_addrs[2])<<8) +
           (((unsigned long)addr.is_ip_addrs[3]));
  return *this;
}
#endif
 

PIPSocket::Address & PIPSocket::Address::operator=(const in_addr & addr)
{
  version = 4;
  v.four = addr;
  return *this;
}

#if P_HAS_IPV6
PIPSocket::Address & PIPSocket::Address::operator=(const in6_addr & addr)
{
  version = 6;
  v.six = addr;
  return *this;
}
#endif


PObject::Comparison PIPSocket::Address::Compare(const PObject & obj) const
{
  const PIPSocket::Address & other = (const PIPSocket::Address &)obj;

  if (version < other.version)
    return LessThan;
  if (version > other.version)
    return GreaterThan;

#if P_HAS_IPV6
  if (version == 6) {
    int result = memcmp(&v.six, &other.v.six, sizeof(v.six));
    if (result < 0)
      return LessThan;
    if (result > 0)
      return GreaterThan;
    return EqualTo;
  }
#endif

  if ((DWORD)*this < other)
    return LessThan;
  if ((DWORD)*this > other)
    return GreaterThan;
  return EqualTo;
}

#if P_HAS_IPV6
bool PIPSocket::Address::operator*=(const PIPSocket::Address & addr) const
{
  if (version == addr.version)
    return operator==(addr);

  if (this->GetVersion() == 6 && this->IsV4Mapped()) 
    return PIPSocket::Address((*this)[12], (*this)[13], (*this)[14], (*this)[15]) == addr;
  else if (addr.GetVersion() == 6 && addr.IsV4Mapped()) 
    return *this == PIPSocket::Address(addr[12], addr[13], addr[14], addr[15]);
  return FALSE;
}

bool PIPSocket::Address::operator==(in6_addr & addr) const
{
  PIPSocket::Address a(addr);
  return Compare(a) == EqualTo;
}
#endif


bool PIPSocket::Address::operator==(in_addr & addr) const
{
  PIPSocket::Address a(addr);
  return Compare(a) == EqualTo;
}


bool PIPSocket::Address::operator==(DWORD dw) const
{
  if (dw != 0)
    return (DWORD)*this == dw;

  return !IsValid();
}


PIPSocket::Address & PIPSocket::Address::operator=(const PString & dotNotation)
{
#if P_HAS_IPV6

  struct addrinfo *res;
  struct addrinfo hints = { AI_NUMERICHOST, PF_UNSPEC }; // Could be IPv4: x.x.x.x or IPv6: x:x:x:x::x
  
  version = 0;
  memset(&v, 0, sizeof(v));
  
  if (getaddrinfo((const char *)dotNotation, NULL , &hints, &res) == 0) {
    if (res->ai_family == PF_INET6) {
      // IPv6 addr
      version = 6;
      struct sockaddr_in6 * addr_in6 = (struct sockaddr_in6 *)res->ai_addr;
      v.six = addr_in6->sin6_addr;
    } else {
      // IPv4 addr
      version = 4;
      struct sockaddr_in * addr_in = (struct sockaddr_in *)res->ai_addr;
      v.four = addr_in->sin_addr;
    }
    freeaddrinfo(res);
  }

#else //P_HAS_IPV6

  if (::strspn(dotNotation, "0123456789.") < ::strlen(dotNotation))
    *this = 0;
  else {
    version = 4;
    v.four.s_addr = inet_addr((const char *)dotNotation);
    if (v.four.s_addr == (DWORD)INADDR_NONE)
      v.four.s_addr = 0;
  }

#endif

  return *this;
}


PString PIPSocket::Address::AsString() const
{
#if P_HAS_IPV6
  if (version == 6) {
    PString str;
    Psockaddr sa(*this, 0);
    PAssertOS(getnameinfo(sa, sa.GetSize(), str.GetPointer(1024), 1024, NULL, 0, NI_NUMERICHOST) == 0);
    PINDEX percent = str.Find('%'); // used for scoped address e.g. fe80::1%ne0, (ne0=network interface 0)
    if (percent != P_MAX_INDEX)
      str[percent] = '\0';
    str.MakeMinimumSize();
    return str;
  }
#endif
#ifdef P_VXWORKS
  char ipStorage[INET_ADDR_LEN];
  inet_ntoa_b(v.four, ipStorage);
  return ipStorage;    
#else // P_VXWORKS
  return inet_ntoa(v.four);
#endif // P_VXWORKS
}


BOOL PIPSocket::Address::FromString(const PString & dotNotation)
{
  (*this) = dotNotation;
  return IsValid();

}


PIPSocket::Address::operator PString() const
{
  return AsString();
}


PIPSocket::Address::operator in_addr() const
{
  if (version != 4)
    return inaddr_empty;

  return v.four;
}


#if P_HAS_IPV6
PIPSocket::Address::operator in6_addr() const
{
  if (version != 6)
    return any6.v.six;

  return v.six;
}
#endif


BYTE PIPSocket::Address::operator[](PINDEX idx) const
{
  PASSERTINDEX(idx);
#if P_HAS_IPV6
  if (version == 6) {
    PAssert(idx <= 15, PInvalidParameter);
    return v.six.s6_addr[idx];
  }
#endif

  PAssert(idx <= 3, PInvalidParameter);
  return ((BYTE *)&v.four)[idx];
}


ostream & operator<<(ostream & s, const PIPSocket::Address & a)
{
  return s << a.AsString();
}

ostream & operator<<(ostream & s, const PString & str)
{
  return s << (const char *)str;
}

istream & operator>>(istream & s, PIPSocket::Address & a)
{
/// Not IPv6 ready !!!!!!!!!!!!!
  char dot1, dot2, dot3;
  unsigned b1, b2, b3, b4;
  s >> b1;
  if (!s.fail()) {
    if (s.peek() != '.')
      a = htonl(b1);
    else {
      s >> dot1 >> b2 >> dot2 >> b3 >> dot3 >> b4;
      if (!s.fail() && dot1 == '.' && dot2 == '.' && dot3 == '.')
        a = PIPSocket::Address((BYTE)b1, (BYTE)b2, (BYTE)b3, (BYTE)b4);
    }
  }
  return s;
}


PINDEX PIPSocket::Address::GetSize() const
{
  switch (version) {
#if P_HAS_IPV6
    case 6 :
      return 16;
#endif

    case 4 :
      return 4;
  }

  return 0;
}


BOOL PIPSocket::Address::IsValid() const
{
  switch (version) {
#if P_HAS_IPV6
    case 6 :
      return memcmp(&v.six, &any6.v.six, sizeof(v.six)) != 0;
#endif

    case 4 :
      return (DWORD)*this != INADDR_ANY;
  }
  return FALSE;
}


BOOL PIPSocket::Address::IsLoopback() const
{
#if P_HAS_IPV6
  if (version == 6)
    return IN6_IS_ADDR_LOOPBACK(&v.six);
#endif
  return *this == loopback4;
}


BOOL PIPSocket::Address::IsBroadcast() const
{
#if P_HAS_IPV6
  if (version == 6) // In IPv6, no broadcast exist. Only multicast
    return FALSE;
#endif

  return *this == broadcast4;
}

BOOL PIPSocket::Address::IsRFC1918() const 
{ 
#if P_HAS_IPV6
  if (version == 6) {
    if (IN6_IS_ADDR_LINKLOCAL(&v.six) || IN6_IS_ADDR_SITELOCAL(&v.six))
      return TRUE;
    if (IsV4Mapped())
      return PIPSocket::Address((*this)[12], (*this)[13], (*this)[14], (*this)[15]).IsRFC1918();
  }
#endif
  return (Byte1() == 10)
          ||
          (
            (Byte1() == 172)
            &&
            (Byte2() >= 16) && (Byte2() <= 31)
          )
          ||
          (
            (Byte1() == 192) 
            &&
            (Byte2() == 168)
          );
}

PIPSocket::InterfaceEntry::InterfaceEntry(const PString & _name,
                                          const Address & _addr,
                                          const Address & _mask,
                                          const PString & _macAddr
#if P_HAS_IPV6
                                         ,const PString & _ip6Addr
#endif
                                         )
  : name(_name.Trim()),
    ipAddr(_addr),
    netMask(_mask),
    macAddr(_macAddr)
#if P_HAS_IPV6
    , ip6Addr(_ip6Addr)
#endif
{
}


void PIPSocket::InterfaceEntry::PrintOn(ostream & strm) const
{
  strm << ipAddr;
#if P_HAS_IPV6
  if (!ip6Addr)
    strm << " [" << ip6Addr << ']';
#endif
  if (!macAddr)
    strm << " <" << macAddr << '>';
  if (!name)
    strm << " (" << name << ')';
}


#ifdef __NUCLEUS_NET__
BOOL PIPSocket::GetInterfaceTable(InterfaceTable & table)
{
    InterfaceEntry *IE;
    list<IPInterface>::iterator i;
    for(i=Route4Configuration->Getm_IPInterfaceList().begin();
            i!=Route4Configuration->Getm_IPInterfaceList().end();
            i++)
    {
        char ma[6];
        for(int j=0; j<6; j++) ma[j]=(*i).Getm_macaddr(j);
        IE = new InterfaceEntry((*i).Getm_name().c_str(), (*i).Getm_ipaddr(), ma );
        if(!IE) return false;
        table.Append(IE);
    }
    return true;
}
#endif

BOOL PIPSocket::GetNetworkInterface(PIPSocket::Address & addr)
{
  PIPSocket::InterfaceTable interfaceTable;
  if (PIPSocket::GetInterfaceTable(interfaceTable)) {
    PINDEX i;
    for (i = 0; i < interfaceTable.GetSize(); ++i) {
      PIPSocket::Address localAddr = interfaceTable[i].GetAddress();
      if (!localAddr.IsLoopback() && (!localAddr.IsRFC1918() || !addr.IsRFC1918()))
        addr = localAddr;
    }
  }
  return addr.IsValid();
}

//////////////////////////////////////////////////////////////////////////////
// PTCPSocket

PTCPSocket::PTCPSocket(WORD newPort)
{
  SetPort(newPort);
}


PTCPSocket::PTCPSocket(const PString & service)
{
  SetPort(service);
}


PTCPSocket::PTCPSocket(const PString & address, WORD newPort)
{
  SetPort(newPort);
  Connect(address);
}


PTCPSocket::PTCPSocket(const PString & address, const PString & service)
{
  SetPort(service);
  Connect(address);
}


PTCPSocket::PTCPSocket(PSocket & socket)
{
  Accept(socket);
}


PTCPSocket::PTCPSocket(PTCPSocket & tcpSocket)
{
  Accept(tcpSocket);
}


PObject * PTCPSocket::Clone() const
{
  return new PTCPSocket(port);
}


// By default IPv4 only adresses
BOOL PTCPSocket::OpenSocket()
{
  return ConvertOSError(os_handle = os_socket(AF_INET, SOCK_STREAM, 0));
}


// ipAdressFamily should be AF_INET or AF_INET6
BOOL PTCPSocket::OpenSocket(int ipAdressFamily) 
{
  return ConvertOSError(os_handle = os_socket(ipAdressFamily, SOCK_STREAM, 0));
}


const char * PTCPSocket::GetProtocolName() const
{
  return "tcp";
}


BOOL PTCPSocket::Write(const void * buf, PINDEX len)
{
  flush();
  PINDEX writeCount = 0;

  while (len > 0) {
    if (!os_sendto(((char *)buf)+writeCount, len, 0, NULL, 0))
      return FALSE;
    writeCount += lastWriteCount;
    len -= lastWriteCount;
  }

  lastWriteCount = writeCount;
  return TRUE;
}


BOOL PTCPSocket::Listen(unsigned queueSize, WORD newPort, Reusability reuse)
{
#if P_HAS_IPV6
  return Listen(GetDefaultIpAny(), queueSize, newPort, reuse);
#else
  return Listen(INADDR_ANY, queueSize, newPort, reuse);
#endif
}


BOOL PTCPSocket::Listen(const Address & bindAddr,
                        unsigned queueSize,
                        WORD newPort,
                        Reusability reuse)
{
  if (PIPSocket::Listen(bindAddr, queueSize, newPort, reuse) &&
      ConvertOSError(::listen(os_handle, queueSize)))
    return TRUE;

  os_close();
  return FALSE;
}


BOOL PTCPSocket::Accept(PSocket & socket)
{
  PAssert(PIsDescendant(&socket, PIPSocket), "Invalid listener socket");

#if P_HAS_IPV6

  Psockaddr sa;
  PINDEX size = sa.GetSize();
  if (!os_accept(socket, sa, &size))
    return FALSE;
    
#else

  sockaddr_in address;
  address.sin_family = AF_INET;
  PINDEX size = sizeof(address);
  if (!os_accept(socket, (struct sockaddr *)&address, &size))
    return FALSE;

#endif

  port = ((PIPSocket &)socket).GetPort();
  
  return TRUE;
}


BOOL PTCPSocket::WriteOutOfBand(void const * buf, PINDEX len)
{
#ifdef __NUCLEUS_NET__
  PAssertAlways("WriteOutOfBand unavailable on Nucleus Plus");
  //int count = NU_Send(os_handle, (char *)buf, len, 0);
  int count = ::send(os_handle, (const char *)buf, len, 0);
#elif defined(P_VXWORKS)
  int count = ::send(os_handle, (char *)buf, len, MSG_OOB);
#else
  int count = ::send(os_handle, (const char *)buf, len, MSG_OOB);
#endif
  if (count < 0) {
    lastWriteCount = 0;
    return ConvertOSError(count, LastWriteError);
  }
  else {
    lastWriteCount = count;
    return TRUE;
  }
}


void PTCPSocket::OnOutOfBand(const void *, PINDEX)
{
}


//////////////////////////////////////////////////////////////////////////////
// PIPDatagramSocket

PIPDatagramSocket::PIPDatagramSocket()
{
}


BOOL PIPDatagramSocket::ReadFrom(void * buf, PINDEX len,
                                 Address & addr, WORD & port)
{
  lastReadCount = 0;

#if P_HAS_IPV6

  Psockaddr sa;
  PINDEX size = sa.GetSize();
  if (os_recvfrom(buf, len, 0, sa, &size)) {
    addr = sa.GetIP();
    port = sa.GetPort();
  }

#else

  sockaddr_in sockAddr;
  PINDEX addrLen = sizeof(sockAddr);
  if (os_recvfrom(buf, len, 0, (struct sockaddr *)&sockAddr, &addrLen)) {
    addr = sockAddr.sin_addr;
    port = ntohs(sockAddr.sin_port);
  }

#endif

  return lastReadCount > 0;
}


BOOL PIPDatagramSocket::WriteTo(const void * buf, PINDEX len,
                                const Address & addr, WORD port)
{
  lastWriteCount = 0;

#if P_HAS_IPV6

  Psockaddr sa(addr, port);
  return os_sendto(buf, len, 0, sa, sa.GetSize()) && lastWriteCount >= len;

#else

  sockaddr_in sockAddr;
  sockAddr.sin_family = AF_INET;
  sockAddr.sin_addr = addr;
  sockAddr.sin_port = htons(port);
  return os_sendto(buf, len, 0, (struct sockaddr *)&sockAddr, sizeof(sockAddr))
         && lastWriteCount >= len;

#endif
}


//////////////////////////////////////////////////////////////////////////////
// PUDPSocket

PUDPSocket::PUDPSocket(WORD newPort)
{
  sendPort = 0;
  SetPort(newPort);
  OpenSocket();
}

PUDPSocket::PUDPSocket(PQoS * qos, WORD newPort)
{
  if (qos != NULL)
      qosSpec = *qos;
  sendPort = 0;
  SetPort(newPort);
  OpenSocket();
}


PUDPSocket::PUDPSocket(const PString & service, PQoS * qos)
{
  if (qos != NULL)
      qosSpec = *qos;
  sendPort = 0;
  SetPort(service);
  OpenSocket();
}


PUDPSocket::PUDPSocket(const PString & address, WORD newPort)
{
  sendPort = 0;
  SetPort(newPort);
  Connect(address);
}


PUDPSocket::PUDPSocket(const PString & address, const PString & service)
{
  sendPort = 0;
  SetPort(service);
  Connect(address);
}


BOOL PUDPSocket::ModifyQoSSpec(PQoS * qos)
{
  if (qos==NULL)
    return FALSE;

  qosSpec = *qos;
  return TRUE;
}

#if P_HAS_QOS
PQoS & PUDPSocket::GetQoSSpec()
{
  return qosSpec;
}
#endif

BOOL PUDPSocket::ApplyQoS()
{
#ifdef _WIN32_WCE
  return FALSE;   //QoS not supported
#endif

  char DSCPval = 0;
  if (qosSpec.GetDSCP() < 0 ||
      qosSpec.GetDSCP() > 63) {
    if (qosSpec.GetServiceType() == SERVICETYPE_PNOTDEFINED)
      return TRUE;
    else {
      switch (qosSpec.GetServiceType()) {
        case SERVICETYPE_GUARANTEED:
          DSCPval = PQoS::guaranteedDSCP;
          break;
        case SERVICETYPE_CONTROLLEDLOAD:
          DSCPval = PQoS::controlledLoadDSCP;
          break;
        case SERVICETYPE_BESTEFFORT:
        default:
          DSCPval = PQoS::bestEffortDSCP;
          break;
      }
    }
  }
  else
    DSCPval = (char)qosSpec.GetDSCP();

#ifdef _WIN32
#if P_HAS_QOS
  if (disableGQoS)
    return FALSE;

  BOOL usesetsockopt = FALSE;

  OSVERSIONINFO versInfo;
  ZeroMemory(&versInfo,sizeof(OSVERSIONINFO));
  versInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if (!(GetVersionEx(&versInfo)))
    usesetsockopt = TRUE;
  else {
    if (versInfo.dwMajorVersion < 5)
      usesetsockopt = TRUE;

    if (disableGQoS)
          return FALSE;

    BOOL usesetsockopt = FALSE;

    if (versInfo.dwMajorVersion == 5 &&
        versInfo.dwMinorVersion == 0)
      usesetsockopt = TRUE;         //Windows 2000 does not always support QOS_DESTADDR
  }

  BOOL retval = FALSE;
  if (!usesetsockopt && sendAddress.IsValid() && sendPort != 0) {
    sockaddr_in sa;
    sa.sin_family = AF_INET;
    sa.sin_port = htons(sendPort);
    sa.sin_addr = sendAddress;
    memset(sa.sin_zero,0,8);

    char * inBuf = new char[2048];
    memset(inBuf,0,2048);
    DWORD bufLen = 0;
    PWinQoS wqos(qosSpec, (struct sockaddr *)(&sa), inBuf, bufLen);

    DWORD dummy = 0;
    int irval = WSAIoctl(os_handle, SIO_SET_QOS, inBuf, bufLen, NULL, 0, &dummy, NULL, NULL);

    delete[] inBuf;

    return irval == 0;
  }

  if (!usesetsockopt)
    return retval;

#endif  // P_HAS_QOS
#endif  // _WIN32

  unsigned int setDSCP = DSCPval<<2;

  int rv = 0;
  unsigned int curval = 0;
  socklen_t cursize = sizeof(curval);
  rv = ::getsockopt(os_handle,IPPROTO_IP, IP_TOS, (char *)(&curval), &cursize);
  if (curval == setDSCP)
    return TRUE;    //Required DSCP already set


  rv = ::setsockopt(os_handle, IPPROTO_IP, IP_TOS, (char *)&setDSCP, sizeof(setDSCP));

  if (rv != 0) {
    int err;
#ifdef _WIN32
    err = WSAGetLastError();
#else
    err = errno;
#endif
    PTRACE(3,"QOS\tsetsockopt failed with code " << err);
    return FALSE;
  }
    
  return TRUE;
}

BOOL PUDPSocket::OpenSocketGQOS(int af, int type, int proto)
{
#ifdef _WIN32_WCE   //QOS not supported
  return ConvertOSError(os_handle = os_socket(af, type, proto));
#endif

#if defined(_WIN32) && defined(P_HAS_QOS)
    
  DWORD bufferSize = 0;
  DWORD numProtocols, i;
  LPWSAPROTOCOL_INFO installedProtocols, qosProtocol;

  //Try to find a QOS-enabled protocol
 
  BOOL retval = ConvertOSError(numProtocols = WSAEnumProtocols(((proto==0) ? NULL : &proto),
                                                            NULL,
                                                            &bufferSize));
    
  if (numProtocols == SOCKET_ERROR && WSAGetLastError()!=WSAENOBUFS) 
    return retval;

  installedProtocols = (LPWSAPROTOCOL_INFO)(new BYTE[bufferSize]);
  retval = ConvertOSError(numProtocols = WSAEnumProtocols(((proto==0) ? NULL : &proto),
                                                            installedProtocols,
                                                            &bufferSize));
  if (numProtocols == SOCKET_ERROR) {
    delete[] installedProtocols;
    return retval;
  }

  qosProtocol = installedProtocols;
  BOOL haveQoSproto = FALSE;

  for (i=0; i<numProtocols; qosProtocol++, i++) {
    if ((qosProtocol->dwServiceFlags1 & XP1_QOS_SUPPORTED) &&
        (qosProtocol->iSocketType == type) &&
        (qosProtocol->iAddressFamily == af)) {
      haveQoSproto = TRUE;
      break;
    }
  }

  if (haveQoSproto) {
    retval =  ConvertOSError(os_handle = WSASocket(af,
                                                   type,
                                                   proto,
                                                   qosProtocol,
                                                   0,
                                                   WSA_FLAG_OVERLAPPED));
  }
  else
  {    
    retval = ConvertOSError(os_handle = WSASocket (af,
                                                   type,
                                                   proto,
                                                   NULL,
                                                   0,
                                                   WSA_FLAG_OVERLAPPED));
  }

  delete[] installedProtocols;

  if (os_handle == INVALID_SOCKET)
    return retval;
#else
  BOOL retval = ConvertOSError(os_handle = os_socket(af, type, proto));
#endif

  return retval;
}

#ifdef _WIN32
#ifndef _WIN32_WCE
#ifdef P_HAS_QOS

#define COULD_HAVE_QOS

static BOOL CheckOSVersion()
{
    OSVERSIONINFO versInfo;
    ZeroMemory(&versInfo,sizeof(OSVERSIONINFO));
    versInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if (GetVersionEx(&versInfo))
    {
        if (versInfo.dwMajorVersion > 5 ||
           (versInfo.dwMajorVersion == 5 &&
            versInfo.dwMinorVersion > 0))
          return TRUE;
    }
  return FALSE;
}

#endif
#endif
#endif

BOOL PUDPSocket::OpenSocket()
{
#ifdef COULD_HAVE_QOS
  if (CheckOSVersion()) 
    return OpenSocketGQOS(AF_INET, SOCK_DGRAM, 0);
#endif

  return ConvertOSError(os_handle = os_socket(AF_INET,SOCK_DGRAM, 0));
}

BOOL PUDPSocket::OpenSocket(int ipAdressFamily)
{
#ifdef COULD_HAVE_QOS
  if (CheckOSVersion()) 
    return OpenSocketGQOS(ipAdressFamily, SOCK_DGRAM, 0);
#endif

  return ConvertOSError(os_handle = os_socket(ipAdressFamily,SOCK_DGRAM, 0));
}

const char * PUDPSocket::GetProtocolName() const
{
  return "udp";
}


BOOL PUDPSocket::Connect(const PString & address)
{
  sendPort = 0;
  return PIPDatagramSocket::Connect(address);
}


BOOL PUDPSocket::Read(void * buf, PINDEX len)
{
  return PIPDatagramSocket::ReadFrom(buf, len, lastReceiveAddress, lastReceivePort);
}


BOOL PUDPSocket::Write(const void * buf, PINDEX len)
{
  if (sendPort == 0)
    return PIPDatagramSocket::Write(buf, len);
  else
    return PIPDatagramSocket::WriteTo(buf, len, sendAddress, sendPort);
}


void PUDPSocket::SetSendAddress(const Address & newAddress, WORD newPort)
{
  sendAddress = newAddress;
  sendPort    = newPort;
  ApplyQoS();
}


void PUDPSocket::GetSendAddress(Address & address, WORD & port)
{
  address = sendAddress;
  port    = sendPort;
}


void PUDPSocket::GetLastReceiveAddress(Address & address, WORD & port)
{
  address = lastReceiveAddress;
  port    = lastReceivePort;
}

//////////////////////////////////////////////////////////////////////////////

BOOL PICMPSocket::OpenSocket(int)
{
  return FALSE;
}

// End Of File ///////////////////////////////////////////////////////////////


syntax highlighted by Code2HTML, v. 0.9.1