/* * 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 #endif #include #include #include #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 #include #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::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; idwServiceFlags1 & 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 ///////////////////////////////////////////////////////////////