/*
* winsock.cxx
*
* WINSOCK implementation of Berkley sockets.
*
* 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: winsock.cxx,v $
* Revision 1.71.4.1 2006/01/31 08:09:13 csoutheren
* Backported from CVS head
*
* Revision 1.73 2006/01/31 03:38:27 csoutheren
* Refixed fix for compiler warning
*
* Revision 1.72 2006/01/31 03:23:17 csoutheren
* Fixed compile warning on MSVC 6
*
* Revision 1.71 2005/11/30 12:47:42 csoutheren
* Removed tabs, reformatted some code, and changed tags for Doxygen
*
* Revision 1.70 2005/11/21 11:49:36 shorne
* Changed disableQos to disableGQoS to better reflect what it does
*
* Revision 1.69 2005/09/23 15:30:46 dominance
* more progress to make mingw compile nicely. Thanks goes to Julien Puydt for pointing out to me how to do it properly. ;)
*
* Revision 1.68 2005/09/18 13:01:44 dominance
* fixed pragma warnings when building with gcc.
*
* Revision 1.67 2005/08/08 06:59:39 rjongbloed
* Fixed compiler warning
*
* Revision 1.66 2005/07/13 12:08:09 csoutheren
* Fixed QoS patches to be more consistent with PWLib style and to allow Unix compatibility
*
* Revision 1.65 2005/07/13 11:48:55 csoutheren
* Backported QOS changes from isvo branch
*
* Revision 1.64 2004/10/23 10:45:32 ykiryanov
* Added ifdef _WIN32_WCE for PocketPC 2003 SDK port
*
* Revision 1.63 2004/05/06 11:28:30 rjongbloed
* Changed P_fd_set to use malloc/free isntead of new/delete due to pedantry about [].
*
* Revision 1.62 2004/04/27 09:53:27 rjongbloed
* Fixed ability to break of a PSocket::Select call under linux when a socket
* is closed by another thread.
*
* Revision 1.61 2004/04/03 08:22:22 csoutheren
* Remove pseudo-RTTI and replaced with real RTTI
*
* Revision 1.60 2003/11/12 04:40:58 csoutheren
* Fixed linking problem on systems without QoS or IPV6
*
* Revision 1.59 2003/11/10 00:21:38 dereksmithies
* Stop compiler warnings (unused formal parameters) when P_HAS_QOS is on
*
* Revision 1.58 2003/10/30 11:33:59 rjongbloed
* Added automatic inclusion of Winsock2 library.
*
* Revision 1.57 2003/10/28 23:36:22 csoutheren
* Changed to use ws2_32.lib or wsock32.lib depending on use of QoS
*
* Revision 1.56 2003/10/27 08:01:52 csoutheren
* Removed use of GetAddressByName when using Winsock2
*
* Revision 1.55 2003/10/27 03:29:11 csoutheren
* Added support for QoS
* Thanks to Henry Harrison of AliceStreet
*
* Revision 1.54 2003/09/17 05:45:10 csoutheren
* Removed recursive includes
*
* Revision 1.53 2002/10/29 08:00:16 robertj
* Changed in_addr6 to more universally used in6_addr.
*
* Revision 1.52 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.51 2002/10/17 07:17:43 robertj
* Added ability to increase maximum file handles on a process.
*
* Revision 1.50 2002/10/08 12:41:52 robertj
* Changed for IPv6 support, thanks Sébastien Josset.
*
* Revision 1.49 2002/05/23 09:07:41 robertj
* Further adjustments to compensate for Winsock weirdness on some platforms.
*
* Revision 1.48 2002/05/23 01:54:35 robertj
* Worked around WinSock bug where getsockopt() does not work immediately
* after the select() function returns an exception.
*
* Revision 1.47 2002/05/22 07:22:17 robertj
* Fixed bug in waiting for connect with a timeout not checking for errors via
* the except fdset in the select() call. Would give timeout for all errors.
*
* Revision 1.46 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.45 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.44 2001/09/06 02:30:31 robertj
* Fixed mismatched declarations, thanks Vjacheslav Andrejev
*
* Revision 1.43 2001/03/20 06:57:14 robertj
* os_accept() function changed due to unix changes re unblocking threads.
*
* Revision 1.42 2001/01/24 06:46:45 yurik
* Windows CE port-related changes
*
* Revision 1.41 1998/11/30 04:50:19 robertj
* New directory structure
*
* Revision 1.40 1998/11/14 06:31:15 robertj
* Changed semantics of os_sendto to return TRUE if ANY bytes are sent.
*
* Revision 1.39 1998/09/24 03:31:02 robertj
* Added open software license.
*
* Revision 1.38 1998/08/28 14:09:45 robertj
* Fixed bug in Write() that caused endlesss loops, introduced in previous version.
*
* Revision 1.37 1998/08/21 05:27:31 robertj
* Fixed bug where write streams out to non-stream socket.
*
* Revision 1.36 1998/08/06 00:55:21 robertj
* Fixed conversion of text to IPX address, was swapping nibbles.
*
* Revision 1.35 1998/05/08 11:52:03 robertj
* Added workaround for winsock bug where getpeername() doesn't work immediately after connect().
*
* Revision 1.34 1998/05/07 05:21:04 robertj
* Fixed DNS lookup so only works around bug in old Win95 and not OSR2
*
* Revision 1.33 1998/01/26 01:00:06 robertj
* Added timeout to os_connect().
* Fixed problems with NT version of IsLocalHost().
*
* Revision 1.32 1997/12/18 05:05:27 robertj
* Moved IsLocalHost() to platform dependent code.
*
* Revision 1.31 1997/12/11 10:41:55 robertj
* Added DWORD operator for IP addresses.
*
* Revision 1.30 1997/01/03 04:37:11 robertj
* Fixed '95 problem with send timeouts.
*
* Revision 1.29 1996/12/05 11:51:50 craigs
* Fixed Win95 recvfrom timeout problem
*
* Revision 1.28 1996/11/10 21:04:56 robertj
* Fixed bug in not flushing stream on close of socket.
*
* Revision 1.27 1996/10/31 12:39:30 robertj
* Fixed bug in byte order of port numbers in IPX protocol.
*
* Revision 1.26 1996/10/26 01:43:18 robertj
* Removed translation of IP address to host order DWORD. Is ALWAYS net order.
*
* Revision 1.25 1996/10/08 13:03:09 robertj
* More IPX support.
*
* Revision 1.24 1996/09/14 13:09:47 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.23 1996/08/08 10:06:07 robertj
* Fixed incorrect value in write, causes incorrect output if send is split.
*
* Revision 1.22 1996/07/27 04:03:29 robertj
* Created static version of ConvertOSError().
*
* Revision 1.21 1996/06/01 04:19:34 robertj
* Added flush to PSocket destructor as needs to use Write() at that level.
*
* Revision 1.20 1996/05/15 10:23:08 robertj
* Changed millisecond access functions to get 64 bit integer.
* Added timeout to accept function.
* Added ICMP protocol socket, getting common ancestor to UDP.
*
* Revision 1.19 1996/04/29 12:22:26 robertj
* Fixed detection of infinite timeout.
*
* Revision 1.18 1996/04/17 12:09:52 robertj
* Fixed bug in detecting infinte timeout.
*
* Revision 1.17 1996/04/12 09:45:06 robertj
* Rewrite of PSocket::Read() to avoid "Connection Reset" errors caused by SO_RCVTIMEO
*
* Revision 1.17 1996/04/10 12:15:11 robertj
* Rewrite of PSocket::Read() to avoid "Connection Reset" errors caused by SO_RCVTIMEO.
*
* Revision 1.16 1996/04/05 01:42:28 robertj
* Assured PSocket::Write always writes the number of bytes specified.
*
* Revision 1.15 1996/03/31 09:11:06 robertj
* Fixed major performance problem in timeout read/write to sockets.
*
* Revision 1.14 1996/03/10 13:16:25 robertj
* Fixed ioctl of closed socket.
*
* Revision 1.13 1996/03/04 12:41:02 robertj
* Fixed bug in leaving socket in non-blocking mode.
* Changed _Close to os_close to be consistent.
*
* Revision 1.12 1996/02/25 11:23:40 robertj
* Fixed bug in Read for when a timeout occurs on select, not returning error code.
*
* Revision 1.11 1996/02/25 03:13:12 robertj
* Moved some socket functions to platform dependent code.
*
* Revision 1.10 1996/02/19 13:52:39 robertj
* Added SO_LINGER option to socket to stop data loss on close.
* Fixed error reporting for winsock classes.
*
* Revision 1.9 1996/02/15 14:53:36 robertj
* Added Select() function to PSocket.
*
* Revision 1.8 1996/01/23 13:25:48 robertj
* Moved Accept from platform independent code.
*
* Revision 1.7 1996/01/02 12:57:17 robertj
* Unix compatibility.
*
* Revision 1.6 1995/12/10 12:06:00 robertj
* Numerous fixes for sockets.
*
* Revision 1.5 1995/06/17 00:59:49 robertj
* Fixed bug with stream being flushed on read/write.
*
* Revision 1.4 1995/06/04 12:49:51 robertj
* Fixed bugs in socket read and write function return status.
* Fixed bug in socket close setting object state to "closed".
*
* Revision 1.3 1995/03/12 05:00:10 robertj
* Re-organisation of DOS/WIN16 and WIN32 platforms to maximise common code.
* Used built-in equate for WIN32 API (_WIN32).
*
* Revision 1.2 1995/01/03 09:43:27 robertj
* Moved out of band stuff to common.
*
* Revision 1.1 1994/10/30 12:06:56 robertj
* Initial revision
*/
#include <ptlib.h>
#include <ptlib/sockets.h>
#include <nspapi.h>
#include <svcguid.h>
#include <wsipx.h>
#ifdef _MSC_VER
#include <wsnwlink.h>
#else
#define IPX_PTYPE 0x4000
#define NS_DEFAULT 0
#define SVCID_NETWARE(_SapId) {(0x000B << 16)|(_SapId),0,0,{0xC0,0,0,0,0,0,0,0x46}}
#define SVCID_FILE_SERVER SVCID_NETWARE(0x4)
#endif
#if defined(P_WINSOCK2_LIBRARY)
#ifdef _MSC_VER
#pragma comment(lib, P_WINSOCK2_LIBRARY)
#endif
#else
#ifndef _WIN32_WCE
#ifdef _MSC_VER
#pragma comment(lib, "wsock32.lib")
#endif
#endif // !_WIN32_WCE
#endif
//////////////////////////////////////////////////////////////////////////////
// PWinSock
PWinSock::PWinSock()
{
WSADATA winsock;
#if 0 // old WinSock version check
PAssert(WSAStartup(0x101, &winsock) == 0, POperatingSystemError);
PAssert(LOBYTE(winsock.wVersion) == 1 &&
HIBYTE(winsock.wVersion) == 1, POperatingSystemError);
#endif
// ensure we support QoS
PAssert(WSAStartup(0x0202, &winsock) == 0, POperatingSystemError);
PAssert(LOBYTE(winsock.wVersion) >= 1 &&
HIBYTE(winsock.wVersion) >= 1, POperatingSystemError);
}
PWinSock::~PWinSock()
{
WSACleanup();
}
BOOL PWinSock::OpenSocket()
{
return FALSE;
}
const char * PWinSock::GetProtocolName() const
{
return NULL;
}
//////////////////////////////////////////////////////////////////////////////
// P_fd_set
void P_fd_set::Construct()
{
max_fd = UINT_MAX;
set = (fd_set *)malloc(sizeof(fd_set));
}
void P_fd_set::Zero()
{
if (PAssertNULL(set) != NULL)
FD_ZERO(set);
}
//////////////////////////////////////////////////////////////////////////////
// PSocket
PSocket::~PSocket()
{
Close();
}
BOOL PSocket::Read(void * buf, PINDEX len)
{
flush();
lastReadCount = 0;
if (len == 0)
return SetErrorValues(BadParameter, EINVAL, LastReadError);
os_recvfrom((char *)buf, len, 0, NULL, NULL);
return lastReadCount > 0;
}
BOOL PSocket::Write(const void * buf, PINDEX len)
{
flush();
return os_sendto(buf, len, 0, NULL, 0) && lastWriteCount >= len;
}
BOOL PSocket::Close()
{
if (!IsOpen())
return FALSE;
flush();
return ConvertOSError(os_close());
}
int PSocket::os_close()
{
int err = closesocket(os_handle);
os_handle = -1;
return err;
}
int PSocket::os_socket(int af, int type, int proto)
{
return ::socket(af, type, proto);
}
BOOL PSocket::os_connect(struct sockaddr * addr, PINDEX size)
{
if (readTimeout == PMaxTimeInterval)
return ConvertOSError(::connect(os_handle, addr, size));
DWORD fionbio = 1;
if (!ConvertOSError(::ioctlsocket(os_handle, FIONBIO, &fionbio)))
return FALSE;
fionbio = 0;
if (::connect(os_handle, addr, size) != SOCKET_ERROR)
return ConvertOSError(::ioctlsocket(os_handle, FIONBIO, &fionbio));
DWORD err = GetLastError();
if (err != WSAEWOULDBLOCK) {
::ioctlsocket(os_handle, FIONBIO, &fionbio);
SetLastError(err);
return ConvertOSError(-1);
}
P_fd_set writefds = os_handle;
P_fd_set exceptfds = os_handle;
P_timeval tv;
/* To avoid some strange behaviour on various windows platforms, do a zero
timeout select first to pick up errors. Then do real timeout. */
int selerr = ::select(1, NULL, writefds, exceptfds, tv);
if (selerr == 0) {
writefds = os_handle;
exceptfds = os_handle;
tv = readTimeout;
selerr = ::select(1, NULL, writefds, exceptfds, tv);
}
switch (selerr) {
case 1 :
if (writefds.IsPresent(os_handle)) {
// The following is to avoid a bug in Win32 sockets. The getpeername() function doesn't
// work for some period of time after a connect, saying it is not connected yet!
for (PINDEX failsafe = 0; failsafe < 1000; failsafe++) {
sockaddr_in address;
int sz = sizeof(address);
if (::getpeername(os_handle, (struct sockaddr *)&address, &sz) == 0) {
if (address.sin_port != 0)
break;
}
::Sleep(0);
}
err = 0;
}
else {
// The following is to avoid a bug in Win32 sockets. The getsockopt() function
// doesn't work for some period of time after a connect, saying no error!
for (PINDEX failsafe = 0; failsafe < 1000; failsafe++) {
int sz = sizeof(err);
if (::getsockopt(os_handle, SOL_SOCKET, SO_ERROR, (char *)&err, &sz) == 0) {
if (err != 0)
break;
}
::Sleep(0);
}
if (err == 0)
err = WSAEFAULT; // Need to have something!
}
break;
case 0 :
err = WSAETIMEDOUT;
break;
default :
err = GetLastError();
}
if (::ioctlsocket(os_handle, FIONBIO, &fionbio) == SOCKET_ERROR) {
if (err == 0)
err = GetLastError();
}
SetLastError(err);
return ConvertOSError(err == 0 ? 0 : SOCKET_ERROR);
}
BOOL PSocket::os_accept(PSocket & listener, struct sockaddr * addr, int * size)
{
if (listener.GetReadTimeout() != PMaxTimeInterval) {
P_fd_set readfds = listener.GetHandle();
P_timeval tv = listener.GetReadTimeout();
switch (select(0, readfds, NULL, NULL, tv)) {
case 1 :
break;
case 0 :
SetLastError(WSAETIMEDOUT);
// Then return -1
default :
return ConvertOSError(-1);
}
}
return ConvertOSError(os_handle = ::accept(listener.GetHandle(), addr, size));
}
BOOL PSocket::os_recvfrom(void * buf,
PINDEX len,
int flags,
struct sockaddr * from,
PINDEX * fromlen)
{
lastReadCount = 0;
if (readTimeout != PMaxTimeInterval) {
DWORD available;
if (!ConvertOSError(ioctlsocket(os_handle, FIONREAD, &available), LastReadError))
return FALSE;
if (available == 0) {
P_fd_set readfds = os_handle;
P_timeval tv = readTimeout;
int selval = ::select(0, readfds, NULL, NULL, tv);
if (!ConvertOSError(selval, LastReadError))
return FALSE;
if (selval == 0)
return SetErrorValues(Timeout, EAGAIN, LastReadError);
if (!ConvertOSError(ioctlsocket(os_handle, FIONREAD, &available), LastReadError))
return FALSE;
}
if (available > 0 && len > (PINDEX)available)
len = available;
}
int recvResult = ::recvfrom(os_handle, (char *)buf, len, flags, from, fromlen);
if (!ConvertOSError(recvResult, LastReadError))
return FALSE;
lastReadCount = recvResult;
return TRUE;
}
BOOL PSocket::os_sendto(const void * buf,
PINDEX len,
int flags,
struct sockaddr * to,
PINDEX tolen)
{
lastWriteCount = 0;
if (writeTimeout != PMaxTimeInterval) {
P_fd_set writefds = os_handle;
P_timeval tv = writeTimeout;
int selval = ::select(0, NULL, writefds, NULL, tv);
if (selval < 0)
return FALSE;
if (selval == 0) {
#ifndef _WIN32_WCE
errno = EAGAIN;
#else
SetLastError(EAGAIN);
#endif
return FALSE;
}
}
int sendResult = ::sendto(os_handle, (const char *)buf, len, flags, to, tolen);
if (!ConvertOSError(sendResult, LastWriteError))
return FALSE;
if (sendResult == 0)
return FALSE;
lastWriteCount = sendResult;
return TRUE;
}
PChannel::Errors PSocket::Select(SelectList & read,
SelectList & write,
SelectList & except,
const PTimeInterval & timeout)
{
PINDEX i;
P_fd_set readfds;
for (i = 0; i < read.GetSize(); i++) {
if (!read[i].IsOpen())
return NotOpen;
readfds += read[i].GetHandle();
}
P_fd_set writefds;
for (i = 0; i < write.GetSize(); i++) {
if (!write[i].IsOpen())
return NotOpen;
writefds += write[i].GetHandle();
}
P_fd_set exceptfds;
for (i = 0; i < except.GetSize(); i++) {
if (!except[i].IsOpen())
return NotOpen;
exceptfds += except[i].GetHandle();
}
P_timeval tval = timeout;
int retval = select(INT_MAX, readfds, writefds, exceptfds, tval);
Errors lastError;
int osError;
if (!ConvertOSError(retval, lastError, osError))
return lastError;
if (retval > 0) {
for (i = 0; i < read.GetSize(); i++) {
int h = read[i].GetHandle();
if (h < 0)
return Interrupted;
if (!readfds.IsPresent(h))
read.RemoveAt(i--);
}
for (i = 0; i < write.GetSize(); i++) {
int h = write[i].GetHandle();
if (h < 0)
return Interrupted;
if (!writefds.IsPresent(h))
write.RemoveAt(i--);
}
for (i = 0; i < except.GetSize(); i++) {
int h = except[i].GetHandle();
if (h < 0)
return Interrupted;
if (!exceptfds.IsPresent(h))
except.RemoveAt(i--);
}
}
else {
read.RemoveAll();
write.RemoveAll();
except.RemoveAll();
}
return NoError;
}
BOOL PSocket::ConvertOSError(int status, ErrorGroup group)
{
Errors lastError;
int osError;
BOOL ok = ConvertOSError(status, lastError, osError);
SetErrorValues(lastError, osError, group);
return ok;
}
BOOL PSocket::ConvertOSError(int status, Errors & lastError, int & osError)
{
if (status >= 0) {
lastError = NoError;
osError = 0;
return TRUE;
}
#ifdef _WIN32
SetLastError(WSAGetLastError());
return PChannel::ConvertOSError(-2, lastError, osError);
#else
osError = WSAGetLastError();
switch (osError) {
case 0 :
lastError = NoError;
return TRUE;
case WSAEWOULDBLOCK :
lastError = Timeout;
break;
default :
osError |= PWIN32ErrorFlag;
lastError = Miscellaneous;
}
return FALSE;
#endif
}
//////////////////////////////////////////////////////////////////////////////
// PIPSocket::Address
PIPSocket::Address::Address(BYTE b1, BYTE b2, BYTE b3, BYTE b4)
{
version = 4;
v.four.S_un.S_un_b.s_b1 = b1;
v.four.S_un.S_un_b.s_b2 = b2;
v.four.S_un.S_un_b.s_b3 = b3;
v.four.S_un.S_un_b.s_b4 = b4;
}
PIPSocket::Address::Address(DWORD dw)
{
operator=(dw);
}
PIPSocket::Address & PIPSocket::Address::operator=(DWORD dw)
{
if (dw == 0) {
version = 0;
memset(&v, 0, sizeof(v));
}
else {
version = 4;
v.four.S_un.S_addr = dw;
}
return *this;
}
PIPSocket::Address::operator DWORD() const
{
return version != 4 ? 0 : v.four.S_un.S_addr;
}
BYTE PIPSocket::Address::Byte1() const
{
return v.four.S_un.S_un_b.s_b1;
}
BYTE PIPSocket::Address::Byte2() const
{
return v.four.S_un.S_un_b.s_b2;
}
BYTE PIPSocket::Address::Byte3() const
{
return v.four.S_un.S_un_b.s_b3;
}
BYTE PIPSocket::Address::Byte4() const
{
return v.four.S_un.S_un_b.s_b4;
}
//////////////////////////////////////////////////////////////////////////////
// PIPSocket
BOOL P_IsOldWin95()
{
static int state = -1;
if (state < 0) {
state = 1;
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof(info);
if (GetVersionEx(&info)) {
state = 0;
if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && info.dwBuildNumber < 1000)
state = 1;
}
}
return state != 0;
}
BOOL PIPSocket::IsLocalHost(const PString & hostname)
{
if (hostname.IsEmpty())
return TRUE;
if (hostname *= "localhost")
return TRUE;
// lookup the host address using inet_addr, assuming it is a "." address
PIPSocket::Address addr = hostname;
if (addr.IsLoopback()) // Is 127.0.0.1 or ::1
return TRUE;
if (addr == 0) {
if (!GetHostAddress(hostname, addr))
return FALSE;
}
// Seb: Should check that it's really IPv4 aware.
struct hostent * host_info = ::gethostbyname(GetHostName());
if (P_IsOldWin95())
return addr == *(struct in_addr *)host_info->h_addr_list[0];
for (PINDEX i = 0; host_info->h_addr_list[i] != NULL; i++) {
#if P_HAS_IPV6
if (host_info->h_length == 16) {
if (addr == *(struct in6_addr *)host_info->h_addr_list[i])
return TRUE;
}
else
#endif
if (addr == *(struct in_addr *)host_info->h_addr_list[i])
return TRUE;
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////////////
// PUDPSocket
BOOL PUDPSocket::disableGQoS = TRUE;
void PUDPSocket::EnableGQoS()
{
disableGQoS = FALSE;
}
#if P_HAS_QOS
BOOL PUDPSocket::SupportQoS(const PIPSocket::Address & address)
{
if (disableGQoS)
return FALSE;
if (!address.IsValid())
return FALSE;
// Check to See if OS supportive
OSVERSIONINFO versInfo;
ZeroMemory(&versInfo,sizeof(OSVERSIONINFO));
versInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (!(GetVersionEx(&versInfo)))
return FALSE;
else
{
if (versInfo.dwMajorVersion < 5)
return FALSE; // Not Supported in Windows
if (versInfo.dwMajorVersion == 5 &&
versInfo.dwMinorVersion == 0)
return FALSE; //Windows 2000 does not always support QOS_DESTADDR
}
// Need to put in a check to see if the NIC has 802.1p packet priority support
// This Requires access to the NIC driver and requires Windows DDK. To Be Done Sometime...
// Get the name of the required NIC to check whether it supports 802.1p
PString NICname = PIPSocket::GetInterface(address);
// For Now Assume it can.
return TRUE;
}
#else
BOOL PUDPSocket::SupportQoS(const PIPSocket::Address &)
{
return FALSE;
}
#endif // P_HAS_QOS
#if P_HAS_QOS
#ifndef _WIN32_WCE
PWinQoS::~PWinQoS()
{
delete sa;
}
PWinQoS::PWinQoS(PQoS & pqos, struct sockaddr * to, char * inBuf, DWORD & bufLen)
{
QOS * qos = (QOS *)inBuf;
if (pqos.GetTokenRate() == QOS_NOT_SPECIFIED)
qos->SendingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT;
else
qos->SendingFlowspec.ServiceType = pqos.GetServiceType();
qos->SendingFlowspec.TokenRate = pqos.GetTokenRate();
qos->SendingFlowspec.TokenBucketSize = pqos.GetTokenBucketSize();
qos->SendingFlowspec.PeakBandwidth = pqos.GetPeakBandwidth();
qos->SendingFlowspec.Latency = QOS_NOT_SPECIFIED;
qos->SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
qos->SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
qos->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
qos->ReceivingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT|SERVICE_NO_QOS_SIGNALING;
qos->ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
qos->ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
qos->ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
qos->ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED;
qos->ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
qos->ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
qos->ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
sa = new sockaddr;
*sa = *to;
QOS_DESTADDR qosdestaddr;
qosdestaddr.ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR;
qosdestaddr.ObjectHdr.ObjectLength = sizeof(qosdestaddr);
qosdestaddr.SocketAddress = sa;
qosdestaddr.SocketAddressLength = sizeof(*sa);
qos->ProviderSpecific.len = sizeof(qosdestaddr);
qos->ProviderSpecific.buf = inBuf + sizeof(*qos);
memcpy(inBuf+sizeof(*qos),&qosdestaddr,sizeof(qosdestaddr));
bufLen = sizeof(*qos)+sizeof(qosdestaddr);
}
#endif // _WIN32_WCE
#endif // P_HAS_QOS
#ifndef _WIN32_WCE
//////////////////////////////////////////////////////////////////////////////
// PIPXSocket
PIPXSocket::Address::Address()
{
memset(this, 0, sizeof(*this));
}
PIPXSocket::Address::Address(const Address & addr)
{
memcpy(this, &addr, sizeof(*this));
}
PIPXSocket::Address::Address(const PString & str)
{
PINDEX colon = str.Find(':');
if (colon == P_MAX_INDEX)
colon = 0;
else {
DWORD netnum = 0;
for (PINDEX i = 0; i < colon; i++) {
int c = str[i];
if (isdigit(c))
netnum = (netnum << 4) + c - '0';
else if (isxdigit(c))
netnum = (netnum << 4) + toupper(c) - 'A' + 10;
else {
memset(this, 0, sizeof(*this));
return;
}
}
network.dw = ntohl(netnum);
}
memset(node, 0, sizeof(node));
int shift = 0;
PINDEX byte = 5;
PINDEX pos = str.GetLength();
while (--pos > colon) {
int c = str[pos];
if (c != '-') {
if (isdigit(c))
node[byte] |= (c - '0') << shift;
else if (isxdigit(c))
node[byte] |= (toupper(c) - 'A' + 10) << shift;
else {
memset(this, 0, sizeof(*this));
return;
}
if (shift == 0)
shift = 4;
else {
shift = 0;
byte--;
}
}
}
}
PIPXSocket::Address::Address(DWORD netNum, const char * nodeNum)
{
network.dw = netNum;
memcpy(node, nodeNum, sizeof(node));
}
PIPXSocket::Address & PIPXSocket::Address::operator=(const Address & addr)
{
memcpy(this, &addr, sizeof(*this));
return *this;
}
PIPXSocket::Address::operator PString() const
{
return psprintf("%02X%02X%02X%02X:%02X%02X%02X%02X%02X%02X",
network.b.b1, network.b.b2, network.b.b3, network.b.b4,
node[0], node[1], node[2], node[3], node[4], node[5]);
}
BOOL PIPXSocket::Address::IsValid() const
{
static Address empty;
return memcmp(this, &empty, sizeof(empty)) != 0;
}
PIPXSocket::PIPXSocket(WORD newPort)
{
SetPort(newPort);
}
PString PIPXSocket::GetName() const
{
Address addr;
if (((PIPXSocket*)this)->GetPeerAddress(addr))
return addr;
else
return PString();
}
BOOL PIPXSocket::OpenSocket()
{
return ConvertOSError(os_handle = os_socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX));
}
const char * PIPXSocket::GetProtocolName() const
{
return "ipx";
}
BOOL PIPXSocket::SetPacketType(int type)
{
return ConvertOSError(::setsockopt(os_handle,
NSPROTO_IPX, IPX_PTYPE, (char *)&type, sizeof(type)));
}
int PIPXSocket::GetPacketType()
{
int value;
int valSize = sizeof(value);
if (ConvertOSError(::getsockopt(os_handle,
NSPROTO_IPX, IPX_PTYPE, (char *)&value, &valSize)))
return value;
return -1;
}
PString PIPXSocket::GetHostName(const Address & addr)
{
return addr;
}
BOOL PIPXSocket::GetHostAddress(Address &)
{
return FALSE;
}
static void AssignAddress(sockaddr_ipx & sip, const PIPXSocket::Address & addr)
{
memcpy(sip.sa_netnum, &addr.network, sizeof(sip.sa_netnum));
memcpy(sip.sa_nodenum, addr.node, sizeof(sip.sa_nodenum));
}
static void AssignAddress(PIPXSocket::Address & addr, const sockaddr_ipx & sip)
{
memcpy(&addr.network, sip.sa_netnum, sizeof(addr.network));
memcpy(addr.node, sip.sa_nodenum, sizeof(addr.node));
}
#ifdef P_HAS_QOS
BOOL PIPXSocket::GetHostAddress(const PString & /*hostname*/, Address & /*addr*/)
{
return FALSE;
}
#else
BOOL PIPXSocket::GetHostAddress(const PString & hostname, Address & addr)
{
addr = hostname;
if (addr.IsValid())
return TRUE;
static GUID netware_file_server = SVCID_FILE_SERVER;
CSADDR_INFO addr_info[10];
DWORD buffer_length = sizeof(addr_info);
int num = GetAddressByName(NS_DEFAULT,
&netware_file_server,
(LPTSTR)(const char *)hostname,
NULL,
0,
NULL,
addr_info,
&buffer_length,
NULL,
NULL
);
if (num <= 0)
return FALSE;
AssignAddress(addr, *(sockaddr_ipx *)addr_info[0].RemoteAddr.lpSockaddr);
return TRUE;
}
#endif
BOOL PIPXSocket::GetLocalAddress(Address & addr)
{
sockaddr_ipx sip;
int size = sizeof(sip);
if (!ConvertOSError(::getsockname(os_handle, (struct sockaddr *)&sip, &size)))
return FALSE;
AssignAddress(addr, sip);
return TRUE;
}
BOOL PIPXSocket::GetLocalAddress(Address & addr, WORD & portNum)
{
sockaddr_ipx sip;
int size = sizeof(sip);
if (!ConvertOSError(::getsockname(os_handle, (struct sockaddr *)&sip, &size)))
return FALSE;
AssignAddress(addr, sip);
portNum = Net2Host(sip.sa_socket);
return TRUE;
}
BOOL PIPXSocket::GetPeerAddress(Address & addr)
{
sockaddr_ipx sip;
int size = sizeof(sip);
if (!ConvertOSError(::getpeername(os_handle, (struct sockaddr *)&sip, &size)))
return FALSE;
AssignAddress(addr, sip);
return TRUE;
}
BOOL PIPXSocket::GetPeerAddress(Address & addr, WORD & portNum)
{
sockaddr_ipx sip;
int size = sizeof(sip);
if (!ConvertOSError(::getpeername(os_handle, (struct sockaddr *)&sip, &size)))
return FALSE;
AssignAddress(addr, sip);
portNum = Net2Host(sip.sa_socket);
return TRUE;
}
BOOL PIPXSocket::Connect(const PString & host)
{
Address addr;
if (GetHostAddress(host, addr))
return Connect(addr);
return FALSE;
}
BOOL PIPXSocket::Connect(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");
// attempt to create a socket
if (!OpenSocket())
return FALSE;
// attempt to lookup the host name
sockaddr_ipx sip;
memset(&sip, 0, sizeof(sip));
sip.sa_family = AF_IPX;
AssignAddress(sip, addr);
sip.sa_socket = Host2Net(port); // set the port
if (os_connect((struct sockaddr *)&sip, sizeof(sip)))
return TRUE;
os_close();
return FALSE;
}
BOOL PIPXSocket::Listen(unsigned, WORD newPort, Reusability reuse)
{
// make sure we have a port
if (newPort != 0)
port = newPort;
// close the port if it is already open
if (!IsOpen()) {
// attempt to create a socket
if (!OpenSocket())
return FALSE;
}
// attempt to listen
if (SetOption(SO_REUSEADDR, reuse == CanReuseAddress ? 1 : 0)) {
// attempt to listen
sockaddr_ipx sip;
memset(&sip, 0, sizeof(sip));
sip.sa_family = AF_IPX;
sip.sa_socket = Host2Net(port); // set the port
if (ConvertOSError(::bind(os_handle, (struct sockaddr*)&sip, sizeof(sip)))) {
int size = sizeof(sip);
if (ConvertOSError(::getsockname(os_handle, (struct sockaddr*)&sip, &size))) {
port = Net2Host(sip.sa_socket);
return TRUE;
}
}
}
os_close();
return FALSE;
}
BOOL PIPXSocket::ReadFrom(void * buf, PINDEX len, Address & addr, WORD & port)
{
lastReadCount = 0;
sockaddr_ipx sip;
int addrLen = sizeof(sip);
if (os_recvfrom(buf, len, 0, (struct sockaddr *)&sip, &addrLen)) {
AssignAddress(addr, sip);
port = Net2Host(sip.sa_socket);
}
return lastReadCount > 0;
}
BOOL PIPXSocket::WriteTo(const void * buf, PINDEX len, const Address & addr, WORD port)
{
lastWriteCount = 0;
sockaddr_ipx sip;
sip.sa_family = AF_IPX;
AssignAddress(sip, addr);
sip.sa_socket = Host2Net(port);
return os_sendto(buf, len, 0, (struct sockaddr *)&sip, sizeof(sip));
}
//////////////////////////////////////////////////////////////////////////////
// PSPXSocket
PSPXSocket::PSPXSocket(WORD port)
: PIPXSocket(port)
{
}
BOOL PSPXSocket::OpenSocket()
{
return ConvertOSError(os_handle = os_socket(AF_IPX, SOCK_STREAM, NSPROTO_SPX));
}
const char * PSPXSocket::GetProtocolName() const
{
return "spx";
}
BOOL PSPXSocket::Listen(unsigned queueSize, WORD newPort, Reusability reuse)
{
if (PIPXSocket::Listen(queueSize, newPort, reuse) &&
ConvertOSError(::listen(os_handle, queueSize)))
return TRUE;
os_close();
return FALSE;
}
BOOL PSPXSocket::Accept(PSocket & socket)
{
PAssert(PIsDescendant(&socket, PIPXSocket), "Invalid listener socket");
sockaddr_ipx sip;
sip.sa_family = AF_IPX;
int size = sizeof(sip);
if (!os_accept(socket, (struct sockaddr *)&sip, &size))
return FALSE;
port = ((PIPXSocket &)socket).GetPort();
return TRUE;
}
#endif
// End Of File ///////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1