/* ** Copyright 2003-2009, Ernest Laurentin (http://www.ernzo.com/) ** ** Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. ** ** File: SocketHandle.cpp ** Version: 1.4 - IPv6 support ** 1.3 - Update for Asynchronous mode / Linux port ** 1.2 - Update interface for TCP remote connection ** 1.1 - Added multicast support */ #include "stdafx.h" #ifdef WIN32 #include #ifndef UNDER_CE #include #endif #include #endif #include "SocketHandle.h" // Jeff.add. //#include "Wspiapi.h" //#include "Ws2tcpip.h" #ifndef BUFFER_SIZE #define BUFFER_SIZE 64*1024 #endif #ifndef SOCKHANDLE_TTL #define SOCKHANDLE_TTL 5 #endif #ifndef SOCKHANDLE_HOPS #define SOCKHANDLE_HOPS 10 #endif #define HOSTNAME_SIZE MAX_PATH #define STRING_LENGTH 40 #if !defined(PLATFORM_HAS_INETFUNC) const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); int inet_pton(int af, const char *src, void *dst); #endif #ifdef WIN32 #ifndef UNDER_CE #pragma comment(lib, "ws2_32.lib") #else #pragma comment(lib, "Ws2.lib") #endif #endif /////////////////////////////////////////////////////////////////////////////// // SockAddrIn Struct SockAddrIn SockAddrIn::NULLAddr; /////////////////////////////////////////////////////////////////////////////// // Constructs SockAddrIn::SockAddrIn() { Clear(); } SockAddrIn::SockAddrIn(const SockAddrIn& sin) { Copy( sin ); } SockAddrIn::~SockAddrIn() { } /////////////////////////////////////////////////////////////////////////////// // Clear void SockAddrIn::Clear() { memset(this, 0, sizeof(sockaddr_storage)); } /////////////////////////////////////////////////////////////////////////////// // Copy SockAddrIn& SockAddrIn::Copy(const SockAddrIn& sin) { ss_family = sin.ss_family; memcpy(this, &sin, Size()); return *this; } /////////////////////////////////////////////////////////////////////////////// // IsEqual bool SockAddrIn::IsEqual(const SockAddrIn& sin) const { // Is it Equal? - ignore 'sin_zero' if ( ss_family == AF_INET ) { return (memcmp(this, &sin, Size()-8) == 0); } return (memcmp(this, &sin, Size()) == 0); } /////////////////////////////////////////////////////////////////////////////// // CreateFrom bool SockAddrIn::CreateFrom(LPCTSTR pszAddr, LPCTSTR pszService, int nFamily /*=AF_INET*/) { Clear(); CSocketHandle::GetAddressInfo(pszAddr, pszService, nFamily, *this); return !IsNull(); } /////////////////////////////////////////////////////////////////////////////// // CreateFrom bool SockAddrIn::CreateFrom(ULONG lIPAddr, USHORT nPort, int nFamily /*= AF_INET*/, bool bFmtHost /*= true*/) { Clear(); _ASSERTE( nFamily == AF_INET ); // supports IPv4 only SOCKADDR_IN* psin = reinterpret_cast(this); psin->sin_family = static_cast(nFamily); if ( bFmtHost ) { psin->sin_addr.s_addr = htonl( lIPAddr ); psin->sin_port = htons( nPort ); } else { psin->sin_addr.s_addr = lIPAddr; psin->sin_port = nPort; } return !IsNull(); } /////////////////////////////////////////////////////////////////////////////// // CSocketHandle CSocketHandle::CSocketHandle() : m_hSocket(INVALID_SOCKET) { m_npendingSize = 0; memset(m_szpendingbuf,0,SOCKET_BUFFSIZE); } CSocketHandle::~CSocketHandle() { Close(); } /////////////////////////////////////////////////////////////////////////////// // IsOpen bool CSocketHandle::IsOpen() const { return ( INVALID_SOCKET != m_hSocket ); } /////////////////////////////////////////////////////////////////////////////// // GetSocket SOCKET CSocketHandle::GetSocket() const { return m_hSocket; } /////////////////////////////////////////////////////////////////////////////// // GetSocketType int CSocketHandle::GetSocketType() const { int type = -1; if ( INVALID_SOCKET != m_hSocket ) { socklen_t optlen = sizeof(int); if ( getsockopt(GetSocket(), SOL_SOCKET, SO_TYPE, reinterpret_cast(&type), &optlen) == SOCKET_ERROR) { SetLastError( WSAGetLastError() ); } } return type; } /////////////////////////////////////////////////////////////////////////////// // Attach bool CSocketHandle::Attach(SOCKET sock) { if ( INVALID_SOCKET == m_hSocket ) { m_hSocket = sock; return true; } return false; } /////////////////////////////////////////////////////////////////////////////// // Detach SOCKET CSocketHandle::Detach() { SOCKET sock = m_hSocket; ::InterlockedExchange(reinterpret_cast(&m_hSocket), INVALID_SOCKET); return sock; } /////////////////////////////////////////////////////////////////////////////// // GetSockName bool CSocketHandle::GetSockName(SockAddrIn& saddr_in) const { _ASSERTE( IsOpen() ); if (IsOpen()) { socklen_t namelen = (socklen_t)saddr_in.Size(); if (SOCKET_ERROR != getsockname(GetSocket(), saddr_in, &namelen)) { return true; } SetLastError( WSAGetLastError() ); } return false; } /////////////////////////////////////////////////////////////////////////////// // GetPeerName bool CSocketHandle::GetPeerName(SockAddrIn& saddr_in) const { _ASSERTE( IsOpen() ); if (IsOpen()) { socklen_t namelen = (socklen_t)saddr_in.Size(); if (SOCKET_ERROR != getpeername(GetSocket(), saddr_in, &namelen)) { return true; } SetLastError( WSAGetLastError() ); } return false; } /////////////////////////////////////////////////////////////////////////////// // Close void CSocketHandle::Close() { if ( IsOpen() ) { ShutdownConnection(static_cast( ::InterlockedExchange((LONG*)&m_hSocket, INVALID_SOCKET) )); } } /////////////////////////////////////////////////////////////////////////////// // AddMembership bool CSocketHandle::AddMembership(LPCTSTR pszIPAddr, LPCTSTR pszNIC) { _ASSERTE( IsOpen() ); if ( IsOpen() ) { int nType = 0; socklen_t nOptLen = sizeof(int); if ( SOCKET_ERROR != getsockopt(m_hSocket, SOL_SOCKET, SO_TYPE, (char*)&nType, &nOptLen)) { if ( nType == SOCK_DGRAM ) { // Setup interface for multicast traffic SockAddrIn mcastAddr; if (GetAddressInfo(pszIPAddr, NULL, AF_UNSPEC, mcastAddr)) { SockAddrIn interfAddr; GetAddressInfo(pszNIC, NULL, mcastAddr.ss_family, interfAddr); if ( mcastAddr.ss_family == AF_INET ) { int nTTL = SOCKHANDLE_TTL; if ( SOCKET_ERROR != setsockopt(m_hSocket, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&nTTL, sizeof(nTTL))) { ULONG ulNIC = interfAddr.GetIPAddr(); if ( SOCKET_ERROR != setsockopt(m_hSocket, IPPROTO_IP, IP_MULTICAST_IF,(char *) &ulNIC, sizeof(ulNIC))) { ip_mreq mreq = { 0 }; mreq.imr_multiaddr.s_addr = mcastAddr.GetIPAddr(); mreq.imr_interface.s_addr = ulNIC; if ( SOCKET_ERROR != setsockopt(m_hSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq))) { return true; } } } } else if ( mcastAddr.ss_family == AF_INET6 ) { int nTTL = SOCKHANDLE_HOPS; if ( SOCKET_ERROR != setsockopt(m_hSocket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&nTTL, sizeof(nTTL))) { ipv6_mreq mreq6 = { 0 }; IN6_ADDR mcin6 = ((sockaddr_in6*)&mcastAddr)->sin6_addr; memcpy(&(mreq6.ipv6mr_multiaddr), &mcin6, sizeof(IN6_ADDR)); if ( SOCKET_ERROR != setsockopt(m_hSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6))) { return true; } } } else { // invalid socket option WSASetLastError(WSAENOPROTOOPT); } } } else { // invalid socket option WSASetLastError(WSAENOPROTOOPT); } } SetLastError( WSAGetLastError() ); } return false; } /////////////////////////////////////////////////////////////////////////////// // DropMembership bool CSocketHandle::DropMembership(LPCTSTR pszIPAddr, LPCTSTR pszNIC) { _ASSERTE( IsOpen() ); if ( IsOpen() ) { int nType = 0; socklen_t nOptLen = sizeof(int); if ( SOCKET_ERROR != getsockopt(m_hSocket, SOL_SOCKET, SO_TYPE, (char*)&nType, &nOptLen)) { if ( nType == SOCK_DGRAM ) { SockAddrIn mcastAddr; if (GetAddressInfo(pszIPAddr, NULL, AF_UNSPEC, mcastAddr)) { SockAddrIn interfAddr; GetAddressInfo(pszNIC, NULL, mcastAddr.ss_family, interfAddr); if ( mcastAddr.ss_family == AF_INET ) { ip_mreq mreq; mreq.imr_multiaddr.s_addr = mcastAddr.GetIPAddr(); mreq.imr_interface.s_addr = interfAddr.GetIPAddr();; if ( SOCKET_ERROR != setsockopt(m_hSocket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq))) { return true; } } else if ( mcastAddr.ss_family == AF_INET6 ) { ipv6_mreq mreq6 = { 0 }; IN6_ADDR mcin6 = ((sockaddr_in6*)&mcastAddr)->sin6_addr; memcpy(&(mreq6.ipv6mr_multiaddr), &mcin6, sizeof(IN6_ADDR)); if ( SOCKET_ERROR != setsockopt(m_hSocket, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6))) { return true; } } else { // invalid socket option WSASetLastError(WSAENOPROTOOPT); } } } else { // invalid socket option WSASetLastError(WSAENOPROTOOPT); } } SetLastError( WSAGetLastError() ); } return false; } /////////////////////////////////////////////////////////////////////////////// // CreateSocket bool CSocketHandle::CreateSocket(LPCTSTR pszHostName, LPCTSTR pszServiceName, int nFamily, int nType, UINT uOptions /* = 0 */) { // Socket is already opened if ( IsOpen() ) { SetLastError(ERROR_ACCESS_DENIED); return false; } // Create a Socket that is bound to a specific service provider // nFamily: (AF_INET, AF_INET6) // nType: (SOCK_STREAM, SOCK_DGRAM) #ifdef SOCKHANDLE_USE_OVERLAPPED SOCKET sock = WSASocket(nFamily, nType, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED); #else SOCKET sock = socket(nFamily, nType, IPPROTO_IP); #endif if (INVALID_SOCKET != sock) { if (uOptions & SO_REUSEADDR) { // Inform Windows Sockets provider that a bind on a socket should not be disallowed // because the desired address is already in use by another socket BOOL optval = TRUE; if ( SOCKET_ERROR == setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof( BOOL ) ) ) { SetLastError( WSAGetLastError() ); closesocket( sock ); return false; } } if (nType == SOCK_DGRAM) { if ((uOptions & SO_BROADCAST) && (nFamily == AF_INET)) { // Inform Windows Sockets provider that broadcast messages are allowed BOOL optval = TRUE; if ( SOCKET_ERROR == setsockopt( sock, SOL_SOCKET, SO_BROADCAST, (char *) &optval, sizeof( BOOL ) ) ) { SetLastError( WSAGetLastError() ); closesocket( sock ); return false; } } #ifdef SOCKHANDLE_CONFIGBUF // configure buffer size socklen_t rcvbuf = BUFFER_SIZE; if ( SOCKET_ERROR == setsockopt( sock, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbuf, sizeof( int ) ) ) { SetLastError( WSAGetLastError() ); closesocket( sock ); return false; } #endif } // Associate a local address with the socket SockAddrIn sockAddr; sockAddr.CreateFrom(pszHostName, pszServiceName, nFamily); if ( SOCKET_ERROR == bind(sock, sockAddr, (int)sockAddr.Size())) { SetLastError( WSAGetLastError() ); closesocket( sock ); return false; } // Listen to the socket, only valid for connection socket (TCP) if (SOCK_STREAM == nType) { if ( SOCKET_ERROR == listen(sock, SOMAXCONN)) { SetLastError( WSAGetLastError() ); closesocket( sock ); return false; } } // Success, now we may save this socket m_hSocket = sock; } return (INVALID_SOCKET != sock); } /////////////////////////////////////////////////////////////////////////////// // ConnectTo bool CSocketHandle::ConnectTo(LPCTSTR pszHostName, LPCTSTR pszRemote, LPCTSTR pszServiceName, int nFamily, int nType) { // Socket is already opened if ( IsOpen() ) { SetLastError(ERROR_ACCESS_DENIED); return false; } // Create a Socket that is bound to a specific service provider // nFamily: (AF_INET, AF_INET6) // nType: (SOCK_STREAM, SOCK_DGRAM) #ifdef SOCKHANDLE_USE_OVERLAPPED SOCKET sock = WSASocket(nFamily, nType, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED); #else SOCKET sock = socket(nFamily, nType, IPPROTO_IP); #endif if (INVALID_SOCKET != sock) { // Associate a local address with the socket but let provider assign a port number SockAddrIn sockAddr; if (false == sockAddr.CreateFrom(pszHostName, TEXT("0"), nFamily)) { SetLastError( WSAGetLastError() ); closesocket( sock ); return false; } if ( SOCKET_ERROR == bind(sock, sockAddr, (int)sockAddr.Size())) { SetLastError( WSAGetLastError() ); closesocket( sock ); return false; } #ifdef SOCKHANDLE_CONFIGBUF if (nType == SOCK_DGRAM) { // configure buffer size socklen_t rcvbuf = BUFFER_SIZE; if ( SOCKET_ERROR == setsockopt( sock, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbuf, sizeof( int ) ) ) { SetLastError( WSAGetLastError() ); closesocket( sock ); return false; } } #endif // Now get destination address & port sockAddr.CreateFrom( pszRemote, pszServiceName, nFamily ); // try to connect - if fail, server not ready if (SOCKET_ERROR == connect( sock, sockAddr, (int)sockAddr.Size())) { SetLastError( WSAGetLastError() ); closesocket( sock ); return false; } // Success, now we may save this socket m_hSocket = sock; } return (INVALID_SOCKET != sock); } /////////////////////////////////////////////////////////////////////////////// // Read DWORD CSocketHandle::Read(LPBYTE lpBuffer, DWORD dwSize, LPSOCKADDR lpAddrIn, DWORD dwTimeout) { _ASSERTE( IsOpen() ); _ASSERTE( lpBuffer != NULL ); if (!IsOpen() || lpBuffer == NULL || dwSize < 1L) return (DWORD)-1L; fd_set fdRead = { 0 }; TIMEVAL stTime; TIMEVAL *pstTime = NULL; if ( INFINITE != dwTimeout ) { stTime.tv_sec = dwTimeout/1000; stTime.tv_usec = (dwTimeout%1000)*1000; pstTime = &stTime; } SOCKET s = GetSocket(); // Set Descriptor FD_SET( s, &fdRead ); // Select function set read timeout DWORD dwBytesRead = 0L; int res = 1; if ( pstTime != NULL ) res = select((int)s, &fdRead, NULL, NULL, pstTime ); if ( res > 0) { if (lpAddrIn) { // UDP socklen_t fromlen = sizeof(SOCKADDR_STORAGE); res = recvfrom(s, reinterpret_cast(lpBuffer), dwSize, 0, lpAddrIn, &fromlen); } else { // TCP res = recv(s, reinterpret_cast(lpBuffer), dwSize, 0); } if ( res == 0 ) { WSASetLastError(WSAECONNRESET); res = SOCKET_ERROR; } } if ( res == SOCKET_ERROR ) { SetLastError( WSAGetLastError() ); } dwBytesRead = (DWORD)((res >= 0)?(res) : (-1)); return dwBytesRead; } #ifdef WIN32 /////////////////////////////////////////////////////////////////////////////// // ReadEx DWORD CSocketHandle::ReadEx(LPBYTE lpBuffer, DWORD dwSize, LPSOCKADDR lpAddrIn, LPWSAOVERLAPPED lpOverlapped, LPWSACOMPLETIONROUTINE lpCompletionRoutine) { _ASSERTE( IsOpen() ); _ASSERTE( lpBuffer != NULL ); if (!IsOpen() || lpBuffer == NULL || dwSize < 1L) return (DWORD)-1L; SOCKET s = GetSocket(); // Send message to peer WSABUF wsabuf; wsabuf.buf = (char FAR*)lpBuffer; wsabuf.len = dwSize; // Select function set read timeout DWORD dwBytesRead = 0L; DWORD dwFlags = 0L; int res = 0; if (lpAddrIn) { // UDP socklen_t fromlen = sizeof(SOCKADDR_STORAGE); res = WSARecvFrom( s, &wsabuf, 1, &dwBytesRead, &dwFlags, lpAddrIn, &fromlen, lpOverlapped, lpCompletionRoutine); } else { // TCP res = WSARecv( s, &wsabuf, 1, &dwBytesRead, &dwFlags, lpOverlapped, lpCompletionRoutine); } if ( res == SOCKET_ERROR ) { res = WSAGetLastError(); if ( res != WSA_IO_PENDING ) { dwBytesRead = (DWORD)-1L; SetLastError( res ); } } return dwBytesRead; } #endif /////////////////////////////////////////////////////////////////////////////// // Write DWORD CSocketHandle::Write(const LPBYTE lpBuffer, DWORD dwCount, const LPSOCKADDR lpAddrIn, DWORD dwTimeout) { _ASSERTE( IsOpen() ); _ASSERTE( NULL != lpBuffer ); // validate params if (!IsOpen() || NULL == lpBuffer) return (DWORD)-1L; fd_set fdWrite = { 0 }; TIMEVAL stTime; TIMEVAL *pstTime = NULL; if ( INFINITE != dwTimeout ) { stTime.tv_sec = dwTimeout/1000; stTime.tv_usec = (dwTimeout%1000)*1000; pstTime = &stTime; } SOCKET s = GetSocket(); // Set Descriptor FD_SET( s, &fdWrite ); // Select function set write timeout DWORD dwBytesWritten = 0L; int res = 1; if ( pstTime != NULL ) res = select((int)s, NULL, &fdWrite, NULL, pstTime ); if ( res > 0) { // Send message to peer if (lpAddrIn) { // UDP res = sendto( s, reinterpret_cast(lpBuffer), dwCount, 0, lpAddrIn, sizeof(SOCKADDR_STORAGE)); } else { // TCP res = send( s, reinterpret_cast(lpBuffer), dwCount, 0); } } if ( res == SOCKET_ERROR ) { SetLastError( WSAGetLastError() ); } dwBytesWritten = (DWORD)((res >= 0)?(res) : (-1)); return dwBytesWritten; } #ifdef WIN32 /////////////////////////////////////////////////////////////////////////////// // WriteEx DWORD CSocketHandle::WriteEx(const LPBYTE lpBuffer, DWORD dwCount, const LPSOCKADDR lpAddrIn, LPWSAOVERLAPPED lpOverlapped, LPWSACOMPLETIONROUTINE lpCompletionRoutine) { _ASSERTE( IsOpen() ); _ASSERTE( NULL != lpBuffer ); // validate params if (!IsOpen() || NULL == lpBuffer) return (DWORD)-1L; SOCKET s = GetSocket(); // Select function set write timeout DWORD dwBytesWritten = 0L; int res = 0; // Send message to peer WSABUF wsabuf; wsabuf.buf = (char FAR*) lpBuffer; wsabuf.len = dwCount; if (lpAddrIn) { // UDP res = WSASendTo( s, &wsabuf, 1, &dwBytesWritten, 0, lpAddrIn, sizeof(SOCKADDR_STORAGE), lpOverlapped, lpCompletionRoutine); } else // TCP res = WSASend( s, &wsabuf, 1, &dwBytesWritten, 0, lpOverlapped, lpCompletionRoutine); if ( res == SOCKET_ERROR ) { res = WSAGetLastError(); if ( res != WSA_IO_PENDING ) { dwBytesWritten = (DWORD)-1L; SetLastError( res ); } } return dwBytesWritten; } #endif #ifdef WIN32 /////////////////////////////////////////////////////////////////////////////// // IOControl bool CSocketHandle::IOControl(DWORD dwIoCode, LPBYTE lpInBuffer, DWORD cbInBuffer, LPBYTE lpOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, LPWSAOVERLAPPED lpOverlapped, LPWSACOMPLETIONROUTINE lpCompletionRoutine) { _ASSERTE( IsOpen() ); // validate params if ( !IsOpen() ) { SetLastError(ERROR_INVALID_HANDLE); return false; } int res; SOCKET s = GetSocket(); res = WSAIoctl(s, dwIoCode, lpInBuffer, cbInBuffer, lpOutBuffer, cbOutBuffer, lpcbBytesReturned, lpOverlapped, lpCompletionRoutine); if ( res == SOCKET_ERROR ) { SetLastError( WSAGetLastError() ); } return ( res != SOCKET_ERROR ); } /////////////////////////////////////////////////////////////////////////////// // GetTransferOverlappedResult bool CSocketHandle::GetTransferOverlappedResult(LPWSAOVERLAPPED lpOverlapped, LPDWORD lpcbTransfer, bool bWait /*= true*/, LPDWORD lpdwFlags /*= NULL*/) { _ASSERTE( IsOpen() ); _ASSERTE( NULL != lpOverlapped ); // validate params if (!IsOpen() || NULL == lpOverlapped) { SetLastError(ERROR_INVALID_HANDLE); return false; } SOCKET s = GetSocket(); DWORD dwFlags = 0; if ( lpdwFlags == NULL ) lpdwFlags = &dwFlags; BOOL bRet = WSAGetOverlappedResult( s, lpOverlapped, lpcbTransfer, bWait, lpdwFlags ); if ( !bRet ) { SetLastError( WSAGetLastError() ); } return (bRet != FALSE); } #endif /////////////////////////////////////////////////////////////////////////////// // Utility functions /////////////////////////////////////////////////////////////////////////////// // InitLibrary bool CSocketHandle::InitLibrary(WORD wVersion) { #ifdef WIN32 WSADATA WSAData = { 0 }; return ( 0 == WSAStartup( wVersion, &WSAData ) ); #else return true; #endif } /////////////////////////////////////////////////////////////////////////////// // ReleaseLibrary bool CSocketHandle::ReleaseLibrary() { #ifdef WIN32 return ( 0 == WSACleanup() ); #else return true; #endif } /////////////////////////////////////////////////////////////////////////////// // WaitForConnection SOCKET CSocketHandle::WaitForConnection(SOCKET sock) { return accept(sock, 0, 0); } /////////////////////////////////////////////////////////////////////////////// // ShutdownConnection bool CSocketHandle::ShutdownConnection(SOCKET sock) { shutdown(sock, SD_BOTH); return ( 0 == closesocket( sock )); } static unsigned char chMinClassA_IP [] = { 1, 0, 0, 0 } ; static unsigned char chMinClassD_IP [] = { 224, 0, 0, 0 } ; static unsigned char chMaxClassD_IP [] = { 239, 255, 255, 255 } ; /////////////////////////////////////////////////////////////////////////////// // IsUnicastIP bool CSocketHandle::IsUnicastIP( ULONG ulAddr ) { return (((unsigned char *) & ulAddr) [0] >= chMinClassA_IP [0] && ((unsigned char *) & ulAddr) [0] < chMinClassD_IP [0]) ; } /////////////////////////////////////////////////////////////////////////////// // IsMulticastIP bool CSocketHandle::IsMulticastIP( ULONG ulAddr ) { return (((unsigned char *) & ulAddr) [0] >= chMinClassD_IP [0] && ((unsigned char *) & ulAddr) [0] <= chMaxClassD_IP [0]) ; } /////////////////////////////////////////////////////////////////////////////// // FormatIP bool CSocketHandle::FormatIP(LPTSTR pszIPAddr, UINT nSize, ULONG ulAddr, bool bFmtHost) { if ( pszIPAddr && nSize > 8) { if ( bFmtHost ) ulAddr = htonl( ulAddr ); // Create Address string return (SUCCEEDED(StringCchPrintf(pszIPAddr, nSize, TEXT("%u.%u.%u.%u"), (UINT)(((PBYTE) &ulAddr)[0]), (UINT)(((PBYTE) &ulAddr)[1]), (UINT)(((PBYTE) &ulAddr)[2]), (UINT)(((PBYTE) &ulAddr)[3])))); } SetLastError(ERROR_INSUFFICIENT_BUFFER); return false; } /////////////////////////////////////////////////////////////////////////////// // FormatIP bool CSocketHandle::FormatIP(LPTSTR pszIPAddr, UINT nSize, const SockAddrIn& addrIn) { if ( pszIPAddr && nSize > 8) { const void* addr; char szIPAddr[MAX_PATH] = { 0 }; if (addrIn.ss_family == AF_INET) { addr = &((const sockaddr_in*)&addrIn)->sin_addr; } else { addr = &((const sockaddr_in6*)&addrIn)->sin6_addr; } if (inet_ntop(addrIn.ss_family, addr, szIPAddr, MAX_PATH) != NULL) { #ifdef _UNICODE return (0 != MultiByteToWideChar(CP_UTF8, 0, szIPAddr, -1, pszIPAddr, nSize )); #else ::StringCbCopyA(pszIPAddr, nSize, szIPAddr); return true; #endif } } SetLastError(ERROR_INSUFFICIENT_BUFFER); return false; } /////////////////////////////////////////////////////////////////////////////// // GetPortNumber USHORT CSocketHandle::GetPortNumber( LPCTSTR pszServiceName ) { LPSERVENT lpservent; USHORT nPort = 0; if ( _istdigit( pszServiceName[0] ) ) { nPort = (USHORT) _ttoi( pszServiceName ); } else { #ifdef _UNICODE char pstrService[HOSTNAME_SIZE] = { 0 }; WideCharToMultiByte(CP_UTF8, 0, pszServiceName, -1, pstrService, sizeof(pstrService), NULL, NULL ); #else LPCTSTR pstrService = pszServiceName; #endif // Convert network byte order to host byte order if ( (lpservent = getservbyname( pstrService, NULL )) != NULL ) nPort = ntohs( lpservent->s_port ); } return nPort; } /////////////////////////////////////////////////////////////////////////////// // GetIPAddress ULONG CSocketHandle::GetIPAddress( LPCTSTR pszHostName ) { LPHOSTENT lphostent; ULONG uAddr = INADDR_NONE; TCHAR szLocal[HOSTNAME_SIZE] = { 0 }; // if no name specified, get local if ( NULL == pszHostName || !pszHostName[0]) { GetLocalName(szLocal, HOSTNAME_SIZE); pszHostName = szLocal; } #ifdef _UNICODE char pstrHost[HOSTNAME_SIZE] = { 0 }; WideCharToMultiByte(CP_UTF8, 0, pszHostName, -1, pstrHost, sizeof(pstrHost), NULL, NULL ); #else LPCTSTR pstrHost = pszHostName; #endif // Check for an Internet Protocol dotted address string uAddr = inet_addr( pstrHost ); if ( (INADDR_NONE == uAddr) && (strcmp( pstrHost, "255.255.255.255" )) ) { // It's not an address, then try to resolve it as a hostname if ( (lphostent = gethostbyname( pstrHost )) != NULL ) uAddr = *((ULONG *) lphostent->h_addr_list[0]); } return ntohl( uAddr ); } /////////////////////////////////////////////////////////////////////////////// // GetLocalName bool CSocketHandle::GetLocalName(LPTSTR pszName, UINT nSize) { if (pszName != NULL && nSize > 0) { char szHost[HOSTNAME_SIZE] = { 0 }; // get host name, if fail, SetLastError is set if (SOCKET_ERROR != gethostname(szHost, sizeof(szHost))) { struct hostent* hp; hp = gethostbyname(szHost); if (hp != NULL) { ::StringCbCopyA(szHost, HOSTNAME_SIZE, hp->h_name); } // check if user provide enough buffer size_t cbLength = 0; ::StringCbLengthA(szHost, HOSTNAME_SIZE, &cbLength); if ( cbLength > nSize ) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return false; } // Unicode conversion #ifdef _UNICODE return (0 != MultiByteToWideChar(CP_UTF8, 0, szHost, -1, pszName, nSize )); #else ::StringCbCopyA(pszName, nSize, szHost); return true; #endif } else SetLastError( WSAGetLastError() ); } else SetLastError(ERROR_INVALID_PARAMETER); return false; } /////////////////////////////////////////////////////////////////////////////// // GetLocalAddress bool CSocketHandle::GetLocalAddress(LPTSTR pszAddress, UINT nSize, int nFamily /*= AF_INET*/) { if (pszAddress != NULL && nSize > 0) { TCHAR szHost[HOSTNAME_SIZE] = { 0 }; // Get computer local address // get host name, if fail, SetLastError is set if (GetLocalName(szHost, HOSTNAME_SIZE)) { char szAddress[MAX_PATH] = { 0 }; #ifdef _UNICODE char pstrHost[HOSTNAME_SIZE] = { 0 }; WideCharToMultiByte(CP_UTF8, 0, szHost, -1, pstrHost, sizeof(pstrHost), NULL, NULL ); #else LPCTSTR pstrHost = szHost; #endif // get address info sockaddr_storage addr_store = { 0 }; addr_store.ss_family = static_cast(nFamily); inet_pton(nFamily, pstrHost, &addr_store); const void* addr; if (addr_store.ss_family == AF_INET) { addr = &((const sockaddr_in*)&addr_store)->sin_addr; } else { addr = &((const sockaddr_in6*)&addr_store)->sin6_addr; } if (inet_ntop(addr_store.ss_family, addr, szAddress, MAX_PATH) != NULL) { // Unicode conversion #ifdef _UNICODE return (0 != MultiByteToWideChar(CP_UTF8, 0, szAddress, -1, pszAddress, nSize )); #else ::StringCbCopyA(pszAddress, nSize, szAddress); return true; #endif } else SetLastError( WSAGetLastError() ); } } else SetLastError(ERROR_INVALID_PARAMETER); return false; } /////////////////////////////////////////////////////////////////////////////// // GetAddressInfo bool CSocketHandle::GetAddressInfo(LPCTSTR pszHostName, LPCTSTR pszServiceName, int nFamily, SockAddrIn& sockAddr) { const TCHAR szZERO[] = TEXT("0"); ADDRINFO aiHints; ADDRINFO *aiList = NULL; memset(&aiHints, 0, sizeof(aiHints)); aiHints.ai_flags = AI_ADDRCONFIG; // Jeff.ÔÝʱ½ûµô; aiHints.ai_family = static_cast(nFamily); TCHAR szLocal[HOSTNAME_SIZE] = { 0 }; // if no name specified, get local if ( NULL == pszHostName || !pszHostName[0]) { GetLocalName(szLocal, HOSTNAME_SIZE); pszHostName = szLocal; } if ( NULL == pszServiceName || !pszServiceName[0]) { pszServiceName = szZERO; } #ifdef _UNICODE char pstrHost[HOSTNAME_SIZE] = { 0 }; WideCharToMultiByte(CP_UTF8, 0, pszHostName, -1, pstrHost, sizeof(pstrHost), NULL, NULL ); char pstrService[HOSTNAME_SIZE] = { 0 }; WideCharToMultiByte(CP_UTF8, 0, pszServiceName, -1, pstrService, sizeof(pstrService), NULL, NULL ); #else LPCTSTR pstrHost = pszHostName; LPCTSTR pstrService = pszServiceName; #endif if ( SOCKET_ERROR != getaddrinfo(pstrHost, pstrService, &aiHints, &aiList) && ( aiList != 0 )) { ADDRINFO ai = { 0 }; ai.ai_addr = sockAddr; memcpy(ai.ai_addr, aiList->ai_addr, aiList->ai_addrlen); freeaddrinfo( aiList ); return true; } SetLastError( WSAGetLastError() ); return false; } /////////////////////////////////////////////////////////////////////////////// // Globals /////////////////////////////////////////////////////////////////////////////// #if !defined(PLATFORM_HAS_INETFUNC) /////////////////////////////////////////////////////////////////////////////// // inet_ntop const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { if ( dst != NULL) { dst[0] = 0; if (af == AF_INET) { sockaddr_in in; memset(&in, 0, sizeof(in)); in.sin_family = AF_INET; memcpy(&in.sin_addr, src, sizeof(in_addr)); getnameinfo((sockaddr *)&in, sizeof(sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } else if (af == AF_INET6) { sockaddr_in6 in; memset(&in, 0, sizeof(in)); in.sin6_family = AF_INET6; memcpy(&in.sin6_addr, src, sizeof(in6_addr)); getnameinfo((sockaddr *)&in, sizeof(sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } } WSASetLastError(WSA_INVALID_PARAMETER); return dst; } /////////////////////////////////////////////////////////////////////////////// // inet_pton int inet_pton(int af, const char *src, void *dst) { int result = SOCKET_ERROR; addrinfo aiHints, *aiList = NULL; memset(&aiHints, 0, sizeof(aiHints)); aiHints.ai_family = af; if ( SOCKET_ERROR != getaddrinfo(src, NULL, &aiHints, &aiList) && (aiList != NULL)) { memcpy(dst, aiList->ai_addr, aiList->ai_addrlen); freeaddrinfo(aiList); result = 0; } return result; } #endif