X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=lhnet.c;h=7da46a74ecf2a799aa3ad99078cf8a0710308f81;hb=1fd37f5239fe019e4d53c3e19b6fa9ff67da12e1;hp=16cf41748734bcac2fded46bad9e4db14f4b3e08;hpb=a01302832564d321dd2a544d2ee6f1eb5a618863;p=xonotic%2Fdarkplaces.git diff --git a/lhnet.c b/lhnet.c index 16cf4174..7da46a74 100644 --- a/lhnet.c +++ b/lhnet.c @@ -1,154 +1,359 @@ // Written by Forest Hale 2003-06-15 and placed into public domain. +#ifdef WIN32 +// Windows XP or higher is required for getaddrinfo, but the inclusion of wspiapi provides fallbacks for older versions +#define _WIN32_WINNT 0x0501 +#include +#include +//#include +#endif + +#ifndef STANDALONETEST +#include "quakedef.h" +#endif + #include #include #include #include -#ifdef WIN32 -#include -#else -#include -#include -//#include +#ifndef WIN32 #include +#include #include #include #include +#include +#include +#include +#include +#endif + +#ifdef __MORPHOS__ +#include #endif // for Z_Malloc/Z_Free in quake #ifndef STANDALONETEST -#include "quakedef.h" #include "zone.h" #include "sys.h" #include "netconn.h" #else +#define Con_Print printf +#define Con_Printf printf #define Z_Malloc malloc #define Z_Free free #endif #include "lhnet.h" -int LHNETADDRESS_FromPort(lhnetaddress_t *address, int addresstype, int port) +#if defined(WIN32) +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ECONNREFUSED WSAECONNREFUSED + +#define SOCKETERRNO WSAGetLastError() + +#define IOC_VENDOR 0x18000000 +#define _WSAIOW(x,y) (IOC_IN|(x)|(y)) +#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) + +#define SOCKLEN_T int +#elif defined(__MORPHOS__) +#define ioctlsocket IoctlSocket +#define closesocket CloseSocket +#define SOCKETERRNO Errno() + +#define SOCKLEN_T int +#else +#define ioctlsocket ioctl +#define closesocket close +#define SOCKETERRNO errno + +#define SOCKLEN_T socklen_t +#endif + +typedef struct lhnetaddressnative_s +{ + lhnetaddresstype_t addresstype; + int port; + union + { + struct sockaddr sock; + struct sockaddr_in in; + struct sockaddr_in6 in6; + } + addr; +} +lhnetaddressnative_t; + +// to make LHNETADDRESS_FromString resolve repeated hostnames faster, cache them +#define MAX_NAMECACHE 64 +static struct namecache_s { + lhnetaddressnative_t address; + double expirationtime; + char name[64]; +} +namecache[MAX_NAMECACHE]; +static int namecacheposition = 0; + +int LHNETADDRESS_FromPort(lhnetaddress_t *vaddress, lhnetaddresstype_t addresstype, int port) +{ + lhnetaddressnative_t *address = (lhnetaddressnative_t *)vaddress; if (!address) return 0; switch(addresstype) { + default: + return 0; case LHNETADDRESSTYPE_LOOP: // local:port (loopback) memset(address, 0, sizeof(*address)); address->addresstype = LHNETADDRESSTYPE_LOOP; - address->addressdata.loop.port = port; + address->port = port; return 1; case LHNETADDRESSTYPE_INET4: // 0.0.0.0:port (INADDR_ANY, binds to all interfaces) memset(address, 0, sizeof(*address)); address->addresstype = LHNETADDRESSTYPE_INET4; - address->addressdata.inet4.family = LHNETADDRESSTYPE_INET4_FAMILY; - address->addressdata.inet4.port = htons((unsigned short)port); + address->port = port; + address->addr.in.sin_family = AF_INET; + address->addr.in.sin_port = htons((unsigned short)port); return 1; case LHNETADDRESSTYPE_INET6: // [0:0:0:0:0:0:0:0]:port (IN6ADDR_ANY, binds to all interfaces) memset(address, 0, sizeof(*address)); address->addresstype = LHNETADDRESSTYPE_INET6; - address->addressdata.inet6.family = LHNETADDRESSTYPE_INET6_FAMILY; - address->addressdata.inet6.port = htons((unsigned short)port); + address->port = port; + address->addr.in6.sin6_family = AF_INET6; + address->addr.in6.sin6_port = htons((unsigned short)port); return 1; } return 0; } -int LHNETADDRESS_FromString(lhnetaddress_t *address, const char *string, int defaultport) +int LHNETADDRESS_Resolve(lhnetaddressnative_t *address, const char *name, int port) +{ + char port_buff [16]; + struct addrinfo hints; + struct addrinfo* addrinf; + int err; + + dpsnprintf (port_buff, sizeof (port_buff), "%d", port); + port_buff[sizeof (port_buff) - 1] = '\0'; + + memset(&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + //hints.ai_flags = AI_PASSIVE; + + err = getaddrinfo(name, port_buff, &hints, &addrinf); + if (err != 0 || addrinf == NULL) + return 0; + if (addrinf->ai_addr->sa_family != AF_INET6 && addrinf->ai_addr->sa_family != AF_INET) + { + freeaddrinfo (addrinf); + return 0; + } + + // great it worked + if (addrinf->ai_addr->sa_family == AF_INET6) + { + address->addresstype = LHNETADDRESSTYPE_INET6; + memcpy(&address->addr.in6, addrinf->ai_addr, sizeof(address->addr.in6)); + } + else + { + address->addresstype = LHNETADDRESSTYPE_INET4; + memcpy(&address->addr.in, addrinf->ai_addr, sizeof(address->addr.in)); + } + address->port = port; + + freeaddrinfo (addrinf); + return 1; +} + +int LHNETADDRESS_FromString(lhnetaddress_t *vaddress, const char *string, int defaultport) { - int port, namelen, d1, d2, d3, d4; - struct hostent *hostentry; - const char *colon; + lhnetaddressnative_t *address = (lhnetaddressnative_t *)vaddress; + int i, port, d1, d2, d3, d4, resolved; + size_t namelen; + unsigned char *a; char name[128]; - if (!address || !string) +#ifdef STANDALONETEST + char string2[128]; +#endif + const char* addr_start; + const char* addr_end = NULL; + const char* port_name = NULL; + int addr_family = AF_UNSPEC; + + if (!address || !string || !*string) return 0; memset(address, 0, sizeof(*address)); address->addresstype = LHNETADDRESSTYPE_NONE; port = 0; - colon = strrchr(string, ':'); - if (colon) - port = atoi(colon + 1); + + // If it's a bracketed IPv6 address + if (string[0] == '[') + { + const char* end_bracket = strchr(string, ']'); + + if (end_bracket == NULL) + return 0; + + if (end_bracket[1] == ':') + port_name = end_bracket + 2; + else if (end_bracket[1] != '\0') + return 0; + + addr_family = AF_INET6; + addr_start = &string[1]; + addr_end = end_bracket; + } else - colon = string + strlen(string); - if (port == 0) - port = defaultport; - namelen = colon - string; - if (namelen > 127) - namelen = 127; - if (string[0] == '[' && namelen > 0 && string[namelen-1] == ']') // ipv6 { - string++; - namelen -= 2; + const char* first_colon; + + addr_start = string; + + // If it's a numeric non-bracket IPv6 address (-> no port), + // or it's a numeric IPv4 address, or a name, with a port + first_colon = strchr(string, ':'); + if (first_colon != NULL) + { + const char* last_colon = strrchr(first_colon + 1, ':'); + + // If it's an numeric IPv4 address, or a name, with a port + if (last_colon == NULL) + { + addr_end = first_colon; + port_name = first_colon + 1; + } + else + addr_family = AF_INET6; + } } - memcpy(name, string, namelen); + + if (addr_end != NULL) + namelen = addr_end - addr_start; + else + namelen = strlen (addr_start); + + if (namelen >= sizeof(name)) + namelen = sizeof(name) - 1; + memcpy (name, addr_start, namelen); name[namelen] = 0; + + if (port_name) + port = atoi(port_name); + + if (port == 0) + port = defaultport; + // handle loopback if (!strcmp(name, "local")) { address->addresstype = LHNETADDRESSTYPE_LOOP; - address->addressdata.loop.port = port; + address->port = port; return 1; } - // try to parse with gethostbyname first, because it can handle ipv4 and - // ipv6 (in various address formats), as well as dns names - hostentry = gethostbyname(name); - if (hostentry) + // try to parse as dotted decimal ipv4 address first + // note this supports partial ip addresses + d1 = d2 = d3 = d4 = 0; +#if _MSC_VER >= 1400 +#define sscanf sscanf_s +#endif + if (addr_family != AF_INET6 && + sscanf(name, "%d.%d.%d.%d", &d1, &d2, &d3, &d4) >= 1 && (unsigned int)d1 < 256 && (unsigned int)d2 < 256 && (unsigned int)d3 < 256 && (unsigned int)d4 < 256) { - if (hostentry->h_addrtype == LHNETADDRESSTYPE_INET6_FAMILY) - { - // great it worked - address->addresstype = LHNETADDRESSTYPE_INET6; - address->addressdata.inet6.family = hostentry->h_addrtype; - address->addressdata.inet6.port = htons((unsigned short)port); - memcpy(address->addressdata.inet6.address, hostentry->h_addr_list[0], sizeof(address->addressdata.inet6.address)); + // parsed a valid ipv4 address + address->addresstype = LHNETADDRESSTYPE_INET4; + address->port = port; + address->addr.in.sin_family = AF_INET; + address->addr.in.sin_port = htons((unsigned short)port); + a = (unsigned char *)&address->addr.in.sin_addr; + a[0] = d1; + a[1] = d2; + a[2] = d3; + a[3] = d4; #ifdef STANDALONETEST - printf("gethostbyname(\"%s\") returned ipv6 address [%x:%x:%x:%x:%x:%x:%x:%x]:%d\n", name, (int)address->addressdata.inet6.address[0], (int)address->addressdata.inet6.address[1], (int)address->addressdata.inet6.address[2], (int)address->addressdata.inet6.address[3], (int)address->addressdata.inet6.address[4], (int)address->addressdata.inet6.address[5], (int)address->addressdata.inet6.address[6], (int)address->addressdata.inet6.address[7], (int)ntohs(address->addressdata.inet6.port)); + LHNETADDRESS_ToString(address, string2, sizeof(string2), 1); + printf("manual parsing of ipv4 dotted decimal address \"%s\" successful: %s\n", string, string2); +#endif + return 1; + } + for (i = 0;i < MAX_NAMECACHE;i++) + if (!strcmp(namecache[i].name, name)) + break; +#ifdef STANDALONETEST + if (i < MAX_NAMECACHE) +#else + if (i < MAX_NAMECACHE && realtime < namecache[i].expirationtime) #endif + { + *address = namecache[i].address; + address->port = port; + if (address->addresstype == LHNETADDRESSTYPE_INET6) + { + address->addr.in6.sin6_port = htons((unsigned short)port); return 1; } - else if (hostentry->h_addrtype == LHNETADDRESSTYPE_INET4_FAMILY) + else if (address->addresstype == LHNETADDRESSTYPE_INET4) { - // great it worked - address->addresstype = LHNETADDRESSTYPE_INET4; - address->addressdata.inet4.family = hostentry->h_addrtype; - address->addressdata.inet4.port = htons((unsigned short)port); - memcpy(address->addressdata.inet4.address, hostentry->h_addr_list[0], sizeof(address->addressdata.inet4.address)); -#ifdef STANDALONETEST - printf("gethostbyname(\"%s\") returned ipv4 address %d.%d.%d.%d:%d\n", name, (int)address->addressdata.inet4.address[0], (int)address->addressdata.inet4.address[1], (int)address->addressdata.inet4.address[2], (int)address->addressdata.inet4.address[3], (int)ntohs(address->addressdata.inet4.port)); -#endif + address->addr.in.sin_port = htons((unsigned short)port); return 1; } + return 0; } - // failed, try to parse as an ipv4 address as a fallback (is this needed?) + + for (i = 0;i < (int)sizeof(namecache[namecacheposition].name)-1 && name[i];i++) + namecache[namecacheposition].name[i] = name[i]; + namecache[namecacheposition].name[i] = 0; +#ifndef STANDALONETEST + namecache[namecacheposition].expirationtime = realtime + 12 * 3600; // 12 hours +#endif + + // try resolving the address (handles dns and other ip formats) + resolved = LHNETADDRESS_Resolve(address, name, port); + if (resolved) + { #ifdef STANDALONETEST - printf("gethostbyname and gethostbyaddr failed on address \"%s\"\n", name); + const char *protoname; + + switch (address->addresstype) + { + case LHNETADDRESSTYPE_INET6: + protoname = "ipv6"; + break; + case LHNETADDRESSTYPE_INET4: + protoname = "ipv4"; + break; + default: + protoname = "UNKNOWN"; + break; + } + LHNETADDRESS_ToString(vaddress, string2, sizeof(string2), 1); + Con_Printf("LHNETADDRESS_Resolve(\"%s\") returned %s address %s\n", string, protoname, string2); #endif - if (sscanf(name, "%d.%d.%d.%d", &d1, &d2, &d3, &d4) == 4 && (unsigned int)d1 < 256 && (unsigned int)d2 < 256 && (unsigned int)d3 < 256 && (unsigned int)d4 < 256) + namecache[namecacheposition].address = *address; + } + else { - // parsed a valid ipv4 address - address->addresstype = LHNETADDRESSTYPE_INET4; - address->addressdata.inet4.family = LHNETADDRESSTYPE_INET4_FAMILY; - address->addressdata.inet4.port = htons((unsigned short)port); - address->addressdata.inet4.address[0] = (unsigned char)d1; - address->addressdata.inet4.address[1] = (unsigned char)d2; - address->addressdata.inet4.address[2] = (unsigned char)d3; - address->addressdata.inet4.address[3] = (unsigned char)d4; #ifdef STANDALONETEST - printf("manual parsing of ipv4 dotted decimal address \"%s\" successful: %d.%d.%d.%d:%d\n", string, (int)address->addressdata.inet4.address[0], (int)address->addressdata.inet4.address[1], (int)address->addressdata.inet4.address[2], (int)address->addressdata.inet4.address[3], (int)ntohs(address->addressdata.inet4.port)); + printf("name resolution failed on address \"%s\"\n", name); #endif - return 1; + namecache[namecacheposition].address.addresstype = LHNETADDRESSTYPE_NONE; } - return 0; + + namecacheposition = (namecacheposition + 1) % MAX_NAMECACHE; + return resolved; } -int LHNETADDRESS_ToString(const lhnetaddress_t *address, char *string, int stringbuffersize, int includeport) +int LHNETADDRESS_ToString(const lhnetaddress_t *vaddress, char *string, int stringbuffersize, int includeport) { + lhnetaddressnative_t *address = (lhnetaddressnative_t *)vaddress; + const unsigned char *a; *string = 0; if (!address || !string || stringbuffersize < 1) return 0; @@ -161,7 +366,7 @@ int LHNETADDRESS_ToString(const lhnetaddress_t *address, char *string, int strin { if (stringbuffersize >= 12) { - sprintf(string, "local:%d", (int)address->addressdata.loop.port); + dpsnprintf(string, stringbuffersize, "local:%d", address->port); return 1; } } @@ -169,17 +374,18 @@ int LHNETADDRESS_ToString(const lhnetaddress_t *address, char *string, int strin { if (stringbuffersize >= 6) { - strcpy(string, "local"); + memcpy(string, "local", 6); return 1; } } break; case LHNETADDRESSTYPE_INET4: + a = (const unsigned char *)(&address->addr.in.sin_addr); if (includeport) { if (stringbuffersize >= 22) { - sprintf(string, "%d.%d.%d.%d:%d", (int)address->addressdata.inet4.address[0], (int)address->addressdata.inet4.address[1], (int)address->addressdata.inet4.address[2], (int)address->addressdata.inet4.address[3], (int)ntohs(address->addressdata.inet4.port)); + dpsnprintf(string, stringbuffersize, "%d.%d.%d.%d:%d", a[0], a[1], a[2], a[3], address->port); return 1; } } @@ -187,17 +393,18 @@ int LHNETADDRESS_ToString(const lhnetaddress_t *address, char *string, int strin { if (stringbuffersize >= 16) { - sprintf(string, "%d.%d.%d.%d", (int)address->addressdata.inet4.address[0], (int)address->addressdata.inet4.address[1], (int)address->addressdata.inet4.address[2], (int)address->addressdata.inet4.address[3]); + dpsnprintf(string, stringbuffersize, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]); return 1; } } break; case LHNETADDRESSTYPE_INET6: + a = (const unsigned char *)(&address->addr.in6.sin6_addr); if (includeport) { if (stringbuffersize >= 88) { - sprintf(string, "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", (int)address->addressdata.inet6.address[0], (int)address->addressdata.inet6.address[1], (int)address->addressdata.inet6.address[2], (int)address->addressdata.inet6.address[3], (int)address->addressdata.inet6.address[4], (int)address->addressdata.inet6.address[5], (int)address->addressdata.inet6.address[6], (int)address->addressdata.inet6.address[7], (int)ntohs(address->addressdata.inet6.port)); + dpsnprintf(string, stringbuffersize, "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", a[0] * 256 + a[1], a[2] * 256 + a[3], a[4] * 256 + a[5], a[6] * 256 + a[7], a[8] * 256 + a[9], a[10] * 256 + a[11], a[12] * 256 + a[13], a[14] * 256 + a[15], address->port); return 1; } } @@ -205,7 +412,7 @@ int LHNETADDRESS_ToString(const lhnetaddress_t *address, char *string, int strin { if (stringbuffersize >= 80) { - sprintf(string, "%x:%x:%x:%x:%x:%x:%x:%x", (int)address->addressdata.inet6.address[0], (int)address->addressdata.inet6.address[1], (int)address->addressdata.inet6.address[2], (int)address->addressdata.inet6.address[3], (int)address->addressdata.inet6.address[4], (int)address->addressdata.inet6.address[5], (int)address->addressdata.inet6.address[6], (int)address->addressdata.inet6.address[7]); + dpsnprintf(string, stringbuffersize, "%x:%x:%x:%x:%x:%x:%x:%x", a[0] * 256 + a[1], a[2] * 256 + a[3], a[4] * 256 + a[5], a[6] * 256 + a[7], a[8] * 256 + a[9], a[10] * 256 + a[11], a[12] * 256 + a[13], a[14] * 256 + a[15]); return 1; } } @@ -222,45 +429,67 @@ int LHNETADDRESS_GetAddressType(const lhnetaddress_t *address) return LHNETADDRESSTYPE_NONE; } +const char *LHNETADDRESS_GetInterfaceName(const lhnetaddress_t *vaddress) +{ + lhnetaddressnative_t *address = (lhnetaddressnative_t *)vaddress; + + if (address && address->addresstype == LHNETADDRESSTYPE_INET6) + { +#ifndef _WIN32 + + static char ifname [IF_NAMESIZE]; + + if (if_indextoname(address->addr.in6.sin6_scope_id, ifname) == ifname) + return ifname; + +#else + + // The Win32 API doesn't have if_indextoname() until Windows Vista, + // but luckily it just uses the interface ID as the interface name + + static char ifname [16]; + + if (dpsnprintf(ifname, sizeof(ifname), "%lu", address->addr.in6.sin6_scope_id) > 0) + return ifname; + +#endif + } + + return NULL; +} + int LHNETADDRESS_GetPort(const lhnetaddress_t *address) { if (!address) return -1; - switch(address->addresstype) - { - case LHNETADDRESSTYPE_LOOP: - return address->addressdata.loop.port; - case LHNETADDRESSTYPE_INET4: - return ntohs(address->addressdata.inet4.port); - case LHNETADDRESSTYPE_INET6: - return ntohs(address->addressdata.inet6.port); - default: - return -1; - } + return address->port; } -int LHNETADDRESS_SetPort(lhnetaddress_t *address, int port) +int LHNETADDRESS_SetPort(lhnetaddress_t *vaddress, int port) { + lhnetaddressnative_t *address = (lhnetaddressnative_t *)vaddress; if (!address) return 0; + address->port = port; switch(address->addresstype) { case LHNETADDRESSTYPE_LOOP: - address->addressdata.loop.port = port; return 1; case LHNETADDRESSTYPE_INET4: - address->addressdata.inet4.port = htons((unsigned short)port); + address->addr.in.sin_port = htons((unsigned short)port); return 1; case LHNETADDRESSTYPE_INET6: - address->addressdata.inet6.port = htons((unsigned short)port); + address->addr.in6.sin6_port = htons((unsigned short)port); return 1; default: return 0; } } -int LHNETADDRESS_Compare(const lhnetaddress_t *address1, const lhnetaddress_t *address2) +int LHNETADDRESS_Compare(const lhnetaddress_t *vaddress1, const lhnetaddress_t *vaddress2) { + lhnetaddressnative_t *address1 = (lhnetaddressnative_t *)vaddress1; + lhnetaddressnative_t *address2 = (lhnetaddressnative_t *)vaddress2; if (!address1 || !address2) return 1; if (address1->addresstype != address2->addresstype) @@ -268,23 +497,23 @@ int LHNETADDRESS_Compare(const lhnetaddress_t *address1, const lhnetaddress_t *a switch(address1->addresstype) { case LHNETADDRESSTYPE_LOOP: - if (address1->addressdata.loop.port != address2->addressdata.loop.port) + if (address1->port != address2->port) return -1; return 0; case LHNETADDRESSTYPE_INET4: - if (address1->addressdata.inet4.family != address2->addressdata.inet4.family) + if (address1->addr.in.sin_family != address2->addr.in.sin_family) return 1; - if (memcmp(address1->addressdata.inet4.address, address2->addressdata.inet4.address, sizeof(address1->addressdata.inet4.address))) + if (memcmp(&address1->addr.in.sin_addr, &address2->addr.in.sin_addr, sizeof(address1->addr.in.sin_addr))) return 1; - if (address1->addressdata.inet4.port != address2->addressdata.inet4.port) + if (address1->port != address2->port) return -1; return 0; case LHNETADDRESSTYPE_INET6: - if (address1->addressdata.inet6.family != address2->addressdata.inet6.family) + if (address1->addr.in6.sin6_family != address2->addr.in6.sin6_family) return 1; - if (memcmp(address1->addressdata.inet6.address, address2->addressdata.inet6.address, sizeof(address1->addressdata.inet6.address))) + if (memcmp(&address1->addr.in6.sin6_addr, &address2->addr.in6.sin6_addr, sizeof(address1->addr.in6.sin6_addr))) return 1; - if (address1->addressdata.inet6.port != address2->addressdata.inet6.port) + if (address1->port != address2->port) return -1; return 0; default: @@ -321,6 +550,11 @@ void LHNET_Init(void) lhnet_socketlist.next = lhnet_socketlist.prev = &lhnet_socketlist; lhnet_packetlist.next = lhnet_packetlist.prev = &lhnet_packetlist; lhnet_active = 1; +#ifdef WIN32 + lhnet_didWSAStartup = !WSAStartup(MAKEWORD(1, 1), &lhnet_winsockdata); + if (!lhnet_didWSAStartup) + Con_Print("LHNET_Init: WSAStartup failed, networking disabled\n"); +#endif } void LHNET_Shutdown(void) @@ -337,15 +571,102 @@ void LHNET_Shutdown(void) p->next->prev = p->prev; Z_Free(p); } +#ifdef WIN32 + if (lhnet_didWSAStartup) + { + lhnet_didWSAStartup = 0; + WSACleanup(); + } +#endif lhnet_active = 0; } +static const char *LHNETPRIVATE_StrError(void) +{ +#ifdef WIN32 + int i = WSAGetLastError(); + switch (i) + { + case WSAEINTR: return "WSAEINTR"; + case WSAEBADF: return "WSAEBADF"; + case WSAEACCES: return "WSAEACCES"; + case WSAEFAULT: return "WSAEFAULT"; + case WSAEINVAL: return "WSAEINVAL"; + case WSAEMFILE: return "WSAEMFILE"; + case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK"; + case WSAEINPROGRESS: return "WSAEINPROGRESS"; + case WSAEALREADY: return "WSAEALREADY"; + case WSAENOTSOCK: return "WSAENOTSOCK"; + case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ"; + case WSAEMSGSIZE: return "WSAEMSGSIZE"; + case WSAEPROTOTYPE: return "WSAEPROTOTYPE"; + case WSAENOPROTOOPT: return "WSAENOPROTOOPT"; + case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT"; + case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT"; + case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP"; + case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT"; + case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT"; + case WSAEADDRINUSE: return "WSAEADDRINUSE"; + case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL"; + case WSAENETDOWN: return "WSAENETDOWN"; + case WSAENETUNREACH: return "WSAENETUNREACH"; + case WSAENETRESET: return "WSAENETRESET"; + case WSAECONNABORTED: return "WSAECONNABORTED"; + case WSAECONNRESET: return "WSAECONNRESET"; + case WSAENOBUFS: return "WSAENOBUFS"; + case WSAEISCONN: return "WSAEISCONN"; + case WSAENOTCONN: return "WSAENOTCONN"; + case WSAESHUTDOWN: return "WSAESHUTDOWN"; + case WSAETOOMANYREFS: return "WSAETOOMANYREFS"; + case WSAETIMEDOUT: return "WSAETIMEDOUT"; + case WSAECONNREFUSED: return "WSAECONNREFUSED"; + case WSAELOOP: return "WSAELOOP"; + case WSAENAMETOOLONG: return "WSAENAMETOOLONG"; + case WSAEHOSTDOWN: return "WSAEHOSTDOWN"; + case WSAEHOSTUNREACH: return "WSAEHOSTUNREACH"; + case WSAENOTEMPTY: return "WSAENOTEMPTY"; + case WSAEPROCLIM: return "WSAEPROCLIM"; + case WSAEUSERS: return "WSAEUSERS"; + case WSAEDQUOT: return "WSAEDQUOT"; + case WSAESTALE: return "WSAESTALE"; + case WSAEREMOTE: return "WSAEREMOTE"; + case WSAEDISCON: return "WSAEDISCON"; + case 0: return "no error"; + default: return "unknown WSAE error"; + } +#else + return strerror(errno); +#endif +} + +void LHNET_SleepUntilPacket_Microseconds(int microseconds) +{ + fd_set fdreadset; + struct timeval tv; + int lastfd; + lhnetsocket_t *s; + FD_ZERO(&fdreadset); + lastfd = 0; + for (s = lhnet_socketlist.next;s != &lhnet_socketlist;s = s->next) + { + if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6) + { + if (lastfd < s->inetsocket) + lastfd = s->inetsocket; + FD_SET((unsigned int)s->inetsocket, &fdreadset); + } + } + tv.tv_sec = microseconds / 1000000; + tv.tv_usec = microseconds % 1000000; + select(lastfd + 1, &fdreadset, NULL, NULL, &tv); +} + lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address) { lhnetsocket_t *lhnetsocket, *s; if (!address) return NULL; - lhnetsocket = Z_Malloc(sizeof(*lhnetsocket)); + lhnetsocket = (lhnetsocket_t *)Z_Malloc(sizeof(*lhnetsocket)); if (lhnetsocket) { memset(lhnetsocket, 0, sizeof(*lhnetsocket)); @@ -353,29 +674,29 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address) switch(lhnetsocket->address.addresstype) { case LHNETADDRESSTYPE_LOOP: - if (lhnetsocket->address.addressdata.loop.port == 0) + if (lhnetsocket->address.port == 0) { // allocate a port dynamically // this search will always terminate because there is never // an allocated socket with port 0, so if the number wraps it // will find the port is unused, and then refuse to use port // 0, causing an intentional failure condition - lhnetsocket->address.addressdata.loop.port = 1024; + lhnetsocket->address.port = 1024; for (;;) { for (s = lhnet_socketlist.next;s != &lhnet_socketlist;s = s->next) - if (s->address.addresstype == lhnetsocket->address.addresstype && s->address.addressdata.loop.port == lhnetsocket->address.addressdata.loop.port) + if (s->address.addresstype == lhnetsocket->address.addresstype && s->address.port == lhnetsocket->address.port) break; if (s == &lhnet_socketlist) break; - lhnetsocket->address.addressdata.loop.port++; + lhnetsocket->address.port++; } } // check if the port is available for (s = lhnet_socketlist.next;s != &lhnet_socketlist;s = s->next) - if (s->address.addresstype == lhnetsocket->address.addresstype && s->address.addressdata.loop.port == lhnetsocket->address.addressdata.loop.port) + if (s->address.addresstype == lhnetsocket->address.addresstype && s->address.port == lhnetsocket->address.port) break; - if (s == &lhnet_socketlist && lhnetsocket->address.addressdata.loop.port != 0) + if (s == &lhnet_socketlist && lhnetsocket->address.port != 0) { lhnetsocket->next = &lhnet_socketlist; lhnetsocket->prev = lhnetsocket->next->prev; @@ -387,42 +708,86 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address) case LHNETADDRESSTYPE_INET4: case LHNETADDRESSTYPE_INET6: #ifdef WIN32 - if (!lhnet_didWSAStartup && !WSAStartup(MAKEWORD(1, 1), &lhnet_winsockdata)) - { - lhnet_didWSAStartup = 1; -#else + if (lhnet_didWSAStartup) { #endif - if (address->addresstype == LHNETADDRESSTYPE_INET6) - lhnetsocket->inetsocket = socket(LHNETADDRESSTYPE_INET6_FAMILY, SOCK_DGRAM, IPPROTO_UDP); - else - lhnetsocket->inetsocket = socket(LHNETADDRESSTYPE_INET4_FAMILY, SOCK_DGRAM, IPPROTO_UDP); - if (lhnetsocket->inetsocket != -1) + if ((lhnetsocket->inetsocket = socket(address->addresstype == LHNETADDRESSTYPE_INET6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1) { #ifdef WIN32 u_long _true = 1; - if (ioctlsocket(lhnetsocket->inetsocket, FIONBIO, &_true) != -1) + u_long _false = 0; #else char _true = 1; - if (ioctl(lhnetsocket->inetsocket, FIONBIO, &_true) != -1) #endif + if (ioctlsocket(lhnetsocket->inetsocket, FIONBIO, &_true) != -1) { - if (bind(lhnetsocket->inetsocket, (void *)&lhnetsocket->address.addressdata, address->addresstype == LHNETADDRESSTYPE_INET6 ? sizeof(lhnetsocket->address.addressdata.inet6) : sizeof(lhnetsocket->address.addressdata.inet4)) != -1) +#ifdef IPV6_V6ONLY + // We need to set this flag to tell the OS that we only listen on IPv6. If we don't + // most OSes will create a dual-protocol socket that also listens on IPv4. In this case + // if an IPv4 socket is already bound to the port we want, our bind() call will fail. + int ipv6_only = 1; + if (address->addresstype != LHNETADDRESSTYPE_INET6 + || setsockopt (lhnetsocket->inetsocket, IPPROTO_IPV6, IPV6_V6ONLY, + (const char *)&ipv6_only, sizeof(ipv6_only)) == 0 +#ifdef WIN32 + // The Win32 API only supports IPV6_V6ONLY since Windows Vista, but fortunately + // the default value is what we want on Win32 anyway (IPV6_V6ONLY = true) + || SOCKETERRNO == WSAENOPROTOOPT +#endif + ) +#endif { - lhnetsocket->next = &lhnet_socketlist; - lhnetsocket->prev = lhnetsocket->next->prev; - lhnetsocket->next->prev = lhnetsocket; - lhnetsocket->prev->next = lhnetsocket; - return lhnetsocket; + lhnetaddressnative_t *localaddress = (lhnetaddressnative_t *)&lhnetsocket->address; + SOCKLEN_T namelen; + int bindresult; + if (address->addresstype == LHNETADDRESSTYPE_INET6) + { + namelen = sizeof(localaddress->addr.in6); + bindresult = bind(lhnetsocket->inetsocket, &localaddress->addr.sock, namelen); + if (bindresult != -1) + getsockname(lhnetsocket->inetsocket, &localaddress->addr.sock, &namelen); + } + else + { + namelen = sizeof(localaddress->addr.in); + bindresult = bind(lhnetsocket->inetsocket, &localaddress->addr.sock, namelen); + if (bindresult != -1) + getsockname(lhnetsocket->inetsocket, &localaddress->addr.sock, &namelen); + } + if (bindresult != -1) + { + int i = 1; + // enable broadcast on this socket + setsockopt(lhnetsocket->inetsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)); + lhnetsocket->next = &lhnet_socketlist; + lhnetsocket->prev = lhnetsocket->next->prev; + lhnetsocket->next->prev = lhnetsocket; + lhnetsocket->prev->next = lhnetsocket; +#ifdef WIN32 + if (ioctlsocket(lhnetsocket->inetsocket, SIO_UDP_CONNRESET, &_false) == -1) + Con_DPrintf("LHNET_OpenSocket_Connectionless: ioctlsocket SIO_UDP_CONNRESET returned error: %s\n", LHNETPRIVATE_StrError()); +#endif + return lhnetsocket; + } + else + Con_Printf("LHNET_OpenSocket_Connectionless: bind returned error: %s\n", LHNETPRIVATE_StrError()); } +#ifdef IPV6_V6ONLY + else + Con_Printf("LHNET_OpenSocket_Connectionless: setsockopt(IPV6_V6ONLY) returned error: %s\n", LHNETPRIVATE_StrError()); +#endif } -#ifdef WIN32 + else + Con_Printf("LHNET_OpenSocket_Connectionless: ioctlsocket returned error: %s\n", LHNETPRIVATE_StrError()); closesocket(lhnetsocket->inetsocket); -#else - close(lhnetsocket->inetsocket); -#endif } + else + Con_Printf("LHNET_OpenSocket_Connectionless: socket returned error: %s\n", LHNETPRIVATE_StrError()); +#ifdef WIN32 } + else + Con_Print("LHNET_OpenSocket_Connectionless: can't open a socket (WSAStartup failed during LHNET_Init)\n"); +#endif break; default: break; @@ -447,19 +812,8 @@ void LHNET_CloseSocket(lhnetsocket_t *lhnetsocket) // no special close code for loopback, just inet if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_INET4 || lhnetsocket->address.addresstype == LHNETADDRESSTYPE_INET6) { -#ifdef WIN32 closesocket(lhnetsocket->inetsocket); -#else - close(lhnetsocket->inetsocket); -#endif } -#ifdef WIN32 - if (lhnet_socketlist.next == &lhnet_socketlist && lhnet_didWSAStartup) - { - lhnet_didWSAStartup = 0; - WSACleanup(); - } -#endif Z_Free(lhnetsocket); } } @@ -472,8 +826,9 @@ lhnetaddress_t *LHNET_AddressFromSocket(lhnetsocket_t *sock) return NULL; } -int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength, lhnetaddress_t *address) +int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength, lhnetaddress_t *vaddress) { + lhnetaddressnative_t *address = (lhnetaddressnative_t *)vaddress; int value = 0; if (!lhnetsocket || !address || !content || maxcontentlength < 1) return -1; @@ -496,15 +851,16 @@ int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength, continue; } #ifndef STANDALONETEST - if (p->sentdoubletime && Sys_DoubleTime() < p->sentdoubletime) + if (cl_netlocalping.value && (realtime - cl_netlocalping.value * (1.0 / 2000.0)) < p->sentdoubletime) continue; #endif - if (value == 0 && p->destinationport == lhnetsocket->address.addressdata.loop.port) + if (value == 0 && p->destinationport == lhnetsocket->address.port) { if (p->length <= maxcontentlength) { - *address = lhnetsocket->address; - address->addressdata.loop.port = p->sourceport; + lhnetaddressnative_t *localaddress = (lhnetaddressnative_t *)&lhnetsocket->address; + *address = *localaddress; + address->port = p->sourceport; memcpy(content, p->data, p->length); value = p->length; } @@ -519,79 +875,62 @@ int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength, } else if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_INET4) { - int inetaddresslength; + SOCKLEN_T inetaddresslength; address->addresstype = LHNETADDRESSTYPE_NONE; - inetaddresslength = sizeof(address->addressdata.inet4); - value = recvfrom(lhnetsocket->inetsocket, content, maxcontentlength, 0, (struct sockaddr *)&address->addressdata.inet4, &inetaddresslength); + inetaddresslength = sizeof(address->addr.in); + value = recvfrom(lhnetsocket->inetsocket, (char *)content, maxcontentlength, 0, &address->addr.sock, &inetaddresslength); if (value > 0) { address->addresstype = LHNETADDRESSTYPE_INET4; + address->port = ntohs(address->addr.in.sin_port); return value; } else if (value == -1) { -#ifdef WIN32 - int e = WSAGetLastError(); - if (e == WSAEWOULDBLOCK) + int e = SOCKETERRNO; + if (e == EWOULDBLOCK) return 0; switch (e) - { - case WSAECONNREFUSED: - Con_Printf("Connection refused\n"); - return 0; - } -#else - if (errno == EWOULDBLOCK) - return 0; - switch (errno) { case ECONNREFUSED: - Con_Printf("Connection refused\n"); + Con_Print("Connection refused\n"); return 0; } -#endif + Con_Printf("LHNET_Read: recvfrom returned error: %s\n", LHNETPRIVATE_StrError()); } } else if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_INET6) { - int inetaddresslength; + SOCKLEN_T inetaddresslength; address->addresstype = LHNETADDRESSTYPE_NONE; - inetaddresslength = sizeof(address->addressdata.inet6); - value = recvfrom(lhnetsocket->inetsocket, content, maxcontentlength, 0, (struct sockaddr *)&address->addressdata.inet6, &inetaddresslength); + inetaddresslength = sizeof(address->addr.in6); + value = recvfrom(lhnetsocket->inetsocket, (char *)content, maxcontentlength, 0, &address->addr.sock, &inetaddresslength); if (value > 0) { address->addresstype = LHNETADDRESSTYPE_INET6; + address->port = ntohs(address->addr.in6.sin6_port); return value; } else if (value == -1) { -#ifdef WIN32 - int e = WSAGetLastError(); - if (e == WSAEWOULDBLOCK) + int e = SOCKETERRNO; + if (e == EWOULDBLOCK) return 0; switch (e) - { - case WSAECONNREFUSED: - Con_Printf("Connection refused\n"); - return 0; - } -#else - if (errno == EWOULDBLOCK) - return 0; - switch (errno) { case ECONNREFUSED: - Con_Printf("Connection refused\n"); + Con_Print("Connection refused\n"); return 0; } -#endif + Con_Printf("LHNET_Read: recvfrom returned error: %s\n", LHNETPRIVATE_StrError()); } } return value; } -int LHNET_Write(lhnetsocket_t *lhnetsocket, const void *content, int contentlength, const lhnetaddress_t *address) +int LHNET_Write(lhnetsocket_t *lhnetsocket, const void *content, int contentlength, const lhnetaddress_t *vaddress) { + lhnetaddressnative_t *address = (lhnetaddressnative_t *)vaddress; int value = -1; if (!lhnetsocket || !address || !content || contentlength < 1) return -1; @@ -600,51 +939,40 @@ int LHNET_Write(lhnetsocket_t *lhnetsocket, const void *content, int contentleng if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_LOOP) { lhnetpacket_t *p; - p = Z_Malloc(sizeof(*p) + contentlength); + p = (lhnetpacket_t *)Z_Malloc(sizeof(*p) + contentlength); p->data = (void *)(p + 1); memcpy(p->data, content, contentlength); p->length = contentlength; - p->sourceport = lhnetsocket->address.addressdata.loop.port; - p->destinationport = address->addressdata.loop.port; + p->sourceport = lhnetsocket->address.port; + p->destinationport = address->port; p->timeout = time(NULL) + 10; p->next = &lhnet_packetlist; p->prev = p->next->prev; p->next->prev = p; p->prev->next = p; #ifndef STANDALONETEST - if (cl_fakelocalping_min.integer || cl_fakelocalping_max.integer) - p->sentdoubletime = Sys_DoubleTime() + (cl_fakelocalping_min.integer + ((cl_fakelocalping_max.integer - cl_fakelocalping_min.integer) * (rand() & 255) / 256)) / 1000.0; + p->sentdoubletime = realtime; #endif value = contentlength; } else if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_INET4) { - value = sendto(lhnetsocket->inetsocket, content, contentlength, 0, (struct sockaddr *)&address->addressdata.inet4, sizeof(address->addressdata.inet4)); + value = sendto(lhnetsocket->inetsocket, (char *)content, contentlength, 0, (struct sockaddr *)&address->addr.in, sizeof(struct sockaddr_in)); if (value == -1) { -#ifdef WIN32 - int e = WSAGetLastError(); - if (e == WSAEWOULDBLOCK) - return 0; -#else - if (errno == EWOULDBLOCK) + if (SOCKETERRNO == EWOULDBLOCK) return 0; -#endif + Con_Printf("LHNET_Write: sendto returned error: %s\n", LHNETPRIVATE_StrError()); } } else if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_INET6) { - value = sendto(lhnetsocket->inetsocket, content, contentlength, 0, (struct sockaddr *)&address->addressdata.inet6, sizeof(address->addressdata.inet6)); + value = sendto(lhnetsocket->inetsocket, (char *)content, contentlength, 0, (struct sockaddr *)&address->addr.in6, sizeof(struct sockaddr_in6)); if (value == -1) { -#ifdef WIN32 - int e = WSAGetLastError(); - if (e == WSAEWOULDBLOCK) - return 0; -#else - if (errno == EWOULDBLOCK) + if (SOCKETERRNO == EWOULDBLOCK) return 0; -#endif + Con_Printf("LHNET_Write: sendto returned error: %s\n", LHNETPRIVATE_StrError()); } } return value; @@ -653,6 +981,58 @@ int LHNET_Write(lhnetsocket_t *lhnetsocket, const void *content, int contentleng #ifdef STANDALONETEST int main(int argc, char **argv) { +#if 1 + char *buffer = "test", buffer2[1024]; + int blen = strlen(buffer); + int b2len = 1024; + lhnetsocket_t *sock1; + lhnetsocket_t *sock2; + lhnetaddress_t myaddy1; + lhnetaddress_t myaddy2; + lhnetaddress_t myaddy3; + lhnetaddress_t localhostaddy1; + lhnetaddress_t localhostaddy2; + int test1; + int test2; + + printf("calling LHNET_Init\n"); + LHNET_Init(); + + printf("calling LHNET_FromPort twice to create two local addresses\n"); + LHNETADDRESS_FromPort(&myaddy1, LHNETADDRESSTYPE_INET4, 4000); + LHNETADDRESS_FromPort(&myaddy2, LHNETADDRESSTYPE_INET4, 4001); + LHNETADDRESS_FromString(&localhostaddy1, "127.0.0.1", 4000); + LHNETADDRESS_FromString(&localhostaddy2, "127.0.0.1", 4001); + + printf("calling LHNET_OpenSocket_Connectionless twice to create two local sockets\n"); + sock1 = LHNET_OpenSocket_Connectionless(&myaddy1); + sock2 = LHNET_OpenSocket_Connectionless(&myaddy2); + + printf("calling LHNET_Write to send a packet from the first socket to the second socket\n"); + test1 = LHNET_Write(sock1, buffer, blen, &localhostaddy2); + printf("sleeping briefly\n"); +#ifdef WIN32 + Sleep (100); +#else + usleep (100000); +#endif + printf("calling LHNET_Read on the second socket to read the packet sent from the first socket\n"); + test2 = LHNET_Read(sock2, buffer2, b2len - 1, &myaddy3); + if (test2 > 0) + Con_Printf("socket to socket test succeeded\n"); + else + Con_Printf("socket to socket test failed\n"); + +#ifdef WIN32 + printf("press any key to exit\n"); + getchar(); +#endif + + printf("calling LHNET_Shutdown\n"); + LHNET_Shutdown(); + printf("exiting\n"); + return 0; +#else lhnetsocket_t *sock[16], *sendsock; int i; int numsockets; @@ -788,6 +1168,7 @@ int main(int argc, char **argv) } printf("Testing code for lhnet.c\nusage: lhnettest [ ]\n"); return -1; +#endif } #endif