/**************************************************************************** * * * cryptlib TCP/IP Interface Header * * Copyright Peter Gutmann 1998-2004 * * * ****************************************************************************/ #ifdef USE_TCP /**************************************************************************** * * * AMX * * * ****************************************************************************/ #ifdef __AMX__ #include /* All KwikNet functions have kn_ prefix, to use the standard sockets API names we have to redefine them to the usual names */ #define accept kn_accept #define bind kn_bind #define closesocket kn_close #define connect kn_connect #define getsockopt kn_getsockopt #define listen kn_listen #define recv kn_recv #define select kn_select #define send kn_send #define setsockopt kn_setsockopt #define shutdown kn_shutdown #define socket kn_socket #endif /* AMX */ /**************************************************************************** * * * BeOS * * * ****************************************************************************/ /* If we're building under BeOS the system may have the new(er) BONE (BeOs Network Environment) network stack. This didn't quite make it into BeOS v5 before the demise of Be Inc but was leaked after Be folded, as was the experimental/developmental Dano release of BeOS, which would have become BeOS 5.1 and also has a newer network stack. In order to detect this we have to pull in sys/socket.h before we try anything else */ #ifdef __BEOS__ #include #endif /* __BEOS__ */ /* If we're using the original (rather minimal) BeOS TCP/IP stack, we have to provide a customised interface for it rather than using the same one as the generic Unix/BSD interface */ #if defined( __BEOS__ ) && !defined( BONE_VERSION ) #include #include #include #include /* BeOS doesn't define any of the PF_xxx's, howewever it does defines some of the AF_xxx equivalents, since these are synonyms we just define the PF_xxx's ourselves */ #define PF_UNSPEC 0 #define PF_INET AF_INET /* BeOS doesn't define in_*_t's */ #define in_addr_t u_long #define in_port_t u_short /* BeOS doesn't define NO_ADDRESS, but NO_DATA is a synonym for this */ #define NO_ADDRESS NO_DATA /* BeOS doesn't support checking for anything except readability in select() and only supports one or two socket options, so we define our own versions of these functions that no-op out unsupported options */ #define select( sockets, readFD, writeFD, exceptFD, timeout ) \ my_select( sockets, readFD, writeFD, exceptFD, timeout ) #define getsockopt( socket, level, optname, optval, optlen ) \ my_getsockopt( socket, level, optname, optval, optlen ) #define setsockopt( socket, level, optname, optval, optlen ) \ my_setsockopt( socket, level, optname, optval, optlen ) /* The following options would be required, but aren't provided by BeOS. If you're building under a newer BeOS version that supports these options, you'll also need to update my_set/setsockopt() to no longer no-op them out */ #define SO_ERROR -1 #define TCP_NODELAY -1 /**************************************************************************** * * * uITRON * * * ****************************************************************************/ #elif defined( __ITRON__ ) /* uITRON has a TCP/IP API but it doesn't seem to be widely used, and the only available documentation is in Japanese. If you need TCP/IP support under uITRON and have an implementation available, you can add the appropriate interface by replacing net_tcp.c and net_dns.c with the equivalent uITRON API glue code */ #error You need to set up the TCP/IP headers and interface in net_tcp.c/net_dns.c /**************************************************************************** * * * Unix and Unix-compatible Systems * * * ****************************************************************************/ /* Guardian sockets originally couldn't handle nonblocking I/O like standard BSD sockets, but required the use of a special non-blocking socket type (nowait sockets) and the use of AWAITIOX() on the I/O tag returned from the nowait socket call, since the async state was tied to this rather than to the socket handle. One of the early G06 releases added select() support, although even the latest documentation still claims that select() isn't supported. To avoid having to support two completely different interfaces, we use the more recent (and BSD standard) select() interface. Anyone running this code on old systems will have to add wrappers for the necessary socket_nw()/accept_nw()/AWAITIOX() calls */ #elif ( defined( __BEOS__ ) && defined( BONE_VERSION ) ) || \ defined( __ECOS__ ) || defined( __MVS__ ) || \ defined( __PALMOS__ ) || defined( __RTEMS__ ) || \ defined ( __SYMBIAN32__ ) || defined( __TANDEM_NSK__ ) || \ defined( __TANDEM_OSS__ ) || defined( __UNIX__ ) /* C_IN is a cryptlib.h value which is also defined in some versions of netdb.h, so we have to undefine it before we include any network header files */ #undef C_IN /* PHUX and Tandem OSS have broken networking headers that require manually defining _XOPEN_SOURCE_EXTENDED in order for various function prototypes to be enabled. The Tandem variant of this problem has all the function prototypes for the NSK target and a comment by the 'else' that follows saying that it's for the OSS target, but then an ifdef for _XOPEN_SOURCE_EXTENDED that prevents it from being enabled unless _XOPEN_SOURCE_EXTENDED is also defined */ #if ( defined( __hpux ) && ( OSVERSION >= 10 ) ) || defined( _OSS_TARGET ) #define _XOPEN_SOURCE_EXTENDED 1 #endif /* Workaround for inconsistent networking headers */ /* In OS X 10.3 (Panther), Apple broke the bind interface by changing the BIND_4_COMPAT define to BIND_8_COMPAT ("Apple reinvented the wheel and made it square" is one of the more polite comments on this change). In order to get things to work, we have to define BIND_8_COMPAT here, which forces the inclusion of nameser_compat.h when we include nameser.h. All (non-Apple) systems automatically define BIND_4_COMPAT to force this inclusion, since Bind9 support (in the form of anything other than the installed binaries) is still pretty rare */ #if defined( __APPLE__ ) && !defined( BIND_8_COMPAT ) #define BIND_8_COMPAT #endif /* Mac OS X without backwards-compatibility bind define */ #include #include #include #if defined( __APPLE__ ) || defined( __BEOS__ ) || defined( __bsdi__ ) || \ defined( __FreeBSD__ ) || defined( __hpux ) || defined( __MVS__ ) || \ defined( __NetBSD__ ) || defined( __OpenBSD__ ) || defined( __QNX__ ) || \ ( defined( sun ) && OSVERSION <= 5 ) || defined( __SYMBIAN32__ ) || \ defined( __VMCMS__ ) #include #endif /* OS x || BeOS || *BSDs || PHUX || SunOS 4.x/2.5.x || Symbian OS */ #include #if !( defined( __CYGWIN__ ) || defined( __PALMOS__ ) || \ defined( __SYMBIAN32__ ) ) #include #endif /* Cygwin || Symbian OS */ #if defined( __MVS__ ) || defined( __VMCMS__ ) /* The following have conflicting definitions in xti.h */ #undef T_NULL #undef T_UNSPEC #endif /* MVS || VM */ #if !defined( __MVS__ ) /* netinet/tcp.h is a BSD-ism, but all Unixen seem to use this even if XPG4 and SUS say it should be in xti.h */ #include #endif /* !MVS */ #if !( defined( __CYGWIN__ ) || defined( __PALMOS__ ) || \ defined( __SYMBIAN32__ ) ) #include #endif /* Cygwin || Symbian OS */ #ifndef TCP_NODELAY #include #if defined( __MVS__ ) || defined( __VMCMS__ ) /* The following have conflicting definitions in nameser.h */ #undef T_NULL #undef T_UNSPEC #endif /* MVS || VM */ #endif /* TCP_NODELAY */ #ifdef __SCO_VERSION__ #include #ifndef SIGIO #include #endif /* SIGIO not defined in signal.h - only from SCO */ #endif /* UnixWare/SCO */ #if defined( _AIX ) || defined( __PALMOS__ ) || defined( __QNX__ ) #include #endif /* Aches || Palm OS || QNX */ #include #include #include #ifdef __PALMOS__ /* Needed for close(). unistd.h, which contains this, is normally included by default in Unix environments, but isn't for PalmOS */ #include #endif /* Palm OS */ /* AIX and SCO don't define sockaddr_storage in their IPv6 headers so we define a placeholder equivalent here */ #if defined( IPv6 ) && \ ( ( defined( _AIX ) && OSVERSION <= 5 ) || defined( __SCO_VERSION__ ) ) struct sockaddr_storage { union { struct sockaddr_in6 bigSockaddrStruct; char padding[ 128 ]; } dummyMember; }; #endif /* IPv6 versions without sockaddr_storage */ /* PHUX generally doesn't define h_errno, we have to be careful here since later versions may use macros to get around threading issues so we check for the existence of a macro with the given name before defining our own version */ #if defined( __hpux ) && !defined( h_errno ) /* Usually missing from netdb.h */ extern int h_errno; #endif /* PHUX && !h_errno */ /**************************************************************************** * * * VxWorks * * * ****************************************************************************/ #elif defined( __VXWORKS__ ) /* VxWorks includes a (somewhat minimal) BSD sockets implementation, if you're using a newer, enhanced implementation or a third-party alternative with more complete functionality (and less bugs), you can use the generic Unix headers and defines further down */ #include #include /**************************************************************************** * * * Windows * * * ****************************************************************************/ #elif defined( __WINDOWS__ ) /* Winsock2 wasn't available until VC++/eVC++ 4.0, so if we're running an older version we have to use the Winsock1 interface */ #if defined( _MSC_VER ) && ( _MSC_VER <= 800 ) || \ defined( __WINCE__ ) && ( _WIN32_WCE < 400 ) #include #else #include #include #endif /* Older WinCE vs. newer WinCE and Win32 */ /* VC++ 7 and newer have IPv6 support included in ws2tcpip.h, VC++ 6 can have it bolted-on using the IPv6 Technology Preview but it's not present by default. In addition the Tech.Preview is quite buggy and unstable, leaking handles and memory and in some cases leading to runaway memory consumption that locks up the machine if the process isn't killed in time, so we don't want to encourage its use */ #if defined( _MSC_VER ) && ( _MSC_VER > 1300 ) /* #include */ /* From IPv6 Tech.Preview */ #endif /* VC++ 7 and newer */ /* VC++ 7 and newer have DNS headers, for older versions we have to define the necessary types and constants ourselves */ #if defined( _MSC_VER ) && ( _MSC_VER > 1300 ) #include #elif defined( _MSC_VER ) && ( _MSC_VER > 800 ) /* windns.h is quite new and many people don't have it yet, not helped by the fact that it's also changed over time. For example, DnsRecordListFree() has also been DnsFreeRecordList() and DnsFree() at various times, with the parameters changing to match. Because of this, we have to define our own (very cut-down) subset of what's in there here. We define PIP4_ARRAY as a void * since it's only used to specify optional DNS servers to query, we never need this so we just set the parameter to NULL. As with the DnsXXX functions, PIP4_ARRAY has changed over time. It was known as PIP_ARRAY in the original VC++ .NET release, but was renamed PIP4_ARRAY for .NET 2003, although some MSDN entries still refer to PIP_ARRAY even in the 2003 version */ typedef LONG DNS_STATUS; typedef void *PIP4_ARRAY; typedef DWORD IP4_ADDRESS; typedef enum { DnsFreeFlat, DnsFreeRecordList } DNS_FREE_TYPE; typedef enum { DnsConfigPrimaryDomainName_W, DnsConfigPrimaryDomainName_A, DnsConfigPrimaryDomainName_UTF8, DnsConfigAdapterDomainName_W, DnsConfigAdapterDomainName_A, DnsConfigAdapterDomainName_UTF8, DnsConfigDnsServerList, DnsConfigSearchList, DnsConfigAdapterInfo, DnsConfigPrimaryHostNameRegistrationEnabled, DnsConfigAdapterHostNameRegistrationEnabled, DnsConfigAddressRegistrationMaxCount, DnsConfigHostName_W, DnsConfigHostName_A, DnsConfigHostName_UTF8, DnsConfigFullHostName_W, DnsConfigFullHostName_A, DnsConfigFullHostName_UTF8 } DNS_CONFIG_TYPE; #define DNS_TYPE_A 1 #define DNS_TYPE_PTR 12 #define DNS_TYPE_SRV 33 #define DNS_QUERY_STANDARD 0 #define DNS_QUERY_BYPASS_CACHE 8 typedef struct { /* Technically these are DWORDs, but only integers are allowed for bitfields. This is OK in this case because sizeof( int ) == sizeof( DWORD ) */ unsigned int Section : 2; unsigned int Delete : 1; unsigned int CharSet : 2; unsigned int Unused : 3; unsigned int Reserved : 24; } DNS_RECORD_FLAGS; typedef struct { IP4_ADDRESS IpAddress; } DNS_A_DATA, *PDNS_A_DATA; typedef struct { LPTSTR pNameHost; } DNS_PTR_DATA, *PDNS_PTR_DATA; typedef struct { LPTSTR pNameTarget; WORD wPriority; WORD wWeight; WORD wPort; WORD Pad; } DNS_SRV_DATA, *PDNS_SRV_DATA; typedef struct _DnsRecord { struct _DnsRecord *pNext; LPTSTR pName; WORD wType; WORD wDataLength; union { DWORD DW; DNS_RECORD_FLAGS S; } Flags; DWORD dwTtl; DWORD dwReserved; union { DNS_A_DATA A; DNS_PTR_DATA PTR, Ptr, NS, Ns, CNAME, Cname, MB, Mb, MD, Md, MF, Mf, MG, Mg, MR, Mr; #if 0 DNS_MINFO_DATA MINFO, Minfo, RP, Rp; DNS_MX_DATA MX, Mx, AFSDB, Afsdb, RT, Rt; DNS_TXT_DATA HINFO, Hinfo, ISDN, Isdn, TXT, Txt, X25; DNS_NULL_DATA Null; DNS_WKS_DATA WKS, Wks; DNS_AAAA_DATA AAAA; DNS_KEY_DATA KEY, Key; DNS_SIG_DATA SIG, Sig; DNS_ATMA_DATA ATMA, Atma; DNS_NXT_DATA NXT, Nxt; #endif /* 0 */ DNS_SRV_DATA SRV, Srv; #if 0 DNS_TKEY_DATA TKEY, Tkey; DNS_TSIG_DATA TSIG, Tsig; DNS_WINS_DATA WINS, Wins; DNS_WINSR_DATA WINSR, WinsR, NBSTAT, Nbstat; #endif /* 0 */ } Data; } DNS_RECORD, *PDNS_RECORD; #endif /* VC++ 7 and newer vs. older versions */ /* For backwards-compatibility purposes, wspiapi.h overrides the new address/ name-handling functions introduced for IPv6 with complex macros that substitute inline function calls that try and dynamically load different libraries depending on the Windows version and call various helper functions to provide the same service. Since we dynamically load the required libraries, we don't need any of this complexity, so we undefine the macros in order to make our own ones work */ #ifdef getaddrinfo #undef freeaddrinfo #undef getaddrinfo #undef getnameinfo #endif /* getaddrinfo defined as macros in wspiapi.h */ /* Set up the appropriate calling convention for the Winsock API */ #if defined( WSAAPI ) #define SOCKET_API WSAAPI #elif defined( WINSOCKAPI ) #define SOCKET_API WINSOCKAPI #else #define SOCKET_API FAR PASCAL #endif /* WSAAPI */ /**************************************************************************** * * * Other Systems * * * ****************************************************************************/ #else #error You need to set up OS-specific networking include handling in net_tcp.h #endif /* OS-specific includes and defines */ /**************************************************************************** * * * General/Portability Defines * * * ****************************************************************************/ /* Now that we've included all of the networking headers, try and guess whether this is an IPv6-enabled system. We can detect this by the existence of definitions for the EAI_xxx return values from getaddrinfo(). Note that we can't safely detect it using the more obvious AF_INET6 since many headers defined this in anticipation of IPv6 long before the remaining code support was present */ #if defined( EAI_BADFLAGS ) && defined( EAI_NONAME ) #define IPv6 #endif /* getaddrinfo() return values defined */ /* BeOS with the BONE network stack has the necessary IPv6 defines but no actual IPv6 support, so we disable it again */ #if defined( __BEOS__ ) && defined( BONE_VERSION ) && defined( IPv6 ) #undef IPv6 #endif /* BeOS with BONE */ /* The size of a (v4) IP address and the number of IP addresses that we try to connect to for a given host, used if we're providing an emulated (IPv4-only) getaddrinfo() */ #define IP_ADDR_SIZE 4 #define IP_ADDR_COUNT 16 /* Test for common socket errors. For isBadSocket() we don't just compare the socket to INVALID_SOCKET but perform a proper range check both to catch any problems with buggy implementations that may return something other than -1 to indicate an error, and because we're going to use the value in FD_xyz() macros which often don't perform any range checking, and a value outside the range 0...FD_SETSIZE can cause segfaults and other problems. In addition we exclude stdin/stdout/stderr if they're present, since a socket with these handle values is somewhat suspicious, The one exception to this is Windows sockets, which don't use a Berkeley- type bitflag representation and therefore don't have the range problems that the Berkeley implementation does */ #ifndef __WINDOWS__ #define INVALID_SOCKET -1 #endif /* __WINDOWS__ */ #if defined( __WINDOWS__ ) #define isBadSocket( socket ) ( ( socket ) <= 0 ) #elif defined( STDERR_FILENO ) #define isBadSocket( socket ) ( ( socket ) <= STDERR_FILENO || \ ( socket ) >= FD_SETSIZE ) #else #define isBadSocket( socket ) ( ( socket ) <= 0 || \ ( socket ) >= FD_SETSIZE ) #endif /* STDERR_FILENO */ #ifdef __WINDOWS__ #define isSocketError( status ) ( ( status ) == SOCKET_ERROR ) #define isBadAddress( address ) ( ( address ) == INADDR_NONE ) #else #define isSocketError( status ) ( ( status ) == -1 ) #define isBadAddress( address ) ( ( address ) == ( in_addr_t ) -1 ) #endif /* Windows vs. other systems */ #if defined( __SYMBIAN32__ ) /* Symbian OS doesn't support nonblocking I/O */ #define isNonblockWarning() 0 #elif defined( __BEOS__ ) #if defined( BONE_VERSION ) /* BONE returns "Operation now in progress" */ #define isNonblockWarning() ( errno == EWOULDBLOCK || \ errno == 0x80007024 ) #else /* BeOS, even though it supposedly doesn't support nonblocking sockets, can return EWOULDBLOCK */ #define isNonblockWarning() ( errno == EWOULDBLOCK ) #endif /* BeOS with/without BONE */ #elif defined( __WINDOWS__ ) #define isNonblockWarning() ( WSAGetLastError() == WSAEWOULDBLOCK ) #else #define isNonblockWarning() ( errno == EINPROGRESS ) #endif /* OS-specific socket error handling */ /* Error code handling */ #ifdef __WINDOWS__ #define getErrorCode() WSAGetLastError() #define getHostErrorCode() WSAGetLastError() #else #define getErrorCode() errno #if ( defined( __MVS__ ) && defined( _OPEN_THREADS ) ) /* MVS converts this into a hidden function in the presence of threads, but not transparently like other systems */ #define getHostErrorCode() ( *__h_errno() ) #else #define getHostErrorCode() h_errno #endif /* MVS */ #endif /* OS-specific error code handling */ /* Windows and BeOS use a distinct socket handle type and require the use of separate closesocket() and ioctlsocket() functions because socket handles aren't the same as standard Windows/BeOS handles */ #ifdef SOCKET /* MP-RAS has already defined this */ #undef SOCKET #endif /* SOCKET */ #define SOCKET int #ifndef __WINDOWS__ #if !defined( __BEOS__ ) || \ ( defined( __BEOS__ ) && defined( BONE_VERSION ) ) #define closesocket close #endif /* BeOS without BONE */ #define ioctlsocket ioctl #endif /* OS-specific portability defines */ /* The generic sockaddr struct used to reserve storage for protocol-specific sockaddr structs. The IPv4 equivalent is given further down in the IPv6- mapping definitions */ #ifdef IPv6 #define SOCKADDR_STORAGE struct sockaddr_storage #endif /* IPv6 */ /* Many systems don't define the in_*_t's */ #if defined( __APPLE__ ) || defined( __BEOS__ ) || \ defined( __bsdi__ ) || defined( _CRAY ) || \ defined( __CYGWIN__ ) || defined( __FreeBSD__ ) || \ defined( __hpux ) || defined( __linux__ ) || \ defined( __NetBSD__ ) || defined( __OpenBSD__ ) || \ defined( __QNX__ ) || ( defined( sun ) && OSVERSION <= 5 ) || \ defined( __WINDOWS__ ) #ifndef in_addr_t #define in_addr_t u_long #define in_port_t u_short #endif /* in_addr_t */ #endif /* Older Unixen without in_*_t's */ /* Some systems use int for size parameters to socket functions and some use size_t (and just to be difficult some use socklen_t, which we use if we can get it). The following is required to distinguish the different ones to avoid compiler warnings on systems that insist on having it one particular way */ #if defined( socklen_t ) || defined( __socklen_t_defined ) #define SIZE_TYPE socklen_t #elif defined( __APPLE__ ) || defined( __BEOS__ ) || defined( _CRAY ) || \ defined( __WINDOWS__ ) #define SIZE_TYPE int #else #define SIZE_TYPE size_t #endif /* Different size types */ /* The Bind namespace (via nameser.h) was cleaned up between the old (widely- used) Bind4 API and the newer (little-used) Bind8/9 one. In order to handle both, we use the newer definitions, but map them back to the Bind4 forms if required. The only thing this doesn't give us is the HEADER struct, which seems to have no equivalent in Bind8/9 */ #ifndef NS_PACKETSZ #define NS_PACKETSZ PACKETSZ #define NS_HFIXEDSZ HFIXEDSZ #define NS_RRFIXEDSZ RRFIXEDSZ #define NS_QFIXEDSZ QFIXEDSZ #endif /* Bind8 names */ /* Older versions of QNX don't define HFIXEDSZ either */ #if defined( __QNX__ ) && ( OSVERSION <= 4 ) #define HFIXEDSZ 12 #endif /* QNX 4.x */ /* Values defined in some environments but not in others. MSG_NOSIGNAL is used to avoid SIGPIPEs on writes if the other side closes the connection, if it's not implemented in this environment we just clear the flag */ #ifndef SHUT_WR #define SHUT_WR 1 #endif /* SHUT_WR */ #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif /* MSG_NOSIGNAL */ /* For some connections that involve long-running sessions we need to be able to gracefully recover from local errors such as an interrupted system call, and remote errors such as the remote process or host crashing and restarting, which we can do by closing and re-opening the connection. The various situations are: Local error: Retry the call on EAGAIN or EINTR Process crashes and restarts: Write: Remote host sends a RST in response to an attempt to continue a TCP session that it doesn't remember, which is reported locally as the dreaded (if you ssh or NNTP to remote hosts a lot) connection reset by peer error. Read: Remote host sends a FIN, we read 0 bytes. Network problem: Write: Data is re-sent, if a read is pending it returns ETIMEDOUT, otherwise write returns EPIPE or SIGPIPE (although we try and avoid the latter using MSG_NOSIGNAL). Some implementations may also return ENETUNREACH or EHOSTUNREACH if they receive the right ICMP information. Read: See above, without the write sematics. Host crashes and restarts: Write: Looks like a network outage until the host is restarted, then gets an EPIPE/SIGPIPE. Read: As for write, but gets a ECONNRESET. The following macros check for various non-fatal/recoverable error conditions, in the future we may want to address some of the others listed above as well. A restartable error is a local error for which we can retry the call, a recoverable error is a remote error for which we would need to re-establish the connection. Note that any version of Winsock newer than the 16-bit ones shouldn't give us an EINPROGRESS, however some early stacks would still give this on occasions such as when another thread was doing (blocking) name resolution, and even with the very latest versions this is still something that can cause problems for other threads */ #ifdef __WINDOWS__ #define isRecoverableError( status ) ( ( status ) == WSAECONNRESET ) #define isRestartableError() ( WSAGetLastError() == WSAEWOULDBLOCK || \ WSAGetLastError() == WSAEINPROGRESS ) #define isTimeoutError() ( WSAGetLastError() == WSAETIMEDOUT ) #else #define isRecoverableError( status ) ( ( status ) == ECONNRESET ) #define isRestartableError() ( errno == EINTR || errno == EAGAIN ) #define isTimeoutError() ( errno == ETIMEDOUT ) #endif /* OS-specific status codes */ /**************************************************************************** * * * Resolver Defines * * * ****************************************************************************/ /* BeOS with the BONE network stack has just enough IPv6 defines present to be awkward, so we temporarily re-enable IPv6 and then use a BONE-specific subset of IPv6 defines further on */ #if defined( __BEOS__ ) && defined( BONE_VERSION ) #define IPv6 #endif /* BeOS with BONE */ /* IPv6 emulation functions used to provide a single consistent interface */ #ifndef IPv6 /* The addrinfo struct used by getaddrinfo() */ struct addrinfo { int ai_flags; /* AI_PASSIVE, NI_NUMERICHOST */ int ai_family; /* PF_INET */ int ai_socktype; /* SOCK_STREAM */ int ai_protocol; /* IPPROTO_TCP */ size_t ai_addrlen; /* Length of ai_addr */ char *ai_canonname; /* CNAME for nodename */ struct sockaddr *ai_addr; /* IPv4 or IPv6 sockaddr */ struct addrinfo *ai_next; /* Next addrinfo structure list */ }; /* The generic sockaddr struct used to reserve storage for protocol- specific sockaddr structs. This isn't quite right but since all we're using it for is to reserve storage (we never actually look inside it) it's OK to use here */ typedef char SOCKADDR_STORAGE[ 128 ]; /* getaddrinfo() flags and values */ #define AI_PASSIVE 0x1 /* Flag for hints are for getaddrinfo() */ /* getnameinfo() flags and values. We have to use slightly different values for these under Windows because Windows uses different values for these than anyone else, and even if we're not on an explicitly IPv6-enabled system we could still end up dynamically pulling in the required libraries, so we need to ensure that we're using the same flag values that Windows does */ #ifdef __WINDOWS__ #define NI_NUMERICHOST 0x2 /* Return numeric form of host addr.*/ #define NI_NUMERICSERV 0x8 /* Return numeric form of host port */ #else #define NI_NUMERICHOST 0x1 /* Return numeric form of host addr.*/ #define NI_NUMERICSERV 0x2 /* Return numeric form of host port */ #endif /* __WINDOWS__ */ /* If there's no getaddrinfo() available and we're not using dynamic linking, use an emulation of the function */ #ifndef __WINDOWS__ #define getaddrinfo my_getaddrinfo #define freeaddrinfo my_freeaddrinfo #define getnameinfo my_getnameinfo static int my_getaddrinfo( const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res ); static void my_freeaddrinfo( struct addrinfo *ai ); static int my_getnameinfo( const struct sockaddr *sa, SIZE_TYPE salen, char *node, SIZE_TYPE nodelen, char *service, SIZE_TYPE servicelen, int flags ); /* Windows uses the Pascal calling convention for these functions, we hide this behind a define that becomes a no-op on non-Windows systems */ #define SOCKET_API #endif /* __WINDOWS__ */ #endif /* IPv6 */ /* A subset of the above for BeOS with the BONE network stack. See the full IPv6 version above for descriptions of the entries */ #if defined( __BEOS__ ) && defined( BONE_VERSION ) #undef IPv6 /* We really don't do IPv6 */ typedef char SOCKADDR_STORAGE[ 128 ]; #define getaddrinfo my_getaddrinfo #define freeaddrinfo my_freeaddrinfo #define getnameinfo my_getnameinfo static int my_getaddrinfo( const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res ); static void my_freeaddrinfo( struct addrinfo *ai ); static int my_getnameinfo( const struct sockaddr *sa, SIZE_TYPE salen, char *node, SIZE_TYPE nodelen, char *service, SIZE_TYPE servicelen, int flags ); #endif /* BeOS with BONE */ /* Values defined in some environments but not in others. T_SRV and NS_SRVFIXEDSZ are used for DNS SRV lookups. Newer versions of bind use a ns_t_srv enum for T_SRV but since we can't autodetect this via the preprocessor we always define T_SRV ourselves */ #ifndef T_SRV #define T_SRV 33 #endif /* !T_SRV */ #ifndef NS_SRVFIXEDSZ #define NS_SRVFIXEDSZ ( NS_RRFIXEDSZ + 6 ) #endif /* !NS_SRVFIXEDSZ */ #ifndef AI_ADDRCONFIG #define AI_ADDRCONFIG 0 #endif /* !AI_ADDRCONFIG */ #ifndef AI_NUMERICSERV #define AI_NUMERICSERV 0 #endif /* !AI_NUMERICSERV */ /* gethostbyname is a problem function because the standard version is non- thread-safe due to the use of static internal storage to contain the returned host info. Some OSes (Windows, PHUX >= 11.0, OSF/1 >= 4.0, Aches >= 4.3) don't have a problem with this because they use thread local storage, but others either require the use of nonstandard _r variants or simply don't handle it at all. To make it even more entertaining, there are at least three different variations of the _r form: Linux (and glibc systems in general, but not BeOS with BONE): int gethostbyname_r( const char *name, struct hostent *result_buf, char *buf, size_t buflen, struct hostent **result, int *h_errnop); Slowaris >= 2.5.1, IRIX >= 6.5, QNX: struct hostent *gethostbyname_r( const char *name, struct hostent *result, char *buffer, int buflen, int *h_errnop ); OSF/1, Aches (deprecated, see above): int gethostbyname_r( const char *name, struct hostent *hptr, struct hostent_data *hdptr ); To work around this mess, we define macros for thread-safe versions of gethostbyname that can be retargeted to the appropriate function as required */ #if defined( USE_THREADS ) && defined( __GLIBC__ ) && ( __GLIBC__ >= 2 ) && \ ( !defined( __BEOS__ ) || !defined( BONE_VERSION ) ) #define gethostbyname_vars() \ char hostBuf[ 4096 ]; \ struct hostent hostEnt; #define gethostbyname_threadsafe( hostName, hostEntPtr, hostErrno ) \ if( gethostbyname_r( hostName, &hostEnt, hostBuf, 4096, &hostEntPtr, &hostErrno ) < 0 ) \ hostEntPtr = NULL #elif defined( USE_THREADS ) && \ ( ( defined( sun ) && OSVERSION > 4 ) || \ ( defined( __sgi ) && OSVERSION >= 6 ) || defined( __QNX__ ) ) #define gethostbyname_vars() \ char hostBuf[ 4096 ]; \ struct hostent hostEnt; #define gethostbyname_threadsafe( hostName, hostEntPtr, hostErrno ) \ hostEntPtr = gethostbyname_r( hostName, &hostEnt, hostBuf, 4096, &hostErrno ) #else #define gethostbyname_vars() #define gethostbyname_threadsafe( hostName, hostEntPtr, hostErrno ) \ hostEntPtr = gethostbyname( hostName ); \ hostErrno = h_errno; #endif /* Various gethostbyname variants */ /**************************************************************************** * * * Non-blocking I/O Defines * * * ****************************************************************************/ /* The traditional way to set a descriptor to nonblocking mode was an ioctl with FIONBIO, however Posix prefers the O_NONBLOCK flag for fcntl so we use this if it's available. Unfortunately if we haven't got the fcntl() interface available there's no way to determine whether a socket is non-blocking or not, which is particularly troublesome for Windows where we need to ensure that the socket is blocking in order to avoid Winsock bugs with nonblocking sockets. Although WSAIoctl() would appear to provide an interface for obtaining the nonblocking status, it doesn't provide any more functionality than ioctlsocket(), returning an error if we try and read the FIONBIO value. If we're just using this as a basic valid-socket check we could also use ( GetFileType( ( HANDLE ) stream->netSocket ) == FILE_TYPE_PIPE ) ? 0 : \ WSAEBADF to check that it's a socket, but there's a bug under all Win9x versions for which GetFileType() on a socket returns FILE_TYPE_UNKNOWN, so we can't reliably detect a socket with this. In any case though ioctlsocket() will return WSAENOTSOCK if it's not a socket, so this is covered by the default handling anyway. The best that we can do in this case is to force the socket to be blocking, which somewhat voids the guarantee that we leave the socket as we found it, but OTOH if we've been passed an invalid socket the caller will have to abort and fix the problem anyway, so changing the socket state isn't such a big deal. BeOS is even worse, not only is there no way to determine whether a socket is blocking or not, it'll also quite happily perform socket functions like setsockopt() on a file descriptor (for example stdout), so we can't even use this as a check for socket validity as it is under other OSes. Because of this the check socket function will always indicate that something vaguely handle-like is a valid socket. When we get the nonblocking status, if there's an error getting the status we report it as a non-blocking socket, which results in the socket being reported as invalid, the same as if it were a a genuine non- blocking socket */ #if defined( F_GETFL ) && defined( F_SETFL ) && defined( O_NONBLOCK ) #define getSocketNonblockingStatus( socket, value ) \ { \ value = fcntl( socket, F_GETFL, 0 ); \ value = ( isSocketError( value ) || ( value & O_NONBLOCK ) ) ? \ TRUE : FALSE; \ } #define setSocketNonblocking( socket ) \ { \ const int flags = fcntl( socket, F_GETFL, 0 ); \ fcntl( socket, F_SETFL, flags | O_NONBLOCK ); \ } #define setSocketBlocking( socket ) \ { \ const int flags = fcntl( socket, F_GETFL, 0 ); \ fcntl( socket, F_SETFL, flags & ~O_NONBLOCK ); \ } #elif defined( FIONBIO ) #define getSocketNonblockingStatus( socket, value ) \ { \ long nonBlock = FALSE; \ value = ioctlsocket( socket, FIONBIO, &nonBlock ); \ value = isSocketError( value ) ? TRUE : FALSE; \ } #define setSocketNonblocking( socket ) \ { \ long nonBlock = TRUE; \ ioctlsocket( socket, FIONBIO, &nonBlock ); \ } #define setSocketBlocking( socket ) \ { \ long nonBlock = FALSE; \ ioctlsocket( socket, FIONBIO, &nonBlock ); \ } #elif defined( __AMX__ ) || defined( __BEOS__ ) #define getSocketNonblockingStatus( socket, value ) \ { \ int nonBlock = FALSE; \ setsockopt( socket, SOL_SOCKET, SO_NONBLOCK, &nonBlock, sizeof( int ) ); \ } #define setSocketNonblocking( socket ) \ { \ int nonBlock = TRUE; \ setsockopt( socket, SOL_SOCKET, SO_NONBLOCK, &nonBlock, sizeof( int ) ); \ } #define setSocketBlocking( socket ) \ { \ int nonBlock = FALSE; \ setsockopt( socket, SOL_SOCKET, SO_NONBLOCK, &nonBlock, sizeof( int ) ); \ } #elif defined( __SYMBIAN32__ ) /* Symbian OS doesn't support nonblocking I/O */ #define getSocketNonblockingStatus( socket, value ) value = 0 #define setSocketNonblocking( socket ) #define setSocketBlocking( socket ) #else #error Need to create macros to handle nonblocking I/O #endif /* Handling of blocking/nonblocking sockets */ /**************************************************************************** * * * Misc.Functions and Defines * * * ****************************************************************************/ /* DNS dynamic-binding init/shutdown functions */ #ifdef __WINDOWS__ int initDNS( INSTANCE_HANDLE hTCP, INSTANCE_HANDLE hAddr ); void endDNS( INSTANCE_HANDLE hTCP ); #endif /* __WINDOWS__ */ /* Prototypes for functions in net_dns.c */ int getAddressInfo( STREAM *stream, struct addrinfo **addrInfoPtrPtr, const char *name, const int port, const BOOLEAN isServer ); void freeAddressInfo( struct addrinfo *addrInfoPtr ); void getNameInfo( const struct sockaddr *sockAddr, char *address, const int addressMaxLen, int *port ); /* Prototypes for functions in net_tcp.c */ int getSocketError( STREAM *stream, const int status ); int setSocketError( STREAM *stream, const char *errorMessage, const int status, const BOOLEAN isFatal ); int getHostError( STREAM *stream, const int status ); #endif /* USE_TCP */