/*
* ethsock.cxx
*
* Direct Ethernet socket implementation.
*
* 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: ethsock.cxx,v $
* Revision 1.47 2005/11/30 12:47:42 csoutheren
* Removed tabs, reformatted some code, and changed tags for Doxygen
*
* Revision 1.46 2005/09/18 13:01:43 dominance
* fixed pragma warnings when building with gcc.
*
* Revision 1.45 2005/07/13 11:48:54 csoutheren
* Backported QOS changes from isvo branch
*
* Revision 1.44.4.1 2005/04/25 13:33:56 shorne
* Extra support for DHCP Environment (Win32)
*
* Revision 1.44 2004/10/23 10:52:59 ykiryanov
* Added ifdef _WIN32_WCE for PocketPC 2003 SDK port
*
* Revision 1.43 2004/06/30 12:17:06 rjongbloed
* Rewrite of plug in system to use single global variable for all factories to avoid all sorts
* of issues with startup orders and Windows DLL multiple instances.
*
* Revision 1.42 2004/06/08 01:19:22 csoutheren
* Fixed problem with SNMP library not loading under Windows in some cases
*
* Revision 1.41 2004/06/01 05:24:12 csoutheren
* Changed loading of inetmib1.dll to use PProcessStartup to avoid crashes when it is unloaded before ~H323Endpoint is called
*
* Revision 1.40 2004/01/30 02:06:06 csoutheren
* Added mutex to avoid threading problems on Windows
* Thanks to Hans Verbeek
*
* Revision 1.39 2003/11/05 22:51:22 csoutheren
* Added pragma to automatically included required libs
*
* Revision 1.38 2003/09/17 05:45:10 csoutheren
* Removed recursive includes
*
* Revision 1.37 2003/04/01 06:01:48 robertj
* Fixed problem with returning correct route table device name if have
* 2 NIC's under Windows 2000, thanks faa06@tid.es
*
* Revision 1.36 2003/01/11 05:10:51 robertj
* Fixed Win CE compatibility issues, thanks Joerg Schoemer
*
* Revision 1.35 2002/11/12 02:22:16 robertj
* Fixed problem where if SNMP not correctly installed on some flavours of
* windows (eg ME) the system cannot find any interfaces. Added fail safe
* that if could not determine one, it uses the ip address of gethostname()
* which should be one of the interfaces on the system.
*
* Revision 1.34 2002/11/08 06:45:23 robertj
* Fixed problem with very long interface names, pointed out by Kees Klop.
*
* Revision 1.33 2002/10/08 12:41:52 robertj
* Changed for IPv6 support, thanks Sébastien Josset.
*
* Revision 1.32 2002/02/25 09:57:29 robertj
* Fixed possible NULL pointer use and memory leak, thanks Klaus König
*
* Revision 1.31 2002/02/15 03:56:46 yurik
* Warnings removed during compilation, patch courtesy of Jehan Bing, jehan@bravobrava.com
*
* Revision 1.30 2001/10/12 19:04:24 yurik
* New more robust ip collection routine
*
* Revision 1.29 2001/10/04 05:59:41 robertj
* Plugged numerous memory leaks.
*
* Revision 1.28 2001/10/03 03:12:21 robertj
* Changed to use only a single instance of SNMP library to avoid memory leak.
*
* Revision 1.27 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.26 2001/09/09 02:03:49 yurik
* no message
*
* Revision 1.25 2001/08/28 03:22:01 yurik
* Fixed crash on snmp init bug
*
* Revision 1.24 2001/08/16 20:12:25 yurik
* Fixed duplicate ordinal - ifdef'd ce code
*
* Revision 1.23 2001/08/15 22:15:27 yurik
* First cut of Windows CE port to support gatekeeper
*
* Revision 1.22 2001/03/05 04:18:27 robertj
* Added net mask to interface info returned by GetInterfaceTable()
*
* Revision 1.21 2000/03/06 03:59:22 robertj
* Fixed warning about handle types, thanks Steve Bennett
*
* Revision 1.20 1999/10/29 03:34:19 robertj
* Fixed possible crash accessing IP addresses from SNMP tables.
*
* Revision 1.19 1999/10/14 01:34:55 robertj
* Fixed backward compatibility problem with old SNMP header file.
*
* Revision 1.18 1999/09/10 04:35:42 robertj
* Added Windows version of PIPSocket::GetInterfaceTable() function.
*
* Revision 1.17 1999/04/18 12:58:39 robertj
* MSVC 5 backward compatibility
*
* Revision 1.16 1999/02/16 08:08:06 robertj
* MSVC 6.0 compatibility changes.
*
* Revision 1.15 1998/11/30 04:48:38 robertj
* New directory structure
*
* Revision 1.14 1998/11/22 11:30:10 robertj
* Check route table function to get a list
*
* Revision 1.13 1998/11/20 03:17:43 robertj
* Split rad and write buffers to separate pools.
*
* Revision 1.12 1998/11/19 05:18:48 robertj
* Added route table manipulation functions to PIPSocket class.
*
* Revision 1.11 1998/11/14 06:31:41 robertj
* Changed semantics of os_sendto to return TRUE if ANY bytes are sent.
* Added support for MSDUN1.3 DHCP registry entries.
*
* Revision 1.10 1998/10/23 04:09:08 robertj
* Fixes for NT support.
* Allowed both old and new driver by compilation option.
*
* Revision 1.9 1998/10/15 05:41:48 robertj
* New memory leak check code.
*
* Revision 1.8 1998/10/12 09:34:42 robertj
* New method for getting IP addresses of interfaces.
*
* Revision 1.7 1998/10/06 10:24:41 robertj
* Fixed hang when using reset command, removed the command!
*
* Revision 1.6 1998/09/24 03:30:45 robertj
* Added open software license.
*
* Revision 1.5 1998/09/15 08:25:36 robertj
* Fixed a number of warnings at maximum optimisation.
*
* Revision 1.4 1998/09/08 15:14:36 robertj
* Fixed packet type based filtering in Read() function.
*
* Revision 1.3 1998/08/25 11:03:15 robertj
* Fixed proble with NT get of OID.
* Fixed bug with not setting channel name when interface opened.
*
* Revision 1.2 1998/08/21 05:27:13 robertj
* Fine tuning of interface.
*
* Revision 1.1 1998/08/20 06:04:52 robertj
* Initial revision
*
*/
#include <ptlib.h>
#include <ptlib/sockets.h>
#include <snmp.h>
#ifndef _WIN32_WCE
#ifdef _MSC_VER
#pragma comment(lib, "snmpapi.lib")
#endif
#endif
///////////////////////////////////////////////////////////////////////////////
// Stuff from snmp.h
#ifndef RFC1157VarBindList
typedef RFC1157VarBind SnmpVarBind;
typedef RFC1157VarBindList SnmpVarBindList;
typedef LONG AsnInteger32;
#define SNMP_PDU_GET ASN_RFC1157_GETREQUEST
#define SNMP_PDU_GETNEXT ASN_RFC1157_GETNEXTREQUEST
#define ASN_IPADDRESS ASN_RFC1155_IPADDRESS // Prevents GetInterfaceTable failure
#pragma message("Later version of snmp.h required!")
#endif
///////////////////////////////////////////////////////////////////////////////
// Stuff from ndis.h
#define OID_802_3_PERMANENT_ADDRESS 0x01010101
#define OID_802_3_CURRENT_ADDRESS 0x01010102
#define OID_GEN_DRIVER_VERSION 0x00010110
#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
#define OID_GEN_MEDIA_SUPPORTED 0x00010103
#define NDIS_PACKET_TYPE_DIRECTED 0x0001
#define NDIS_PACKET_TYPE_MULTICAST 0x0002
#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x0004
#define NDIS_PACKET_TYPE_BROADCAST 0x0008
#define NDIS_PACKET_TYPE_PROMISCUOUS 0x0020
typedef enum _NDIS_MEDIUM {
NdisMedium802_3,
NdisMedium802_5,
NdisMediumFddi,
NdisMediumWan,
NdisMediumLocalTalk,
NdisMediumDix, // defined for convenience, not a real medium
NdisMediumArcnetRaw,
NdisMediumArcnet878_2
} NDIS_MEDIUM, *PNDIS_MEDIUM;
///////////////////////////////////////////////////////////////////////////////
#define USE_VPACKET
#include <ptlib/msos/ptlib/epacket.h>
#ifdef USE_VPACKET
#define PACKET_SERVICE_NAME "Packet"
#define PACKET_VXD_NAME "VPacket"
#else
#define PACKET_SERVICE_NAME "EPacket"
#define PACKET_VXD_NAME "EPacket"
#define GetQueryOidCommand(oid) IOCTL_EPACKET_QUERY_OID
#endif
#define SERVICES_REGISTRY_KEY "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\"
///////////////////////////////////////////////////////////////////////////////
class PWin32AsnAny : public AsnAny
{
public:
PWin32AsnAny();
~PWin32AsnAny() { MemFree(); }
BOOL GetInteger(AsnInteger & i);
BOOL GetIpAddress(PIPSocket::Address & addr);
void MemFree();
private:
PWin32AsnAny(const PWin32AsnAny &) { }
PWin32AsnAny & operator=(const PWin32AsnAny &) { return *this; }
};
///////////////////////////////////////////////////////////////////////////////
class PWin32AsnOid : public AsnObjectIdentifier
{
public:
PWin32AsnOid();
PWin32AsnOid(const char * str);
PWin32AsnOid(const PWin32AsnOid & oid) { SnmpUtilOidCpy(this, (AsnObjectIdentifier *)&oid); }
~PWin32AsnOid() { SnmpUtilOidFree(this); }
PWin32AsnOid & operator=(const AsnObjectIdentifier&);
PWin32AsnOid & operator=(const PWin32AsnOid & oid) { SnmpUtilOidFree(this); SnmpUtilOidCpy(this, (AsnObjectIdentifier *)&oid); return *this; }
PWin32AsnOid & operator+=(const PWin32AsnOid & oid) { SnmpUtilOidAppend(this, (AsnObjectIdentifier *)&oid); return *this; }
UINT & operator[](int idx) { return ids[idx]; }
UINT operator[](int idx) const { return ids[idx]; }
bool operator==(const PWin32AsnOid & oid) { return SnmpUtilOidCmp(this, (AsnObjectIdentifier *)&oid) == 0; }
bool operator!=(const PWin32AsnOid & oid) { return SnmpUtilOidCmp(this, (AsnObjectIdentifier *)&oid) != 0; }
bool operator< (const PWin32AsnOid & oid) { return SnmpUtilOidCmp(this, (AsnObjectIdentifier *)&oid) < 0; }
bool operator<=(const PWin32AsnOid & oid) { return SnmpUtilOidCmp(this, (AsnObjectIdentifier *)&oid) <= 0; }
bool operator> (const PWin32AsnOid & oid) { return SnmpUtilOidCmp(this, (AsnObjectIdentifier *)&oid) > 0; }
bool operator>=(const PWin32AsnOid & oid) { return SnmpUtilOidCmp(this, (AsnObjectIdentifier *)&oid) >= 0; }
bool operator*=(const PWin32AsnOid & oid) { return SnmpUtilOidNCmp(this, (AsnObjectIdentifier *)&oid, idLength) == 0; }
};
/////////////////////////////////////////////////////////////////////////////
class PWin32SnmpLibrary
#ifndef _WIN32_WCE
: public PDynaLink
{
PCLASSINFO(PWin32SnmpLibrary, PDynaLink)
public:
#else
{
public:
void Close();
BOOL IsLoaded() { return TRUE; }
#endif
public:
PWin32SnmpLibrary();
BOOL GetOid(AsnObjectIdentifier & oid, AsnInteger & value);
BOOL GetOid(AsnObjectIdentifier & oid, PIPSocket::Address & ip_address);
BOOL GetOid(AsnObjectIdentifier & oid, PString & str);
BOOL GetOid(AsnObjectIdentifier & oid, void * value, UINT valSize, UINT * len = NULL);
BOOL GetOid(AsnObjectIdentifier & oid, PWin32AsnAny & value) { return QueryOid(SNMP_PDU_GET, oid, value); }
BOOL GetNextOid(AsnObjectIdentifier & oid, PWin32AsnAny & value) { return QueryOid(SNMP_PDU_GETNEXT, oid, value); }
PString GetInterfaceName(int ifNum);
PString GetInterfaceName(PIPSocket::Address ipAddr);
PIPSocket::Address GetInterfaceAddress(int ifNum);
static PWin32SnmpLibrary & Current();
static PMutex & GetMutex();
private:
PMutex mutex;
BOOL (WINAPI *_Init)(DWORD,HANDLE*,AsnObjectIdentifier*);
BOOL (WINAPI *_Query)(BYTE,SnmpVarBindList*,AsnInteger32*,AsnInteger32*);
BOOL Init(DWORD upTime, HANDLE * trapEvent, AsnObjectIdentifier * firstSupportedRegion)
{ return (*_Init)(upTime, trapEvent, firstSupportedRegion); }
BOOL Query(BYTE pduType, SnmpVarBindList * pVarBindList, AsnInteger32 * pErrorStatus, AsnInteger32 * pErrorIndex)
{ return _Query(pduType, pVarBindList, pErrorStatus, pErrorIndex); }
BOOL QueryOid(BYTE cmd, AsnObjectIdentifier & oid, PWin32AsnAny & value);
};
class WinSNMPLoader : public PProcessStartup
{
PCLASSINFO(WinSNMPLoader, PProcessStartup);
public:
void OnStartup()
{ }
PWin32SnmpLibrary & Current()
{
PWaitAndSignal m(mutex);
if (snmpLibrary == NULL) {
snmpLibrary = new PWin32SnmpLibrary;
}
return *snmpLibrary;
}
void OnShutdown()
{
PWaitAndSignal m(mutex);
delete snmpLibrary;
snmpLibrary = NULL;
}
protected:
PMutex mutex;
static PWin32SnmpLibrary * snmpLibrary;
};
PWin32SnmpLibrary * WinSNMPLoader::snmpLibrary = NULL;
static PFactory<PProcessStartup>::Worker<WinSNMPLoader> winSNMPLoadedStartupFactory("WinSNMPLoader", true);
PWin32SnmpLibrary & PWin32SnmpLibrary::Current()
{
return ((WinSNMPLoader *)PFactory<PProcessStartup>::CreateInstance("WinSNMPLoader"))->Current();
}
PMutex & PWin32SnmpLibrary::GetMutex()
{
return Current().mutex;
}
#define GetSNMPMutex PWin32SnmpLibrary::GetMutex
///////////////////////////////////////////////////////////////////////////////
class PWin32OidBuffer
{
public:
PWin32OidBuffer(UINT oid, UINT len, const BYTE * data = NULL);
~PWin32OidBuffer() { delete buffer; }
operator void *() { return buffer; }
operator DWORD () { return size; }
DWORD operator [](int i) { return buffer[i]; }
void Move(BYTE * data, DWORD received);
private:
DWORD * buffer;
UINT size;
};
///////////////////////////////////////////////////////////////////////////////
class PWin32PacketDriver
{
public:
static PWin32PacketDriver * Create();
virtual ~PWin32PacketDriver();
BOOL IsOpen() const;
void Close();
DWORD GetLastError() const;
virtual BOOL EnumInterfaces(PINDEX idx, PString & name) = 0;
virtual BOOL BindInterface(const PString & interfaceName) = 0;
virtual BOOL EnumIpAddress(PINDEX idx, PIPSocket::Address & addr, PIPSocket::Address & net_mask) = 0;
virtual BOOL BeginRead(void * buf, DWORD size, DWORD & received, PWin32Overlapped & overlap) = 0;
virtual BOOL BeginWrite(const void * buf, DWORD len, PWin32Overlapped & overlap) = 0;
BOOL CompleteIO(DWORD & received, PWin32Overlapped & overlap);
BOOL IoControl(UINT func,
const void * input, DWORD inSize,
void * output, DWORD outSize,
DWORD & received);
BOOL QueryOid(UINT oid, DWORD & data);
BOOL QueryOid(UINT oid, UINT len, BYTE * data);
BOOL SetOid(UINT oid, DWORD data);
BOOL SetOid(UINT oid, UINT len, const BYTE * data);
#ifdef USE_VPACKET
virtual UINT GetQueryOidCommand(DWORD oid) const = 0;
#endif
protected:
PWin32PacketDriver();
DWORD dwError;
HANDLE hDriver;
};
///////////////////////////////////////////////////////////////////////////////
class PWin32PacketVxD : public PWin32PacketDriver
{
public:
virtual BOOL EnumInterfaces(PINDEX idx, PString & name);
virtual BOOL BindInterface(const PString & interfaceName);
virtual BOOL EnumIpAddress(PINDEX idx, PIPSocket::Address & addr, PIPSocket::Address & net_mask);
virtual BOOL BeginRead(void * buf, DWORD size, DWORD & received, PWin32Overlapped & overlap);
virtual BOOL BeginWrite(const void * buf, DWORD len, PWin32Overlapped & overlap);
#ifdef USE_VPACKET
virtual UINT GetQueryOidCommand(DWORD oid) const
{ return oid >= OID_802_3_PERMANENT_ADDRESS ? IOCTL_EPACKET_QUERY_OID : IOCTL_EPACKET_STATISTICS; }
#endif
protected:
PStringList transportBinding;
};
///////////////////////////////////////////////////////////////////////////////
class PWin32PacketSYS : public PWin32PacketDriver
{
public:
PWin32PacketSYS();
virtual BOOL EnumInterfaces(PINDEX idx, PString & name);
virtual BOOL BindInterface(const PString & interfaceName);
virtual BOOL EnumIpAddress(PINDEX idx, PIPSocket::Address & addr, PIPSocket::Address & net_mask);
virtual BOOL BeginRead(void * buf, DWORD size, DWORD & received, PWin32Overlapped & overlap);
virtual BOOL BeginWrite(const void * buf, DWORD len, PWin32Overlapped & overlap);
#ifdef USE_VPACKET
virtual UINT GetQueryOidCommand(DWORD) const
{ return IOCTL_EPACKET_QUERY_OID; }
#endif
protected:
PString registryKey;
};
///////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32_WCE
class PWin32PacketCe : public PWin32PacketDriver
{
public:
PWin32PacketCe();
virtual BOOL EnumInterfaces(PINDEX idx, PString & name);
virtual BOOL BindInterface(const PString & interfaceName);
virtual BOOL EnumIpAddress(PINDEX idx, PIPSocket::Address & addr, PIPSocket::Address & net_mask);
virtual BOOL BeginRead(void * buf, DWORD size, DWORD & received, PWin32Overlapped & overlap);
virtual BOOL BeginWrite(const void * buf, DWORD len, PWin32Overlapped & overlap);
#ifdef USE_VPACKET
virtual UINT GetQueryOidCommand(DWORD) const
{ return IOCTL_EPACKET_QUERY_OID; }
#endif
protected:
PStringArray ipAddresses;
PStringArray netMasks;
PStringArray interfaces;
};
#endif // _WIN32_WCE
/////////////////////////////////////////////////////////////////////////////
class PWin32PacketBuffer : public PBYTEArray
{
PCLASSINFO(PWin32PacketBuffer, PBYTEArray)
public:
enum Statuses {
Uninitialised,
Progressing,
Completed
};
PWin32PacketBuffer(PINDEX sz);
PINDEX GetData(void * buf, PINDEX size);
PINDEX PutData(const void * buf, PINDEX length);
HANDLE GetEvent() const { return overlap.hEvent; }
BOOL ReadAsync(PWin32PacketDriver & pkt);
BOOL ReadComplete(PWin32PacketDriver & pkt);
BOOL WriteAsync(PWin32PacketDriver & pkt);
BOOL WriteComplete(PWin32PacketDriver & pkt);
BOOL InProgress() const { return status == Progressing; }
BOOL IsCompleted() const { return status == Completed; }
BOOL IsType(WORD type) const;
protected:
Statuses status;
PWin32Overlapped overlap;
DWORD count;
};
#define new PNEW
/////////////////////////////////////////////////////////////////////////////
PWin32AsnAny::PWin32AsnAny()
{
asnType = ASN_INTEGER;
asnValue.number = 0;
}
void PWin32AsnAny::MemFree()
{
switch (asnType) {
case ASN_OCTETSTRING :
SnmpUtilMemFree(asnValue.string.stream);
break;
#ifdef ASN_BITS
case ASN_BITS :
SnmpUtilMemFree(asnValue.bits.stream);
break;
#endif
case ASN_OBJECTIDENTIFIER :
SnmpUtilMemFree(asnValue.object.ids);
break;
case ASN_SEQUENCE :
SnmpUtilMemFree(asnValue.sequence.stream);
break;
case ASN_IPADDRESS :
SnmpUtilMemFree(asnValue.address.stream);
break;
#ifdef ASN_OPAQUE
case ASN_OPAQUE :
SnmpUtilMemFree(asnValue.arbitrary.stream);
break;
#endif
}
asnType = ASN_INTEGER;
}
BOOL PWin32AsnAny::GetInteger(AsnInteger & i)
{
if (asnType != ASN_INTEGER)
return FALSE;
i = asnValue.number;
return TRUE;
}
BOOL PWin32AsnAny::GetIpAddress(PIPSocket::Address & addr)
{
if (asnType != ASN_IPADDRESS || asnValue.address.stream == NULL)
return FALSE;
addr = PIPSocket::Address(asnValue.address.length, asnValue.address.stream);
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
PWin32AsnOid::PWin32AsnOid()
{
ids = NULL;
idLength = 0;
}
PWin32AsnOid::PWin32AsnOid(const char * str)
{
idLength = 0;
ids = NULL;
AsnObjectIdentifier oid;
oid.idLength = 0;
const char * dot = strchr(str, '.');
while (dot != NULL) {
oid.idLength++;
dot = strchr(dot+1, '.');
}
if (oid.idLength > 0) {
oid.ids = new UINT[++oid.idLength];
char * next = (char *)str;
for (UINT i = 0; i < oid.idLength; i++) {
oid.ids[i] = strtoul(next, &next, 10);
if (*next != '.')
break;
next++;
}
if (*next == '\0')
SnmpUtilOidCpy(this, &oid);
delete [] oid.ids;
}
}
PWin32AsnOid & PWin32AsnOid::operator=(const AsnObjectIdentifier & oid)
{
ids = oid.ids;
idLength = oid.idLength;
return *this;
}
///////////////////////////////////////////////////////////////////////////////
PWin32SnmpLibrary::PWin32SnmpLibrary()
#ifndef _WIN32_WCE
: PDynaLink("inetmib1.dll")
#endif
{
#ifndef _WIN32_WCE
HANDLE hEvent;
AsnObjectIdentifier baseOid;
if (!GetFunction("SnmpExtensionInit", (Function &)_Init) ||
!GetFunction("SnmpExtensionQuery", (Function &)_Query)) {
Close();
PTRACE(1, "PWlib\tInvalid DLL: inetmib1.dll");
}
else if (!Init(0, &hEvent, &baseOid)) {
PTRACE(1, "PWlib\tCould not initialise SNMP DLL: error=" << ::GetLastError());
Close();
}
#else
_Init = SnmpExtensionInit; // do not call Init as we dont'have Close
_Query = SnmpExtensionQuery;
#endif
}
BOOL PWin32SnmpLibrary::GetOid(AsnObjectIdentifier & oid, AsnInteger & value)
{
//if (!IsLoaded())
// return FALSE;
PWin32AsnAny any;
if (!GetOid(oid, any))
return FALSE;
return any.GetInteger(value);
}
BOOL PWin32SnmpLibrary::GetOid(AsnObjectIdentifier & oid, PIPSocket::Address & value)
{
//if (!IsLoaded())
// return FALSE;
PWin32AsnAny any;
if (!GetOid(oid, any))
return FALSE;
return any.GetIpAddress(value);
}
BOOL PWin32SnmpLibrary::GetOid(AsnObjectIdentifier & oid, PString & str)
{
//if (!IsLoaded())
// return FALSE;
PWin32AsnAny any;
if (!GetOid(oid, any))
return FALSE;
if (any.asnType != ASN_OCTETSTRING)
return FALSE;
str = PString((char *)any.asnValue.string.stream, any.asnValue.string.length);
return TRUE;
}
BOOL PWin32SnmpLibrary::GetOid(AsnObjectIdentifier & oid, void * value, UINT valSize, UINT * len)
{
//if (!IsLoaded())
// return FALSE;
PWin32AsnAny any;
if (!GetOid(oid, any))
return FALSE;
if (any.asnType != ASN_OCTETSTRING)
return FALSE;
if (len != NULL)
*len = any.asnValue.string.length;
if (any.asnValue.string.length > valSize)
return FALSE;
memcpy(value, any.asnValue.string.stream, any.asnValue.string.length);
if (valSize > any.asnValue.string.length)
((char *)value)[any.asnValue.string.length] = '\0';
return TRUE;
}
BOOL PWin32SnmpLibrary::QueryOid(BYTE cmd, AsnObjectIdentifier & oid, PWin32AsnAny & value)
{
//if (!IsLoaded())
// return FALSE;
value.MemFree();
SnmpVarBindList vars;
vars.len = 1;
vars.list = (SnmpVarBind*)SnmpUtilMemAlloc(sizeof(SnmpVarBind));
if (vars.list == NULL)
return FALSE;
vars.list->name = oid;
vars.list->value = value;
AsnInteger status, error;
if (Query(cmd, &vars, &status, &error) && status == SNMP_ERRORSTATUS_NOERROR) {
(AsnAny&)value = vars.list->value; // Use cast so does simple copy
oid = vars.list->name;
}
SnmpUtilMemFree(vars.list);
return status == SNMP_ERRORSTATUS_NOERROR;
}
PString PWin32SnmpLibrary::GetInterfaceName(int ifNum)
{
PIPSocket::Address gwAddr = 0;
gwAddr = GetInterfaceAddress(ifNum);
if (gwAddr == 0)
return PString::Empty();
PString name = GetInterfaceName(gwAddr);
if (name.IsEmpty()) {
PWin32AsnOid nameOid = "1.3.6.1.2.1.2.2.1.2.0";
nameOid[10] = ifNum;
if (GetOid(nameOid, name.GetPointer(100), 100))
name.MakeMinimumSize();
}
return name;
}
PString PWin32SnmpLibrary::GetInterfaceName(PIPSocket::Address ipAddr)
{
PString gatewayInterface, anInterface;
PWin32PacketDriver * tempDriver = PWin32PacketDriver::Create();
PINDEX ifIdx = 0;
while (gatewayInterface.IsEmpty() && tempDriver->EnumInterfaces(ifIdx++, anInterface)) {
if (tempDriver->BindInterface(anInterface)) {
PIPSocket::Address ifAddr, ifMask;
PINDEX ipIdx = 0;
if (tempDriver->EnumIpAddress(ipIdx++, ifAddr, ifMask) && ifAddr == ipAddr) {
gatewayInterface = anInterface;
break;
}
}
}
delete tempDriver;
return gatewayInterface;
}
PIPSocket::Address PWin32SnmpLibrary::GetInterfaceAddress(int ifNum)
{
PIPSocket::Address gwAddr = 0;
PWin32AsnOid baseOid = "1.3.6.1.2.1.4.20.1";
PWin32AsnOid oid = baseOid;
PWin32AsnAny value;
while (GetNextOid(oid, value)) {
if (!(baseOid *= oid))
break;
if (value.asnType != ASN_IPADDRESS)
break;
oid[9] = 2;
AsnInteger ifIndex = -1;
if (!GetOid(oid, ifIndex) || ifIndex < 0)
break;
if (ifIndex == ifNum) {
value.GetIpAddress(gwAddr);
break;
}
oid[9] = 1;
}
return gwAddr;
}
/*
PWin32SnmpLibrary & PWin32SnmpLibrary::Current()
{
static PWin32SnmpLibrary instance;
return instance;
}
*/
///////////////////////////////////////////////////////////////////////////////
PWin32OidBuffer::PWin32OidBuffer(UINT oid, UINT len, const BYTE * data)
{
size = sizeof(DWORD)*2 + len;
buffer = new DWORD[(size+sizeof(DWORD)-1)/sizeof(DWORD)];
buffer[0] = oid;
buffer[1] = len;
if (data != NULL)
memcpy(&buffer[2], data, len);
}
void PWin32OidBuffer::Move(BYTE * data, DWORD received)
{
memcpy(data, &buffer[2], received-sizeof(DWORD)*2);
}
///////////////////////////////////////////////////////////////////////////////
PWin32PacketDriver * PWin32PacketDriver::Create()
{
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof(info);
GetVersionEx(&info);
#ifndef _WIN32_WCE
if (info.dwPlatformId == VER_PLATFORM_WIN32_NT)
return new PWin32PacketSYS;
else
return new PWin32PacketVxD;
#else
return new PWin32PacketCe;
#endif
}
PWin32PacketDriver::PWin32PacketDriver()
{
hDriver = INVALID_HANDLE_VALUE;
dwError = ERROR_OPEN_FAILED;
}
PWin32PacketDriver::~PWin32PacketDriver()
{
Close();
}
void PWin32PacketDriver::Close()
{
if (hDriver != INVALID_HANDLE_VALUE) {
CloseHandle(hDriver);
hDriver = INVALID_HANDLE_VALUE;
}
}
BOOL PWin32PacketDriver::IsOpen() const
{
return hDriver != INVALID_HANDLE_VALUE;
}
DWORD PWin32PacketDriver::GetLastError() const
{
return dwError;
}
BOOL PWin32PacketDriver::IoControl(UINT func,
const void * input, DWORD inSize,
void * output, DWORD outSize, DWORD & received)
{
PWin32Overlapped overlap;
if (DeviceIoControl(hDriver, func,
(LPVOID)input, inSize, output, outSize,
&received, &overlap)) {
dwError = ERROR_SUCCESS;
return TRUE;
}
dwError = ::GetLastError();
if (dwError != ERROR_IO_PENDING)
return FALSE;
return CompleteIO(received, overlap);
}
BOOL PWin32PacketDriver::CompleteIO(DWORD & received, PWin32Overlapped & overlap)
{
#ifndef _WIN32_WCE
received = 0;
if (GetOverlappedResult(hDriver, &overlap, &received, TRUE)) {
dwError = ERROR_SUCCESS;
return TRUE;
}
dwError = ::GetLastError();
return FALSE;
#else
return TRUE;
#endif
}
BOOL PWin32PacketDriver::QueryOid(UINT oid, UINT len, BYTE * data)
{
PWin32OidBuffer buf(oid, len);
DWORD rxsize = 0;
if (!IoControl(GetQueryOidCommand(oid), buf, buf, buf, buf, rxsize))
return FALSE;
if (rxsize == 0)
return FALSE;
buf.Move(data, rxsize);
return TRUE;
}
BOOL PWin32PacketDriver::QueryOid(UINT oid, DWORD & data)
{
DWORD oidData[3];
oidData[0] = oid;
oidData[1] = sizeof(data);
oidData[2] = 0x12345678;
DWORD rxsize = 0;
if (!IoControl(GetQueryOidCommand(oid),
oidData, sizeof(oidData),
oidData, sizeof(oidData),
rxsize))
return FALSE;
if (rxsize == 0)
return FALSE;
data = oidData[2];
return TRUE;
}
BOOL PWin32PacketDriver::SetOid(UINT oid, UINT len, const BYTE * data)
{
DWORD rxsize = 0;
PWin32OidBuffer buf(oid, len, data);
return IoControl(IOCTL_EPACKET_SET_OID, buf, buf, buf, buf, rxsize);
}
BOOL PWin32PacketDriver::SetOid(UINT oid, DWORD data)
{
DWORD oidData[3];
oidData[0] = oid;
oidData[1] = sizeof(data);
oidData[2] = data;
DWORD rxsize;
return IoControl(IOCTL_EPACKET_SET_OID,
oidData, sizeof(oidData), oidData, sizeof(oidData), rxsize);
}
static BOOL RegistryQueryMultiSz(RegistryKey & registry,
const PString & variable,
PINDEX idx,
PString & value)
{
PString allValues;
if (!registry.QueryValue(variable, allValues))
return FALSE;
const char * ptr = allValues;
while (*ptr != '\0' && idx-- > 0)
ptr += strlen(ptr)+1;
if (*ptr == '\0')
return FALSE;
value = ptr;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
BOOL PWin32PacketVxD::EnumInterfaces(PINDEX idx, PString & name)
{
static const PString RegBase = SERVICES_REGISTRY_KEY "Class\\Net";
PString keyName;
RegistryKey registry(RegBase, RegistryKey::ReadOnly);
if (!registry.EnumKey(idx, keyName))
return FALSE;
PString description;
RegistryKey subkey(RegBase + "\\" + keyName, RegistryKey::ReadOnly);
if (subkey.QueryValue("DriverDesc", description))
name = keyName + ": " + description;
else
name = keyName;
return TRUE;
}
static PString SearchRegistryKeys(const PString & key,
const PString & variable,
const PString & value)
{
RegistryKey registry(key, RegistryKey::ReadOnly);
PString str;
if (registry.QueryValue(variable, str) && (str *= value))
return key;
for (PINDEX idx = 0; registry.EnumKey(idx, str); idx++) {
PString result = SearchRegistryKeys(key + str + '\\', variable, value);
if (!result)
return result;
}
return PString::Empty();
}
BOOL PWin32PacketVxD::BindInterface(const PString & interfaceName)
{
#ifndef _WIN32_WCE
BYTE buf[20];
DWORD rxsize;
if (hDriver == INVALID_HANDLE_VALUE) {
hDriver = CreateFile("\\\\.\\" PACKET_VXD_NAME ".VXD",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_OVERLAPPED |
FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if (hDriver == INVALID_HANDLE_VALUE) {
dwError = ::GetLastError();
return FALSE;
}
#ifndef USE_VPACKET
rxsize = 0;
if (!IoControl(IOCTL_EPACKET_VERSION, NULL, 0, buf, sizeof(buf), rxsize)) {
dwError = ::GetLastError();
return FALSE;
}
if (rxsize != 2 || buf[0] < 1 || buf[1] < 1) { // Require driver version 1.1
Close();
dwError = ERROR_BAD_DRIVER;
return FALSE;
}
#endif
}
PString devName;
PINDEX colon = interfaceName.Find(':');
if (colon != P_MAX_INDEX)
devName = interfaceName.Left(colon);
else
devName = interfaceName;
rxsize = 0;
if (!IoControl(IOCTL_EPACKET_BIND,
(const char *)devName, devName.GetLength()+1,
buf, sizeof(buf), rxsize) || rxsize == 0) {
dwError = ::GetLastError();
if (dwError == 0)
dwError = ERROR_BAD_DRIVER;
return FALSE;
}
// Get a random OID to verify that the driver did actually open
if (!QueryOid(OID_GEN_DRIVER_VERSION, 2, buf))
return FALSE;
dwError = ERROR_SUCCESS; // Successful, even if may not be bound.
PString devKey = SearchRegistryKeys("HKEY_LOCAL_MACHINE\\Enum\\", "Driver", "Net\\" + devName);
if (devKey.IsEmpty())
return TRUE;
RegistryKey bindRegistry(devKey + "Bindings", RegistryKey::ReadOnly);
PString binding;
PINDEX idx = 0;
while (bindRegistry.EnumValue(idx++, binding)) {
if (binding.Left(6) *= "MSTCP\\") {
RegistryKey mstcpRegistry("HKEY_LOCAL_MACHINE\\Enum\\Network\\" + binding, RegistryKey::ReadOnly);
PString str;
if (mstcpRegistry.QueryValue("Driver", str))
transportBinding.AppendString(SERVICES_REGISTRY_KEY "Class\\" + str);
}
}
#endif // !_WIN32_WCE
return TRUE;
}
BOOL PWin32PacketVxD::EnumIpAddress(PINDEX idx,
PIPSocket::Address & addr,
PIPSocket::Address & net_mask)
{
if (idx >= transportBinding.GetSize())
return FALSE;
RegistryKey transportRegistry(transportBinding[idx], RegistryKey::ReadOnly);
PString str;
if (transportRegistry.QueryValue("IPAddress", str))
addr = str;
else
addr = 0;
if (addr != 0) {
if (addr.GetVersion() == 6) {
net_mask = 0;
// Seb: Something to do ?
} else {
if (transportRegistry.QueryValue("IPMask", str))
net_mask = str;
else {
if (IN_CLASSA(addr))
net_mask = "255.0.0.0";
else if (IN_CLASSB(addr))
net_mask = "255.255.0.0";
else if (IN_CLASSC(addr))
net_mask = "255.255.255.0";
else
net_mask = 0;
}
}
return TRUE;
}
PEthSocket::Address macAddress;
if (!QueryOid(OID_802_3_CURRENT_ADDRESS, sizeof(macAddress), macAddress.b))
return FALSE;
PINDEX dhcpCount;
for (dhcpCount = 0; dhcpCount < 8; dhcpCount++) {
RegistryKey dhcpRegistry(psprintf(SERVICES_REGISTRY_KEY "VxD\\DHCP\\DhcpInfo%02u", dhcpCount),
RegistryKey::ReadOnly);
if (dhcpRegistry.QueryValue("DhcpInfo", str)) {
struct DhcpInfo {
DWORD index;
PIPSocket::Address ipAddress;
PIPSocket::Address mask;
PIPSocket::Address server;
PIPSocket::Address anotherAddress;
DWORD unknown1;
DWORD unknown2;
DWORD unknown3;
DWORD unknown4;
DWORD unknown5;
DWORD unknown6;
BYTE unknown7;
PEthSocket::Address macAddress;
} * dhcpInfo = (DhcpInfo *)(const char *)str;
if (dhcpInfo->macAddress == macAddress) {
addr = dhcpInfo->ipAddress;
net_mask = dhcpInfo->mask;
return TRUE;
}
}
else if (dhcpRegistry.QueryValue("HardwareAddress", str) &&
str.GetSize() >= sizeof(PEthSocket::Address)) {
PEthSocket::Address hardwareAddress;
memcpy(&hardwareAddress, (const char *)str, sizeof(hardwareAddress));
if (hardwareAddress == macAddress) {
if (dhcpRegistry.QueryValue("DhcpIPAddress", str) &&
str.GetSize() >= sizeof(addr)) {
memcpy(&addr, (const char *)str, sizeof(addr));
if (dhcpRegistry.QueryValue("DhcpSubnetMask", str) &&
str.GetSize() >= sizeof(net_mask)) {
memcpy(&net_mask, (const char *)str, sizeof(net_mask));
return TRUE;
}
}
}
}
}
return FALSE;
}
BOOL PWin32PacketVxD::BeginRead(void * buf, DWORD size, DWORD & received, PWin32Overlapped & overlap)
{
received = 0;
if (DeviceIoControl(hDriver, IOCTL_EPACKET_READ,
buf, size, buf, size, &received, &overlap)) {
dwError = ERROR_SUCCESS;
return TRUE;
}
dwError = ::GetLastError();
return dwError == ERROR_IO_PENDING;
}
BOOL PWin32PacketVxD::BeginWrite(const void * buf, DWORD len, PWin32Overlapped & overlap)
{
DWORD rxsize = 0;
BYTE dummy[2];
if (DeviceIoControl(hDriver, IOCTL_EPACKET_WRITE,
(void *)buf, len, dummy, sizeof(dummy), &rxsize, &overlap)) {
dwError = ERROR_SUCCESS;
return TRUE;
}
dwError = ::GetLastError();
return dwError == ERROR_IO_PENDING;
}
///////////////////////////////////////////////////////////////////////////////
PWin32PacketSYS::PWin32PacketSYS()
{
#ifndef _WIN32_WCE
// Start the packet driver service
SC_HANDLE hManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hManager != NULL) {
SC_HANDLE hService = OpenService(hManager, PACKET_SERVICE_NAME, SERVICE_START);
if (hService != NULL) {
StartService(hService, 0, NULL);
dwError = ::GetLastError();
CloseServiceHandle(hService);
}
CloseServiceHandle(hManager);
}
#endif // !_WIN32_WCE
}
static const char PacketDeviceStr[] = "\\Device\\" PACKET_SERVICE_NAME "_";
BOOL PWin32PacketSYS::EnumInterfaces(PINDEX idx, PString & name)
{
#ifndef _WIN32_WCE
RegistryKey registry(SERVICES_REGISTRY_KEY PACKET_SERVICE_NAME "\\Linkage",
RegistryKey::ReadOnly);
if (!RegistryQueryMultiSz(registry, "Export", idx, name)) {
dwError = ERROR_NO_MORE_ITEMS;
return FALSE;
}
if (strnicmp(name, PacketDeviceStr, sizeof(PacketDeviceStr)-1) == 0)
name.Delete(0, sizeof(PacketDeviceStr)-1);
#endif // !_WIN32_WCE
return TRUE;
}
BOOL PWin32PacketSYS::BindInterface(const PString & interfaceName)
{
#ifndef _WIN32_WCE
Close();
if (!DefineDosDevice(DDD_RAW_TARGET_PATH,
PACKET_SERVICE_NAME "_" + interfaceName,
PacketDeviceStr + interfaceName)) {
dwError = ::GetLastError();
return FALSE;
}
::SetLastError(0);
hDriver = CreateFile("\\\\.\\" PACKET_SERVICE_NAME "_" + interfaceName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_FLAG_OVERLAPPED,
NULL);
if (hDriver == INVALID_HANDLE_VALUE) {
dwError = ::GetLastError();
return FALSE;
}
registryKey = SERVICES_REGISTRY_KEY + interfaceName + "\\Parameters\\Tcpip";
dwError = ERROR_SUCCESS;
#endif // !_WIN32_WCE
return TRUE;
}
BOOL PWin32PacketSYS::EnumIpAddress(PINDEX idx,
PIPSocket::Address & addr,
PIPSocket::Address & net_mask)
{
PString str;
RegistryKey registry(registryKey, RegistryKey::ReadOnly);
if (!RegistryQueryMultiSz(registry, "IPAddress", idx, str)) {
dwError = ERROR_NO_MORE_ITEMS;
return FALSE;
}
addr = str;
if (!RegistryQueryMultiSz(registry, "SubnetMask", idx, str)) {
dwError = ERROR_NO_MORE_ITEMS;
return FALSE;
}
net_mask = str;
return TRUE;
}
BOOL PWin32PacketSYS::BeginRead(void * buf, DWORD size, DWORD & received, PWin32Overlapped & overlap)
{
overlap.Reset();
received = 0;
if (ReadFile(hDriver, buf, size, &received, &overlap)) {
dwError = ERROR_SUCCESS;
return TRUE;
}
return (dwError = ::GetLastError()) == ERROR_IO_PENDING;
}
BOOL PWin32PacketSYS::BeginWrite(const void * buf, DWORD len, PWin32Overlapped & overlap)
{
overlap.Reset();
DWORD sent = 0;
if (WriteFile(hDriver, buf, len, &sent, &overlap)) {
dwError = ERROR_SUCCESS;
return TRUE;
}
dwError = ::GetLastError();
return dwError == ERROR_IO_PENDING;
}
///////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32_WCE
PWin32PacketCe::PWin32PacketCe()
{
PString str, driver, nameStr, keyStr, driverStr, miniportStr, linkageStr, routeStr, tcpipStr;
static const PString ActiveDrivers = "HKEY_LOCAL_MACHINE\\Drivers\\Active";
static const PString CommBase = "HKEY_LOCAL_MACHINE\\Comm";
// Collecting active drivers
RegistryKey registry(ActiveDrivers, RegistryKey::ReadOnly);
for (PINDEX idx = 0; registry.EnumKey(idx, str); idx++)
{
driver = ActiveDrivers + "\\" + str;
RegistryKey driverKey( driver, RegistryKey::ReadOnly );
// Filter out non - NDS drivers
if (!driverKey.QueryValue( "Name", nameStr ) || nameStr.Find("NDS") == P_MAX_INDEX )
continue;
// Active network driver found
//
// e.g. built-in driver has "Key" = Drivers\BuiltIn\NDIS
if( driverKey.QueryValue( "Key", keyStr ) )
{
if( P_MAX_INDEX != keyStr.Find("BuiltIn") )
{
// Built-in driver case
continue;
}
else
{
driverStr = "HKEY_LOCAL_MACHINE\\"+ keyStr;
RegistryKey ActiveDriverKey( driverStr, RegistryKey::ReadOnly );
// Get miniport value
if( ActiveDriverKey.QueryValue( "Miniport", miniportStr ) )
{
// Get miniport linkage
//
// e.g. [HKEY_LOCAL_MACHINE\Comm\SOCKETLPE\Linkage]
linkageStr = CommBase + "\\" + miniportStr + "\\Linkage";
RegistryKey LinkageKey( linkageStr, RegistryKey::ReadOnly );
// Get route to real driver
if( LinkageKey.QueryValue( "Route", routeStr ) )
{
tcpipStr = CommBase + "\\" + routeStr + "\\Parms\\TcpIp";
RegistryKey TcpIpKey( tcpipStr, RegistryKey::ReadOnly );
DWORD dwDHCPEnabled = FALSE;
TcpIpKey.QueryValue( "EnableDHCP", dwDHCPEnabled, TRUE );
/// Collect IP addresses and net masks
PString ipAddress, netMask;
if ( !dwDHCPEnabled )
{
if (TcpIpKey.QueryValue( "IpAddress", ipAddress )
&& (ipAddress != "0.0.0.0") )
{
interfaces[interfaces.GetSize()] = tcpipStr; // Registry key for the driver
ipAddresses[ipAddresses.GetSize()] = ipAddress; // It's IP
if( driverKey.QueryValue( "Subnetmask", netMask ) )
netMasks[netMasks.GetSize()] = netMask; // It's mask
else
netMasks[netMasks.GetSize()] = "255.255.255.0";
}
}
else // DHCP enabled
if( TcpIpKey.QueryValue( "DhcpIpAddress", ipAddress )
&& (ipAddress != "0.0.0.0") )
{
interfaces[interfaces.GetSize()] = str;
ipAddresses[ipAddresses.GetSize()] = ipAddress;
if( driverKey.QueryValue( "DhcpSubnetMask", netMask ) )
netMasks[netMasks.GetSize()] = netMask;
else
netMasks[netMasks.GetSize()] = "255.255.255.0";
}
}
}
}
}
}
}
BOOL PWin32PacketCe::EnumInterfaces(PINDEX idx, PString & name)
{
if( idx >= interfaces.GetSize() )
return FALSE;
name = interfaces[idx];
return TRUE;
}
BOOL PWin32PacketCe::BindInterface(const PString &)
{
return TRUE;
}
BOOL PWin32PacketCe::EnumIpAddress(PINDEX idx,
PIPSocket::Address & addr,
PIPSocket::Address & net_mask)
{
if( idx >= interfaces.GetSize() )
return FALSE;
addr = ipAddresses[idx];
net_mask = netMasks[idx];
return TRUE;
}
BOOL PWin32PacketCe::BeginRead(void *, DWORD, DWORD & , PWin32Overlapped &)
{
return TRUE;
}
BOOL PWin32PacketCe::BeginWrite(const void *, DWORD, PWin32Overlapped &)
{
return TRUE;
}
#endif // _WIN32_WCE
///////////////////////////////////////////////////////////////////////////////
PEthSocket::PEthSocket(PINDEX nReadBuffers, PINDEX nWriteBuffers, PINDEX size)
: readBuffers(min(nReadBuffers, MAXIMUM_WAIT_OBJECTS)),
writeBuffers(min(nWriteBuffers, MAXIMUM_WAIT_OBJECTS))
{
driver = PWin32PacketDriver::Create();
PINDEX i;
for (i = 0; i < nReadBuffers; i++)
readBuffers.SetAt(i, new PWin32PacketBuffer(size));
for (i = 0; i < nWriteBuffers; i++)
writeBuffers.SetAt(i, new PWin32PacketBuffer(size));
filterType = TypeAll;
}
PEthSocket::~PEthSocket()
{
Close();
delete driver;
}
BOOL PEthSocket::OpenSocket()
{
PAssertAlways(PUnimplementedFunction);
return FALSE;
}
BOOL PEthSocket::Close()
{
driver->Close();
os_handle = -1;
return TRUE;
}
PString PEthSocket::GetName() const
{
return interfaceName;
}
BOOL PEthSocket::Connect(const PString & newName)
{
Close();
if (!driver->BindInterface(newName))
return SetErrorValues(Miscellaneous, driver->GetLastError()|PWIN32ErrorFlag);
interfaceName = newName;
os_handle = 1;
return TRUE;
}
BOOL PEthSocket::EnumInterfaces(PINDEX idx, PString & name)
{
return driver->EnumInterfaces(idx, name);
}
BOOL PEthSocket::GetAddress(Address & addr)
{
if (driver->QueryOid(OID_802_3_CURRENT_ADDRESS, sizeof(addr), addr.b))
return TRUE;
return SetErrorValues(Miscellaneous, driver->GetLastError()|PWIN32ErrorFlag);
}
BOOL PEthSocket::EnumIpAddress(PINDEX idx,
PIPSocket::Address & addr,
PIPSocket::Address & net_mask)
{
if (IsOpen()) {
if (driver->EnumIpAddress(idx, addr, net_mask))
return TRUE;
return SetErrorValues(NotFound, ENOENT);
}
return SetErrorValues(NotOpen, EBADF);
}
static const struct {
unsigned pwlib;
DWORD ndis;
} FilterMasks[] = {
{ PEthSocket::FilterDirected, NDIS_PACKET_TYPE_DIRECTED },
{ PEthSocket::FilterMulticast, NDIS_PACKET_TYPE_MULTICAST },
{ PEthSocket::FilterAllMulticast, NDIS_PACKET_TYPE_ALL_MULTICAST },
{ PEthSocket::FilterBroadcast, NDIS_PACKET_TYPE_BROADCAST },
{ PEthSocket::FilterPromiscuous, NDIS_PACKET_TYPE_PROMISCUOUS }
};
BOOL PEthSocket::GetFilter(unsigned & mask, WORD & type)
{
if (!IsOpen())
return SetErrorValues(NotOpen, EBADF);
DWORD filter = 0;
if (!driver->QueryOid(OID_GEN_CURRENT_PACKET_FILTER, filter))
return SetErrorValues(Miscellaneous, driver->GetLastError()|PWIN32ErrorFlag);
if (filter == 0)
return PEthSocket::FilterDirected;
mask = 0;
for (PINDEX i = 0; i < PARRAYSIZE(FilterMasks); i++) {
if ((filter&FilterMasks[i].ndis) != 0)
mask |= FilterMasks[i].pwlib;
}
type = (WORD)filterType;
return TRUE;
}
BOOL PEthSocket::SetFilter(unsigned filter, WORD type)
{
if (!IsOpen())
return SetErrorValues(NotOpen, EBADF);
DWORD bits = 0;
for (PINDEX i = 0; i < PARRAYSIZE(FilterMasks); i++) {
if ((filter&FilterMasks[i].pwlib) != 0)
bits |= FilterMasks[i].ndis;
}
if (!driver->SetOid(OID_GEN_CURRENT_PACKET_FILTER, bits))
return SetErrorValues(Miscellaneous, driver->GetLastError()|PWIN32ErrorFlag);
filterType = type;
return TRUE;
}
PEthSocket::MediumTypes PEthSocket::GetMedium()
{
if (!IsOpen()) {
SetErrorValues(NotOpen, EBADF);
return NumMediumTypes;
}
DWORD medium = 0xffffffff;
if (!driver->QueryOid(OID_GEN_MEDIA_SUPPORTED, medium) || medium == 0xffffffff) {
SetErrorValues(Miscellaneous, driver->GetLastError()|PWIN32ErrorFlag);
return NumMediumTypes;
}
static const DWORD MediumValues[NumMediumTypes] = {
0xffffffff, NdisMedium802_3, NdisMediumWan, 0xffffffff
};
for (int type = Medium802_3; type < NumMediumTypes; type++) {
if (MediumValues[type] == medium)
return (MediumTypes)type;
}
return MediumUnknown;
}
BOOL PEthSocket::Read(void * data, PINDEX length)
{
if (!IsOpen())
return SetErrorValues(NotOpen, EBADF, LastReadError);
PINDEX idx;
PINDEX numBuffers = readBuffers.GetSize();
do {
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
for (idx = 0; idx < numBuffers; idx++) {
PWin32PacketBuffer & buffer = readBuffers[idx];
if (buffer.InProgress()) {
if (WaitForSingleObject(buffer.GetEvent(), 0) == WAIT_OBJECT_0)
if (!buffer.ReadComplete(*driver))
return ConvertOSError(-1, LastReadError);
}
else {
if (!buffer.ReadAsync(*driver))
return ConvertOSError(-1, LastReadError);
}
if (buffer.IsCompleted() && buffer.IsType(filterType)) {
lastReadCount = buffer.GetData(data, length);
return TRUE;
}
handles[idx] = buffer.GetEvent();
}
DWORD result;
PINDEX retries = 100;
for (;;) {
result = WaitForMultipleObjects(numBuffers, handles, FALSE, INFINITE);
if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + (DWORD)numBuffers)
break;
if (::GetLastError() != ERROR_INVALID_HANDLE || retries == 0)
return ConvertOSError(-1, LastReadError);
retries--;
}
idx = result - WAIT_OBJECT_0;
if (!readBuffers[idx].ReadComplete(*driver))
return ConvertOSError(-1, LastReadError);
} while (!readBuffers[idx].IsType(filterType));
lastReadCount = readBuffers[idx].GetData(data, length);
return TRUE;
}
BOOL PEthSocket::Write(const void * data, PINDEX length)
{
if (!IsOpen())
return SetErrorValues(NotOpen, EBADF, LastWriteError);
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
PINDEX numBuffers = writeBuffers.GetSize();
PINDEX idx;
for (idx = 0; idx < numBuffers; idx++) {
PWin32PacketBuffer & buffer = writeBuffers[idx];
if (buffer.InProgress()) {
if (WaitForSingleObject(buffer.GetEvent(), 0) == WAIT_OBJECT_0)
if (!buffer.WriteComplete(*driver))
return ConvertOSError(-1, LastWriteError);
}
if (!buffer.InProgress()) {
lastWriteCount = buffer.PutData(data, length);
return ConvertOSError(buffer.WriteAsync(*driver) ? 0 : -1, LastWriteError);
}
handles[idx] = buffer.GetEvent();
}
DWORD result = WaitForMultipleObjects(numBuffers, handles, FALSE, INFINITE);
if (result < WAIT_OBJECT_0 || result >= WAIT_OBJECT_0 + (DWORD) numBuffers)
return ConvertOSError(-1, LastWriteError);
idx = result - WAIT_OBJECT_0;
if (!writeBuffers[idx].WriteComplete(*driver))
return ConvertOSError(-1, LastWriteError);
lastWriteCount = writeBuffers[idx].PutData(data, length);
return ConvertOSError(writeBuffers[idx].WriteAsync(*driver) ? 0 : -1, LastWriteError);
}
///////////////////////////////////////////////////////////////////////////////
PWin32PacketBuffer::PWin32PacketBuffer(PINDEX sz)
: PBYTEArray(sz)
{
status = Uninitialised;
count = 0;
}
PINDEX PWin32PacketBuffer::GetData(void * buf, PINDEX size)
{
if (count > (DWORD)size)
count = size;
memcpy(buf, theArray, count);
return count;
}
PINDEX PWin32PacketBuffer::PutData(const void * buf, PINDEX length)
{
count = min(GetSize(), length);
memcpy(theArray, buf, count);
return count;
}
BOOL PWin32PacketBuffer::ReadAsync(PWin32PacketDriver & pkt)
{
if (status == Progressing)
return FALSE;
status = Uninitialised;
if (!pkt.BeginRead(theArray, GetSize(), count, overlap))
return FALSE;
if (pkt.GetLastError() == ERROR_SUCCESS)
status = Completed;
else
status = Progressing;
return TRUE;
}
BOOL PWin32PacketBuffer::ReadComplete(PWin32PacketDriver & pkt)
{
if (status != Progressing)
return status == Completed;
if (!pkt.CompleteIO(count, overlap)) {
status = Uninitialised;
return FALSE;
}
status = Completed;
return TRUE;
}
BOOL PWin32PacketBuffer::WriteAsync(PWin32PacketDriver & pkt)
{
if (status == Progressing)
return FALSE;
status = Uninitialised;
if (!pkt.BeginWrite(theArray, count, overlap))
return FALSE;
if (pkt.GetLastError() == ERROR_SUCCESS)
status = Completed;
else
status = Progressing;
return TRUE;
}
BOOL PWin32PacketBuffer::WriteComplete(PWin32PacketDriver & pkt)
{
if (status != Progressing)
return status == Completed;
DWORD dummy;
if (pkt.CompleteIO(dummy, overlap)) {
status = Completed;
return TRUE;
}
status = Uninitialised;
return FALSE;
}
BOOL PWin32PacketBuffer::IsType(WORD filterType) const
{
if (filterType == PEthSocket::TypeAll)
return TRUE;
const PEthSocket::Frame * frame = (const PEthSocket::Frame *)theArray;
WORD len_or_type = ntohs(frame->snap.length);
if (len_or_type > sizeof(*frame))
return len_or_type == filterType;
if (frame->snap.dsap == 0xaa && frame->snap.ssap == 0xaa)
return ntohs(frame->snap.type) == filterType; // SNAP header
if (frame->snap.dsap == 0xff && frame->snap.ssap == 0xff)
return PEthSocket::TypeIPX == filterType; // Special case for Novell netware's stuffed up 802.3
if (frame->snap.dsap == 0xe0 && frame->snap.ssap == 0xe0)
return PEthSocket::TypeIPX == filterType; // Special case for Novell netware's 802.2
return frame->snap.dsap == filterType; // A pure 802.2 protocol id
}
///////////////////////////////////////////////////////////////////////////////
/*
static PMutex & GetSNMPMutex()
{
static PMutex snmpmutex;
return snmpmutex;
}
*/
BOOL PIPSocket::GetGatewayAddress(Address & addr)
{
PWaitAndSignal m(GetSNMPMutex());
PWin32SnmpLibrary & snmp = PWin32SnmpLibrary::Current();
PWin32AsnOid gatewayOid = "1.3.6.1.2.1.4.21.1.7.0.0.0.0";
return snmp.GetOid(gatewayOid, addr);
}
PString PIPSocket::GetGatewayInterface()
{
PWaitAndSignal m(GetSNMPMutex());
PWin32SnmpLibrary & snmp = PWin32SnmpLibrary::Current();
AsnInteger ifNum = -1;
PWin32AsnOid gatewayOid = "1.3.6.1.2.1.4.21.1.2.0.0.0.0";
if (!snmp.GetOid(gatewayOid, ifNum) && ifNum >= 0)
return PString::Empty();
return snmp.GetInterfaceName(ifNum);
}
PIPSocket::Address PIPSocket::GetGatewayInterfaceAddress()
{
PWaitAndSignal m(GetSNMPMutex());
PWin32SnmpLibrary & snmp = PWin32SnmpLibrary::Current();
AsnInteger ifNum = -1;
PWin32AsnOid gatewayOid = "1.3.6.1.2.1.4.21.1.2.0.0.0.0";
if (!snmp.GetOid(gatewayOid, ifNum) && ifNum >= 0)
return PString::Empty();
return snmp.GetInterfaceAddress(ifNum);
}
BOOL PIPSocket::GetRouteTable(RouteTable & table)
{
PWaitAndSignal m(GetSNMPMutex());
PWin32SnmpLibrary & snmp = snmp.Current();
table.RemoveAll();
PWin32AsnOid baseOid = "1.3.6.1.2.1.4.21.1";
PWin32AsnOid oid = baseOid;
DWORD lastVariable = 1;
PWin32AsnAny value;
PLongArray ifNum;
PINDEX idx = 0;
while (snmp.GetNextOid(oid, value) && (baseOid *= oid)) {
if (lastVariable != oid[9]) {
lastVariable = oid[9];
if (lastVariable == 2)
ifNum.SetSize(table.GetSize());
idx = 0;
}
switch (lastVariable) {
case 1 : // network address
{
Address addr;
if (!value.GetIpAddress(addr))
return FALSE; // Very confused route table
table.Append(new RouteEntry(addr));
break;
}
case 2 : // device interface
if (!value.GetInteger(ifNum[idx]))
return FALSE;
break;
case 3 : // metric
if (!value.GetInteger(table[idx].metric))
return FALSE;
break;
case 7 : // Get destination (next hop)
if (!value.GetIpAddress(table[idx].destination))
return FALSE;
break;
case 11 : // Get mask
if (!value.GetIpAddress(table[idx].net_mask))
return FALSE;
break;
}
idx++;
}
for (idx = 0; idx < table.GetSize(); idx++)
table[idx].interfaceName = snmp.GetInterfaceName(ifNum[idx]);
return TRUE;
}
unsigned PIPSocket::AsNumeric(PIPSocket::Address addr)
{
return ((addr.Byte1() << 24) | (addr.Byte2() << 16) |
(addr.Byte3() << 8) | addr.Byte4());
}
PIPSocket::Address PIPSocket::GetRouteAddress(PIPSocket::Address RemoteAddress)
{
Address localaddr;
if (!RemoteAddress.IsRFC1918()) { // Remote Address is not Local
if (!GetNetworkInterface(localaddr)) { // User not connected directly to Internet
localaddr = GetGatewayInterfaceAddress(); // Get the default Gateway NIC address
if ( localaddr != 0 ) // No connection to the Internet?
return localaddr;
}
} else {
PIPSocket::InterfaceTable interfaceTable;
if (PIPSocket::GetInterfaceTable(interfaceTable)) {
PINDEX i;
for (i = 0; i < interfaceTable.GetSize(); ++i) {
localaddr = interfaceTable[i].GetAddress();
if (!localaddr.IsLoopback() && localaddr.IsRFC1918()) {
if (IsAddressReachable(localaddr,
interfaceTable[i].GetNetMask(),RemoteAddress))
return localaddr;
}
}
}
}
return 0;
}
BOOL PIPSocket::IsAddressReachable(PIPSocket::Address LocalIP,
PIPSocket::Address LocalMask,
PIPSocket::Address RemoteIP)
{
BYTE t = 255;
int t1=t,t2=t,t3 =t,t4=t;
int b1=0,b2=0,b3=0,b4=0;
if ((int)LocalMask.Byte1() > 0)
{ t1 = LocalIP.Byte1() + (t-LocalMask.Byte1()); b1 = LocalIP.Byte1();}
if ((int)LocalMask.Byte2() > 0)
{ t2 = LocalIP.Byte2() + (t-LocalMask.Byte2()); b2 = LocalIP.Byte2();}
if ((int)LocalMask.Byte3() > 0)
{ t3 = LocalIP.Byte3() + (t-LocalMask.Byte3()); b3 = LocalIP.Byte3();}
if ((int)LocalMask.Byte4() > 0)
{ t4 = LocalIP.Byte4() + (t-LocalMask.Byte4()); b4 = LocalIP.Byte4();}
Address lt = Address((BYTE)t1,(BYTE)t2,(BYTE)t3,(BYTE)t4);
Address lb = Address((BYTE)b1,(BYTE)b2,(BYTE)b3,(BYTE)b4);
if (AsNumeric(RemoteIP) > AsNumeric(lb) &&
AsNumeric(lt) > AsNumeric(RemoteIP))
return TRUE;
return FALSE;
}
PString PIPSocket::GetInterface(PIPSocket::Address addr)
{
PIPSocket::InterfaceTable if_table;
if (PIPSocket::GetInterfaceTable( if_table ) ) {
for (PINDEX i=0; i < if_table.GetSize(); i++) {
PIPSocket::InterfaceEntry if_entry = if_table[i];
if (if_entry.GetAddress() == addr)
return if_entry.GetName();
}
}
return PString();
}
BOOL PIPSocket::GetInterfaceTable(InterfaceTable & table)
{
PWin32SnmpLibrary & snmp = snmp.Current();
PWaitAndSignal m(GetSNMPMutex());
table.RemoveAll();
/*
if (!snmp.IsLoaded()) {
// Error loading the SNMP library, fail safe to using whatever the
// address of the local host is.
Address ipAddr;
if (!GetHostAddress(ipAddr))
return FALSE;
Address netMask(255,255,255,255);
table.Append(new InterfaceEntry("FailSafe Interface", ipAddr, netMask, PString::Empty()));
table.Append(new InterfaceEntry("localhost", PIPSocket::Address(), netMask, PString::Empty()));
return TRUE;
}
*/
PWin32AsnOid baseOid = "1.3.6.1.2.1.4.20.1";
PWin32AsnOid oid = baseOid;
PWin32AsnAny value;
while (snmp.GetNextOid(oid, value)) {
if (!(baseOid *= oid))
break;
if (value.asnType != ASN_IPADDRESS)
break;
Address ipAddr;
value.GetIpAddress(ipAddr);
oid[9] = 3;
Address netMask;
if (!snmp.GetOid(oid, netMask))
break;
oid[9] = 2;
AsnInteger ifIndex = -1;
if (!snmp.GetOid(oid, ifIndex))
break;
PString macAddr;
PEthSocket::Address ifPhysAddress("");
PWin32AsnOid ifOid = "1.3.6.1.2.1.2.2.1.6.0";
ifOid[10] = ifIndex;
UINT len;
if (snmp.GetOid(ifOid, &ifPhysAddress, sizeof(ifPhysAddress), &len) && len > 0)
macAddr = ifPhysAddress;
PString name = snmp.GetInterfaceName(ipAddr);
if (name.IsEmpty()) {
PWin32AsnOid nameOid = "1.3.6.1.2.1.2.2.1.2.0";
nameOid[10] = ifIndex;
if (!snmp.GetOid(nameOid, name))
break;
name.MakeMinimumSize();
}
#ifdef _WIN32_WCE // Getting rid of ghost ips
if ( !name.IsEmpty() )
#endif
table.Append(new InterfaceEntry(name, ipAddr, netMask, macAddr));
oid[9] = 1;
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1