IPv6 Compatability (DLL-detection aware), added GetString() and GetPort() to SocketAddress, Documentation

This was SVN commit r138.
This commit is contained in:
Simon Brenner 2003-12-10 19:39:22 +00:00
parent 50ad3a0e42
commit 73771118b7
2 changed files with 165 additions and 61 deletions

View File

@ -2,6 +2,7 @@
#include "NetworkInternal.h" #include "NetworkInternal.h"
#include "misc.h" #include "misc.h"
#include "CStr.h"
#include <errno.h> #include <errno.h>
@ -41,46 +42,106 @@ PS_RESULT GetPS_RESULT(int error)
SocketAddress::SocketAddress(int port, SocketProtocol proto) SocketAddress::SocketAddress(int port, SocketProtocol proto)
{ {
memset(&m_Union, 0, sizeof(m_Union));
switch (proto) switch (proto)
{ {
case IPv4: case IPv4:
memset(&m_IPv4, 0, sizeof(m_IPv4)); m_Union.m_IPv4.sin_family=PF_INET;
m_IPv4.sin_family=PF_INET; m_Union.m_IPv4.sin_addr.s_addr=htonl(INADDR_ANY);
m_IPv4.sin_addr.s_addr=htonl(INADDR_ANY); m_Union.m_IPv4.sin_port=htons(port);
m_IPv4.sin_port=htons(port);
break; break;
#ifdef USE_INET6
case IPv6: case IPv6:
m_Union.m_IPv6.sin6_family=PF_INET6;
memcpy(&m_Union.m_IPv6.sin6_addr, &in6addr_any, sizeof(in6addr_any));
m_Union.m_IPv6.sin6_port=htons(port);
break; break;
#endif
} }
} }
PS_RESULT SocketAddress::Resolve(const char *name, int port, SocketAddress &addr) PS_RESULT SocketAddress::Resolve(const char *name, int port, SocketAddress &addr)
{ {
hostent *he; if ((getaddrinfo) != NULL)
//FIXME IPv6 compatibilitise
// Construct address
// Try to parse dot-notation IP
addr.m_IPv4.sin_addr.s_addr=inet_addr(name);
if (addr.m_IPv4.sin_addr.s_addr==INADDR_NONE) // Not a dotted IP, try name resolution
{ {
// gethostbyname should be replaced by getaddrinfo, and all of this addrinfo *ai;
// should be done so that IPv6 is just a manner of entering an IPv6 int res=getaddrinfo(name, NULL, NULL, &ai);
// address in the box. if (res == 0)
he=gethostbyname(name);
if (!he)
{ {
return NO_SUCH_HOST; if (ai->ai_addrlen < sizeof(addr.m_Union))
memcpy(&addr.m_Union, ai->ai_addr, ai->ai_addrlen);
switch (addr.m_Union.m_Family)
{
case IPv4:
addr.m_Union.m_IPv4.sin_port=htons(port);
break;
case IPv6:
addr.m_Union.m_IPv6.sin6_port=htons(port);
break;
}
freeaddrinfo(ai);
return PS_OK;
} }
addr.m_IPv4.sin_addr=*(struct in_addr *)(he->h_addr_list[0]); else
return NO_SUCH_HOST;
} }
addr.m_IPv4.sin_family=AF_INET; else
addr.m_IPv4.sin_port=htons(port); {
hostent *he;
addr.m_Union.m_IPv4.sin_family=AF_INET;
addr.m_Union.m_IPv4.sin_port=htons(port);
// Try to parse dot-notation IP
addr.m_Union.m_IPv4.sin_addr.s_addr=inet_addr(name);
if (addr.m_Union.m_IPv4.sin_addr.s_addr==INADDR_NONE) // Not a dotted IP, try name resolution
{
he=gethostbyname(name);
if (!he)
{
return NO_SUCH_HOST;
}
addr.m_Union.m_IPv4.sin_addr=*(struct in_addr *)(he->h_addr_list[0]);
}
return PS_OK;
}
}
return PS_OK; CStr SocketAddress::GetString() const
{
char convBuf[NI_MAXHOST];
if ((getnameinfo) != NULL)
{
int res=getnameinfo((sockaddr *)&m_Union, sizeof(m_Union), convBuf, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (res == 0)
{
return CStr(convBuf);
}
// getnameinfo won't return a string for the IPv6 unspecified address
else if (m_Union.m_Family == IPv6 && res==EAI_NONAME)
return "::";
else
return "";
}
else if (m_Union.m_Family == IPv4)
{
sprintf(convBuf, "%d.%d.%d.%d",
m_Union.m_IPv4.sin_addr.s_addr&0xff,
(m_Union.m_IPv4.sin_addr.s_addr>>8)&0xff,
(m_Union.m_IPv4.sin_addr.s_addr>>16)&0xff,
(m_Union.m_IPv4.sin_addr.s_addr>>24)&0xff);
return CStr(convBuf);
}
else
return CStr();
}
int SocketAddress::GetPort() const
{
switch (m_Union.m_Family)
{
case IPv4:
case IPv6:
return ntohs(m_Union.m_IPv4.sin_port);
}
return -1;
} }
CSocketBase::CSocketBase() CSocketBase::CSocketBase()
@ -345,8 +406,8 @@ PS_RESULT CSocketBase::Bind(const SocketAddress &address)
PS_RESULT CSocketBase::PreAccept(SocketAddress &addr) PS_RESULT CSocketBase::PreAccept(SocketAddress &addr)
{ {
socklen_t addrLen=sizeof(SocketAddress); socklen_t addrLen=sizeof(addr.m_Union);
int fd=accept(m_pInternal->m_fd, (struct sockaddr *)&addr, &addrLen); int fd=accept(m_pInternal->m_fd, (struct sockaddr *)&addr.m_Union, &addrLen);
m_pInternal->m_AcceptFd=fd; m_pInternal->m_AcceptFd=fd;
m_pInternal->m_AcceptAddr=addr; m_pInternal->m_AcceptAddr=addr;
if (fd != -1) if (fd != -1)
@ -716,7 +777,7 @@ void CSocketBase::RunWaitLoop()
printf("Commencing message loop. hWnd %p\n", g_SocketSetInternal.m_hWnd); printf("Commencing message loop. hWnd %p\n", g_SocketSetInternal.m_hWnd);
while ((ret=GetMessage(&msg, g_SocketSetInternal.m_hWnd, 0, 0))!=0) while ((ret=GetMessage(&msg, g_SocketSetInternal.m_hWnd, 0, 0))!=0)
{ {
printf("RunWaitLoop(): Windows message: %d:%d:%d\n", msg.message, msg.wParam, msg.lParam); //printf("RunWaitLoop(): Windows message: %d:%d:%d\n", msg.message, msg.wParam, msg.lParam);
if (ret == -1) if (ret == -1)
{ {
ret=GetLastError(); ret=GetLastError();
@ -756,12 +817,12 @@ void CSocketBase::SendWaitLoopUpdate()
if (m_pInternal->m_Ops & WRITE) if (m_pInternal->m_Ops & WRITE)
wsaOps |= FD_WRITE|FD_CONNECT; wsaOps |= FD_WRITE|FD_CONNECT;
pthread_mutex_unlock(&g_SocketSetInternal.m_Mutex); pthread_mutex_unlock(&g_SocketSetInternal.m_Mutex);
printf("SendWaitLoopUpdate: %d: %u %x -> %p\n", m_pInternal->m_fd, m_pInternal->m_Ops, wsaOps, g_SocketSetInternal.m_hWnd); //printf("SendWaitLoopUpdate: %d: %u %x -> %p\n", m_pInternal->m_fd, m_pInternal->m_Ops, wsaOps, g_SocketSetInternal.m_hWnd);
WSAAsyncSelect(m_pInternal->m_fd, g_SocketSetInternal.m_hWnd, MSG_SOCKET_READY, wsaOps); WSAAsyncSelect(m_pInternal->m_fd, g_SocketSetInternal.m_hWnd, MSG_SOCKET_READY, wsaOps);
} }
else else
{ {
printf("SendWaitLoopUpdate: No WaitLoop Running.\n"); //printf("SendWaitLoopUpdate: No WaitLoop Running.\n");
pthread_mutex_unlock(&g_SocketSetInternal.m_Mutex); pthread_mutex_unlock(&g_SocketSetInternal.m_Mutex);
} }
} }
@ -786,10 +847,11 @@ void CSocketBase::SetOpMask(uint ops)
g_SocketSetInternal.m_HandleMap[m_pInternal->m_fd]=this; g_SocketSetInternal.m_HandleMap[m_pInternal->m_fd]=this;
m_pInternal->m_Ops=ops; m_pInternal->m_Ops=ops;
printf("SetOpMask(fd %d, ops %u) %u\n", /*printf("SetOpMask(fd %d, ops %u) %u\n",
m_pInternal->m_fd, m_pInternal->m_fd,
ops, ops,
g_SocketSetInternal.m_HandleMap[m_pInternal->m_fd]->m_pInternal->m_Ops); g_SocketSetInternal.m_HandleMap[m_pInternal->m_fd]->m_pInternal->m_Ops);*/
pthread_mutex_unlock(&g_SocketSetInternal.m_Mutex); pthread_mutex_unlock(&g_SocketSetInternal.m_Mutex);
SendWaitLoopUpdate(); SendWaitLoopUpdate();

View File

@ -9,6 +9,7 @@
#include "types.h" #include "types.h"
#include "Prometheus.h" #include "Prometheus.h"
#include <string.h> #include <string.h>
#include "CStr.h"
//------------------------------------------------- //-------------------------------------------------
// Error Codes // Error Codes
@ -44,9 +45,7 @@ enum SocketProtocol
// you don't accidentally use an UNSPEC SocketAddress // you don't accidentally use an UNSPEC SocketAddress
UNSPEC=((sa_family_t)-1), UNSPEC=((sa_family_t)-1),
IPv4=PF_INET, IPv4=PF_INET,
#ifdef USE_INET6
IPv6=PF_INET6, IPv6=PF_INET6,
#endif
/* More protocols */ /* More protocols */
}; };
@ -57,20 +56,24 @@ enum SocketProtocol
// Modifiers Note: Each member must contain a first field, compatible with the // Modifiers Note: Each member must contain a first field, compatible with the
// sin_family field of sockaddr_in. The field contains the SocketProtocol value // sin_family field of sockaddr_in. The field contains the SocketProtocol value
// for the address, and it is returned by GetProtocol() // for the address, and it is returned by GetProtocol()
union SocketAddress struct SocketAddress
{ {
sockaddr_in m_IPv4; union
sockaddr_in6 m_IPv6; {
sa_family_t m_Family;
sockaddr_in m_IPv4;
sockaddr_in6 m_IPv6;
} m_Union;
inline SocketProtocol GetProtocol() const inline SocketProtocol GetProtocol() const
{ {
return (SocketProtocol)m_IPv4.sin_family; return (SocketProtocol)m_Union.m_Family;
} }
inline SocketAddress() inline SocketAddress()
{ {
memset(this, 0, sizeof(SocketAddress)); memset(&m_Union, 0, sizeof(m_Union));
m_IPv4.sin_family=UNSPEC; m_Union.m_Family=UNSPEC;
} }
/** /**
@ -93,16 +96,34 @@ union SocketAddress
SocketAddress(u8 address[4], int port); SocketAddress(u8 address[4], int port);
/** /**
* Resolve the name using the systems name resolution service (i.e. DNS), * Resolve the name using the system name resolution service (i.e. DNS) and
* and store the resulting address. When multiple addresses are found, the * store the resulting address. When multiple addresses are found, the
* first result is returned. * first result is returned.
* *
* Note that this call will block until the name resolution attempt is
* either completed successfully or timed out.
*
* @param name The name to resolve * @param name The name to resolve
* @param addr A reference to the variable to hold the address * @param addr A reference to the variable to hold the address
* *
* @return An error code; PS_OK for success * @return The result of the operation
* @retval PS_OK The hostname was successfully retrieved
* @retval NO_SUCH_HOST The hostname was not found
*/ */
static PS_RESULT Resolve(const char *name, int port, SocketAddress &addr); static PS_RESULT Resolve(const char *name, int port, SocketAddress &addr);
/**
* Returns the string representation of the address, i.e. the IP (v4 or v6)
* address. Note that the port is not included in the string (mostly due to
* the fact that the port representation differs wildly between address
* families, and that Resolve does not take port as part of the hostname)
*/
CStr GetString() const;
/**
* Returns the port number part of the address
*/
int GetPort() const;
}; };
/** /**
@ -278,6 +299,9 @@ public:
*/ */
PS_RESULT Connect(const SocketAddress &addr); PS_RESULT Connect(const SocketAddress &addr);
/** @name Functions for Server Sockets */
//@{
/** /**
* Bind the socket to the specified address and start listening for * Bind the socket to the specified address and start listening for
* incoming connections. You must initialize the socket for the correct * incoming connections. You must initialize the socket for the correct
@ -314,6 +338,10 @@ public:
*/ */
void Reject(); void Reject();
//@}
/** @name Status and Options */
//@{
/** /**
* Set or reset non-blocking operation. When non-blocking, all socket * Set or reset non-blocking operation. When non-blocking, all socket
* operations will return immediately, having done none or parts of * operations will return immediately, having done none or parts of
@ -365,21 +393,22 @@ public:
*/ */
const SocketAddress &GetRemoteAddress(); const SocketAddress &GetRemoteAddress();
//@}
/** @name Stream I/O */
//@{
/** /**
* Attempt to read data from the socket. Any data available without blocking * Attempt to read data from the socket. Any data available without blocking
* will be returned. Note that a successful return does not mean that the * will be returned. Note that a successful return does not mean that the
* whole buffer was filled. * whole buffer was filled.
* *
* Inputs * @param buf A pointer to the buffer where the data should be written
* buf A pointer to the buffer where the data should be written * @param len The amount of data that should be read.
* len The length of the buffer. The amount of data the function should * @param bytesRead The number of bytes read will be stored in the variable
* try to read. * pointed to by bytesRead
* bytesRead A pointer to an uint where the amount of bytes read should
* be stored
* *
* Returns * @retval PS_OK Some or all data was successfully read.
* PS_OK Some or all data was successfully read. * @retval CONNECTION_BROKEN The socket is not connected or a server socket
* CONNECTION_BROKEN The socket is not connected or a server socket
*/ */
PS_RESULT Read(void *buf, uint len, uint *bytesRead); PS_RESULT Read(void *buf, uint len, uint *bytesRead);
@ -387,20 +416,33 @@ public:
* Attempt to write data to the socket. All data that can be sent without * Attempt to write data to the socket. All data that can be sent without
* blocking will be buffered. * blocking will be buffered.
* *
* Inputs * @param buf A pointer to the data that should be written
* buf A pointer to the buffer of data to write * @param len The length of the buffer.
* len The length of the buffer. * @param bytesWritten The number of bytes written will be stored in the
* bytesWritten A pointer to an uint to store the bytes written * variable pointed to by bytesWritten
* *
* Returns * @retval PS_OK Some or all data was successfully read.
* PS_OK Some or all data was successfully read. * @retval CONNECTION_BROKEN The socket is not connected or a server socket
* CONNECTION_BROKEN The socket is not connected or a server socket
*/ */
PS_RESULT Write(void *buf, uint len, uint *bytesWritten); PS_RESULT Write(void *buf, uint len, uint *bytesWritten);
// CALLBACKS //@}
/** @name Callbacks */
//@{
/**
* Called by the Network Thread when data is available for reading. Use
* SetOpMask with the READ bit set to enable calling of this function.
*
* For server sockets, "data is available for reading" means "incoming
* connections are pending".
*/
virtual void OnRead()=0; virtual void OnRead()=0;
/**
* Called by the Network Thread when data can be written to the socket.
* Will only be called when the WRITE bit is set in the Op Mask of the
* socket.
*/
virtual void OnWrite()=0; virtual void OnWrite()=0;
/** /**
@ -408,8 +450,8 @@ public:
* provides meaningful diagnostics. CONNECTION_BROKEN is the generic catch- * provides meaningful diagnostics. CONNECTION_BROKEN is the generic catch-
* all for erroneous closures, PS_OK for clean closures. * all for erroneous closures, PS_OK for clean closures.
* *
* Inputs * @param errorCode A result code describing the reason why the socket was
* errorCode The reason for closure. * closed
*/ */
virtual void OnClose(PS_RESULT errorCode)=0; virtual void OnClose(PS_RESULT errorCode)=0;
}; };