0
0
Fork 0
mirror of https://github.com/yquake2/yquake2remaster.git synced 2025-03-01 07:01:16 +00:00

Add IPv6 support for Windows

This is essentially a port of unix/network.c to windows.
This commit is contained in:
Yamagi Burmeister 2012-06-06 09:48:48 +02:00
parent 0c74a1af75
commit f5233db0b2

View file

@ -19,16 +19,21 @@
* *
* ======================================================================= * =======================================================================
* *
* Network connections over IPv4 and IPX via Winsocks. * Network connections over IPv4, IPv6 and IPX via Winsocks.
* *
* ======================================================================= * =======================================================================
*/ */
#include "winsock.h" /* Require Win XP or higher */
#include "wsipx.h" #define _WIN32_WINNT 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
#include <wsipx.h>
#include "../common/header/common.h" #include "../common/header/common.h"
#define MAX_LOOPBACK 4 #define MAX_LOOPBACK 4
#define QUAKE2MCAST "ff12::666"
typedef struct typedef struct
{ {
@ -48,62 +53,137 @@ static cvar_t *noipx;
loopback_t loopbacks[2]; loopback_t loopbacks[2];
int ip_sockets[2]; int ip_sockets[2];
int ip6_sockets[2];
int ipx_sockets[2]; int ipx_sockets[2];
static WSADATA winsockdata; char *multicast_interface;
char *NET_ErrorString(void); char *NET_ErrorString(void);
static WSADATA winsockdata;
/* ============================================================================= */ /* ============================================================================= */
void void
NetadrToSockadr(netadr_t *a, struct sockaddr *s) NetadrToSockadr(netadr_t *a, struct sockaddr_storage *s)
{ {
struct sockaddr_in6 *s6;
struct addrinfo hints;
struct addrinfo *res;
int error;
memset(s, 0, sizeof(*s)); memset(s, 0, sizeof(*s));
if (a->type == NA_BROADCAST) switch (a->type)
{ {
case NA_BROADCAST:
((struct sockaddr_in *)s)->sin_family = AF_INET; ((struct sockaddr_in *)s)->sin_family = AF_INET;
((struct sockaddr_in *)s)->sin_port = a->port; ((struct sockaddr_in *)s)->sin_port = a->port;
((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST; ((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST;
} break;
else if (a->type == NA_IP)
{ case NA_IP:
((struct sockaddr_in *)s)->sin_family = AF_INET; ((struct sockaddr_in *)s)->sin_family = AF_INET;
((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip; *(int *)&((struct sockaddr_in *)s)->sin_addr = *(int *)&a->ip;
((struct sockaddr_in *)s)->sin_port = a->port;
break;
case NA_MULTICAST6:
s6 = (struct sockaddr_in6 *)s;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_NUMERICHOST;
error = getaddrinfo(QUAKE2MCAST, NULL, &hints, &res);
if (error)
{
Com_Printf("NET_NetadrToSockadr: inet_pton: %s",
gai_strerror(error));
return;
}
/* sockaddr_in6 should now have a valid scope_id. */
memcpy(s6, res->ai_addr, res->ai_addrlen);
s6->sin6_port = a->port;
/* scope_id is important for link-local
* destination. */
s6->sin6_scope_id = a->scope_id;
break;
case NA_IP6:
if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)a->ip))
{
s->ss_family = AF_INET;
memcpy(&((struct sockaddr_in *)s)->sin_addr,
&((struct in6_addr *)a->ip)->s6_addr[12],
sizeof(struct in_addr));
((struct sockaddr_in *)s)->sin_port = a->port; ((struct sockaddr_in *)s)->sin_port = a->port;
} }
else if (a->type == NA_IPX) else
{ {
((struct sockaddr_ipx *)s)->sa_family = AF_IPX; s6 = (struct sockaddr_in6 *)s;
memcpy(((struct sockaddr_ipx *)s)->sa_netnum, &a->ipx[0], 4);
memcpy(((struct sockaddr_ipx *)s)->sa_nodenum, &a->ipx[4], 6); s6->sin6_family = AF_INET6;
((struct sockaddr_ipx *)s)->sa_socket = a->port; memcpy(&s6->sin6_addr, a->ip, sizeof(s6->sin6_addr));
s6->sin6_port = a->port;
/* scope_id is important for link-local
* destination. */
s6->sin6_scope_id = a->scope_id;
} }
else if (a->type == NA_BROADCAST_IPX)
{ break;
((struct sockaddr_ipx *)s)->sa_family = AF_IPX;
memset(((struct sockaddr_ipx *)s)->sa_netnum, 0, 4); case NA_LOOPBACK:
memset(((struct sockaddr_ipx *)s)->sa_nodenum, 0xff, 6); case NA_IPX:
((struct sockaddr_ipx *)s)->sa_socket = a->port; case NA_BROADCAST_IPX:
/* no handling of NA_LOOPBACK,
NA_IPX, NA_BROADCAST_IPX */
break;
} }
} }
void void
SockadrToNetadr(struct sockaddr *s, netadr_t *a) SockadrToNetadr(struct sockaddr_storage *s, netadr_t *a)
{ {
if (s->sa_family == AF_INET) struct sockaddr_in6 *s6;
if (s->ss_family == AF_INET)
{ {
a->type = NA_IP; *(int *) &a->ip = *(int *)&((struct sockaddr_in *)s)->sin_addr;
*(int *) &a->ip = ((struct sockaddr_in *)s)->sin_addr.s_addr;
a->port = ((struct sockaddr_in *)s)->sin_port; a->port = ((struct sockaddr_in *)s)->sin_port;
a->type = NA_IP;
} }
else if (s->sa_family == AF_IPX) else if (s->ss_family == AF_INET6)
{ {
a->type = NA_IPX; s6 = (struct sockaddr_in6 *)s;
memcpy(&a->ipx[0], ((struct sockaddr_ipx *)s)->sa_netnum, 4);
memcpy(&a->ipx[4], ((struct sockaddr_ipx *)s)->sa_nodenum, 6); if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)&s6->sin6_addr))
a->port = ((struct sockaddr_ipx *)s)->sa_socket; {
memcpy(a->ip,
(struct in_addr *)&s6->sin6_addr.s6_addr[12],
sizeof(struct in_addr));
a->port = ((struct sockaddr_in *)s)->sin_port;
a->type = NA_IP;
}
else
{
memcpy(a->ip, &s6->sin6_addr, sizeof(a->ip));
a->port = s6->sin6_port;
a->type = NA_IP6;
a->scope_id = s6->sin6_scope_id;
}
}
else
{
s = NULL;
} }
} }
@ -132,6 +212,14 @@ NET_CompareAdr(netadr_t a, netadr_t b)
return false; return false;
} }
if (a.type == NA_IP6)
{
if ((memcmp(a.ip, b.ip, 16) == 0) && (a.port == b.port))
{
return true;
}
}
if (a.type == NA_IPX) if (a.type == NA_IPX)
{ {
if ((memcmp(a.ipx, b.ipx, 10) == 0) && (a.port == b.port)) if ((memcmp(a.ipx, b.ipx, 10) == 0) && (a.port == b.port))
@ -145,6 +233,7 @@ NET_CompareAdr(netadr_t a, netadr_t b)
return false; return false;
} }
qboolean qboolean
NET_CompareBaseAdr(netadr_t a, netadr_t b) NET_CompareBaseAdr(netadr_t a, netadr_t b)
{ {
@ -169,6 +258,16 @@ NET_CompareBaseAdr(netadr_t a, netadr_t b)
return false; return false;
} }
if (a.type == NA_IP6)
{
if ((memcmp(a.ip, b.ip, 16) == 0))
{
return true;
}
return false;
}
if (a.type == NA_IPX) if (a.type == NA_IPX)
{ {
if ((memcmp(a.ipx, b.ipx, 10) == 0)) if ((memcmp(a.ipx, b.ipx, 10) == 0))
@ -183,34 +282,100 @@ NET_CompareBaseAdr(netadr_t a, netadr_t b)
} }
char * char *
NET_AdrToString(netadr_t a) NET_BaseAdrToString(netadr_t a)
{ {
static char s[64]; static char s[64], tmp[64];
struct sockaddr_storage ss;
struct sockaddr_in6 *s6;
if (a.type == NA_LOOPBACK) switch (a.type)
{ {
Com_sprintf(s, sizeof(s), "loopback"); case NA_IP:
} case NA_LOOPBACK:
else if (a.type == NA_IP) Com_sprintf(s, sizeof(s), "%i.%i.%i.%i",
a.ip[0], a.ip[1], a.ip[2], a.ip[3]);
break;
case NA_BROADCAST:
Com_sprintf(s, sizeof(s), "255.255.255.255");
break;
case NA_IP6:
case NA_MULTICAST6:
memset(&ss, 0, sizeof(ss));
s6 = (struct sockaddr_in6 *)&ss;
if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)a.ip))
{ {
Com_sprintf(s, sizeof(s), "%i.%i.%i.%i:%i", a.ip[0], ss.ss_family = AF_INET;
a.ip[1], a.ip[2], a.ip[3], ntohs(a.port)); memcpy(&((struct sockaddr_in *)&ss)->sin_addr,
&((struct in6_addr *)a.ip)->s6_addr[12],
sizeof(struct in_addr));
} }
else else
{ {
s6->sin6_scope_id = a.scope_id;
s6->sin6_family = AF_INET6;
memcpy(&s6->sin6_addr, a.ip, sizeof(struct in6_addr));
}
if (getnameinfo((struct sockaddr *)&ss, sizeof(struct sockaddr_in6),
s, sizeof(s), NULL, 0, NI_NUMERICHOST))
{
Com_sprintf(s, sizeof(s), "<invalid>");
}
else
{
if ((a.type == NA_MULTICAST6) ||
IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&ss)->
sin6_addr))
{
/* If the address is multicast (link) or a
link-local, need to carry the scope. The string
format of the IPv6 address is used by the client
to extablish the connect to the server. A better
way to handle this is to always use
sockaddr_storage to represent an IP (v4, v6)
address. Check first if address is already
scoped. getnameinfo under Windows and Linux (?)
already return scoped IPv6 address. */
if (strchr(s, '%') == NULL)
{
Com_sprintf(tmp, sizeof(tmp), "%s%%%d", s,
s6->sin6_scope_id);
memcpy(s, tmp, sizeof(s));
}
}
}
break;
case NA_IPX:
case NA_BROADCAST_IPX:
Com_sprintf(s, sizeof(s), "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%i", Com_sprintf(s, sizeof(s), "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%i",
a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6],
a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9], ntohs(a.port)); a.ipx[7], a.ipx[8], a.ipx[9], ntohs(a.port));
break;
default:
Com_sprintf(s, sizeof(s), "invalid IP address family type");
break;
} }
return s; return s;
} }
#define DO(src, dest) \ char *
copy[0] = s[src]; \ NET_AdrToString(netadr_t a)
copy[1] = s[src + 1]; \ {
sscanf(copy, "%x", &val); \ static char s[64];
((struct sockaddr_ipx *)sadr)->dest = val const char *base;
base = NET_BaseAdrToString(a);
Com_sprintf(s, sizeof(s), "[%s]:%d", base, ntohs(a.port));
return s;
}
/* /*
* localhost * localhost
@ -220,72 +385,78 @@ NET_AdrToString(netadr_t a)
* 192.246.40.70:28000 * 192.246.40.70:28000
*/ */
qboolean qboolean
NET_StringToSockaddr(char *s, struct sockaddr *sadr) NET_StringToSockaddr(char *s, struct sockaddr_storage *sadr)
{ {
struct hostent *h;
char *colon;
int val;
char copy[128]; char copy[128];
char *addrs, *space;
char *ports = NULL;
int err;
struct addrinfo hints;
struct addrinfo *resultp;
memset(sadr, 0, sizeof(*sadr)); memset(sadr, 0, sizeof(*sadr));
memset(&hints, 0, sizeof(hints));
if ((strlen(s) >= 23) && (s[8] == ':') && (s[21] == ':')) /* check for an IPX address */ hints.ai_socktype = SOCK_DGRAM;
{ hints.ai_family = PF_UNSPEC;
((struct sockaddr_ipx *)sadr)->sa_family = AF_IPX;
copy[2] = 0;
DO(0, sa_netnum[0]);
DO(2, sa_netnum[1]);
DO(4, sa_netnum[2]);
DO(6, sa_netnum[3]);
DO(9, sa_nodenum[0]);
DO(11, sa_nodenum[1]);
DO(13, sa_nodenum[2]);
DO(15, sa_nodenum[3]);
DO(17, sa_nodenum[4]);
DO(19, sa_nodenum[5]);
sscanf(&s[22], "%u", &val);
((struct sockaddr_ipx *)sadr)->sa_socket = htons((unsigned short)val);
}
else
{
((struct sockaddr_in *)sadr)->sin_family = AF_INET;
((struct sockaddr_in *)sadr)->sin_port = 0;
strcpy(copy, s); strcpy(copy, s);
addrs = space = copy;
/* strip off a trailing :port if present */ if (*addrs == '[')
for (colon = copy; *colon; colon++)
{ {
if (*colon == ':') addrs++;
for ( ; *space && *space != ']'; space++)
{ {
*colon = 0;
((struct sockaddr_in *)sadr)->sin_port =
htons((short)(int)strtol(colon + 1, (char **)NULL, 10));
}
} }
if ((copy[0] >= '0') && (copy[0] <= '9')) if (!*space)
{
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy);
}
else
{
if (!(h = gethostbyname(copy)))
{ {
Com_Printf("NET_StringToSockaddr: invalid IPv6 address %s\n", s);
return 0; return 0;
} }
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = *space++ = '\0';
*(int *)h->h_addr_list[0];
} }
for ( ; *space; space++)
{
if (*space == ':')
{
*space = '\0';
ports = space + 1;
}
}
if ((err = getaddrinfo(addrs, ports, &hints, &resultp)))
{
/* Error */
Com_Printf("NET_StringToSockaddr: string %s:\n%s\n", s,
gai_strerror(err));
return 0;
}
switch (resultp->ai_family)
{
case AF_INET:
/* convert to ipv4 addr */
memset(sadr, 0, sizeof(struct sockaddr_storage));
memcpy(sadr, resultp->ai_addr, resultp->ai_addrlen);
break;
case AF_INET6:
/* convert to ipv6 addr */
memset(sadr, 0, sizeof(struct sockaddr_storage));
memcpy(sadr, resultp->ai_addr, resultp->ai_addrlen);
break;
default:
Com_Printf ("NET_StringToSockaddr: string %s:\nprotocol family %d not supported\n",
s, resultp->ai_family);
return 0;
} }
return true; return true;
} }
#undef DO
/* /*
* localhost * localhost
* idnewt * idnewt
@ -296,7 +467,7 @@ NET_StringToSockaddr(char *s, struct sockaddr *sadr)
qboolean qboolean
NET_StringToAdr(char *s, netadr_t *a) NET_StringToAdr(char *s, netadr_t *a)
{ {
struct sockaddr sadr; struct sockaddr_storage sadr;
if (!strcmp(s, "localhost")) if (!strcmp(s, "localhost"))
{ {
@ -372,8 +543,8 @@ qboolean
NET_GetPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message) NET_GetPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message)
{ {
int ret; int ret;
struct sockaddr from; struct sockaddr_storage from;
int fromlen; socklen_t fromlen;
int net_socket; int net_socket;
int protocol; int protocol;
int err; int err;
@ -383,12 +554,16 @@ NET_GetPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message)
return true; return true;
} }
for (protocol = 0; protocol < 2; protocol++) for (protocol = 0; protocol < 3; protocol++)
{ {
if (protocol == 0) if (protocol == 0)
{ {
net_socket = ip_sockets[sock]; net_socket = ip_sockets[sock];
} }
else if (protocol == 1)
{
net_socket = ip6_sockets[sock];
}
else else
{ {
net_socket = ipx_sockets[sock]; net_socket = ipx_sockets[sock];
@ -400,8 +575,9 @@ NET_GetPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message)
} }
fromlen = sizeof(from); fromlen = sizeof(from);
ret = recvfrom(net_socket, (char *)net_message->data, net_message->maxsize, ret = recvfrom(net_socket, (char *)net_message->data,
0, (struct sockaddr *)&from, &fromlen); net_message->maxsize, 0, (struct sockaddr *)&from,
&fromlen);
SockadrToNetadr(&from, net_from); SockadrToNetadr(&from, net_from);
@ -428,7 +604,7 @@ NET_GetPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message)
} }
else else
{ {
Com_Error(ERR_DROP, "NET_GetPacket: %s from %s", Com_Printf("NET_GetPacket: %s from %s",
NET_ErrorString(), NET_AdrToString(*net_from)); NET_ErrorString(), NET_AdrToString(*net_from));
} }
@ -454,59 +630,133 @@ void
NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to) NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to)
{ {
int ret; int ret;
struct sockaddr addr; struct sockaddr_storage addr;
int net_socket = 0; int net_socket;
int addr_size = sizeof(struct sockaddr_in);
if (to.type == NA_LOOPBACK) switch (to.type)
{ {
case NA_LOOPBACK:
NET_SendLoopPacket(sock, length, data, to); NET_SendLoopPacket(sock, length, data, to);
return; return;
} break;
case NA_BROADCAST:
if (to.type == NA_BROADCAST) case NA_IP:
{
net_socket = ip_sockets[sock]; net_socket = ip_sockets[sock];
if (!net_socket) if (!net_socket)
{ {
return; return;
} }
}
else if (to.type == NA_IP) break;
{ case NA_IP6:
net_socket = ip_sockets[sock]; case NA_MULTICAST6:
net_socket = ip6_sockets[sock];
addr_size = sizeof(struct sockaddr_in6);
if (!net_socket) if (!net_socket)
{ {
return; return;
} }
}
else if (to.type == NA_IPX) break;
{ case NA_IPX:
case NA_BROADCAST_IPX:
net_socket = ipx_sockets[sock]; net_socket = ipx_sockets[sock];
if (!net_socket) if (!net_socket)
{ {
return; return;
} }
}
else if (to.type == NA_BROADCAST_IPX)
{
net_socket = ipx_sockets[sock];
if (!net_socket) break;
{ default:
Com_Printf("NET_SendPacket: bad address type");
return; return;
} break;
}
else
{
Com_Error(ERR_FATAL, "NET_SendPacket: bad address type");
} }
NetadrToSockadr(&to, &addr); NetadrToSockadr(&to, &addr);
ret = sendto(net_socket, data, length, 0, &addr, sizeof(addr)); /* Re-check the address family. If to.type is NA_IP6 but
contains an IPv4 mapped address, NetadrToSockadr will
return an AF_INET struct. If so, switch back to AF_INET
socket. */
if ((to.type == NA_IP6) && (addr.ss_family == AF_INET))
{
net_socket = ip_sockets[sock];
addr_size = sizeof(struct sockaddr_in);
if (!net_socket)
{
return;
}
}
if (addr.ss_family == AF_INET6)
{
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&addr;
/* If multicast socket, must specify scope.
So multicast_interface must be specified */
if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr))
{
struct addrinfo hints;
struct addrinfo *res;
char tmp[128], mcast_addr[128], mcast_port[10];
int error;
/* Do a getnameinfo/getaddrinfo cycle
to calculate the scope_id of the
multicast address. getaddrinfo is
passed a multicast address of the
form ff0x::xxx%multicast_interface */
error = getnameinfo((struct sockaddr *)s6,
sizeof(struct sockaddr_in6),
tmp, sizeof(tmp), NULL, 0,
NI_NUMERICHOST);
if (error)
{
Com_Printf("NET_SendPacket: getnameinfo: %s",
gai_strerror(error));
return;
}
if (multicast_interface != NULL)
{
Com_sprintf(mcast_addr, sizeof(mcast_addr), "%s", tmp);
Com_sprintf(mcast_port, sizeof(mcast_port), "%d",
ntohs(s6->sin6_port));
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_NUMERICHOST;
error = getaddrinfo(mcast_addr, mcast_port, &hints, &res);
if (error)
{
Com_Printf("NET_SendPacket: getaddrinfo: %s",
gai_strerror(error));
return;
}
/* sockaddr_in6 should now have a valid scope_id. */
memcpy(s6, res->ai_addr, res->ai_addrlen);
}
else
{
Com_Printf("NET_SendPacket: IPv6 multicast destination but +set multicast not specified: %s",
tmp);
return;
}
}
}
ret = sendto(net_socket, data, length, 0,
(struct sockaddr *)&addr, addr_size);
if (ret == -1) if (ret == -1)
{ {
@ -520,7 +770,8 @@ NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to)
/* some PPP links dont allow broadcasts */ /* some PPP links dont allow broadcasts */
if ((err == WSAEADDRNOTAVAIL) && if ((err == WSAEADDRNOTAVAIL) &&
((to.type == NA_BROADCAST) || (to.type == NA_BROADCAST_IPX))) ((to.type == NA_BROADCAST) ||
(to.type == NA_BROADCAST_IPX)))
{ {
return; return;
} }
@ -539,7 +790,7 @@ NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to)
} }
else else
{ {
Com_Error(ERR_DROP, "NET_SendPacket ERROR: %s to %s\n", Com_Printf("NET_SendPacket ERROR: %s to %s\n",
NET_ErrorString(), NET_AdrToString(to)); NET_ErrorString(), NET_AdrToString(to));
} }
} }
@ -549,71 +800,171 @@ NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to)
/* ============================================================================= */ /* ============================================================================= */
int int
NET_IPSocket(char *net_interface, int port) NET_IPSocket(char *net_interface, int port, netsrc_t type, int family)
{ {
int newsocket; char Buf[BUFSIZ], *Host, *Service;
struct sockaddr_in address; int newsocket, Error;
unsigned long t; struct sockaddr_storage ss;
int i = 1; struct addrinfo hints, *res, *ai;
int err; unsigned long t = true;
int one = 1;
if ((newsocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) struct ipv6_mreq mreq;
{ cvar_t *mcast;
err = WSAGetLastError();
if (err != WSAEAFNOSUPPORT) memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;
if (!net_interface || !net_interface[0] ||
!stricmp(net_interface, "localhost"))
{ {
Com_Printf("WARNING: UDP_OpenSocket: socket: %s", NET_ErrorString()); Host = (family == AF_INET6) ? "::" : "0.0.0.0";
}
else
{
Host = net_interface;
} }
if (port == PORT_ANY)
{
Service = NULL;
}
else
{
sprintf(Buf, "%5d", port);
Service = Buf;
}
if ((Error = getaddrinfo(Host, Service, &hints, &res)))
{
return 0; return 0;
} }
for (ai = res; ai != NULL; ai = ai->ai_next)
{
if ((newsocket =
socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1)
{
Com_Printf("NET_IPSocket: socket: %s\n", strerror(errno));
continue;
}
/* make it non-blocking */ /* make it non-blocking */
if (ioctlsocket(newsocket, FIONBIO, &t) == -1) if (ioctlsocket(newsocket, FIONBIO, &t) == -1)
{ {
Com_Printf("WARNING: UDP_OpenSocket: ioctl FIONBIO: %s\n", Com_Printf("NET_IPSocket: ioctl FIONBIO: %s\n", strerror(errno));
NET_ErrorString()); continue;
return 0;
} }
if (setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&one,
sizeof(one)))
{
printf("NET_IPSocket: setsockopt(SO_REUSEADDR) failed: %u\n",
WSAGetLastError());
continue;
}
if (family == AF_INET)
{
/* make it broadcast capable */ /* make it broadcast capable */
if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&one,
sizeof(i)) == -1) sizeof(one)))
{ {
Com_Printf("WARNING: UDP_OpenSocket: setsockopt SO_BROADCAST: %s\n", Com_Printf("ERROR: NET_IPSocket: setsockopt SO_BROADCAST:%s\n",
NET_ErrorString()); strerror(errno));
return 0; return 0;
} }
if (!net_interface || !net_interface[0] ||
!stricmp(net_interface, "localhost"))
{
address.sin_addr.s_addr = INADDR_ANY;
}
else
{
NET_StringToSockaddr(net_interface, (struct sockaddr *)&address);
} }
if (port == PORT_ANY) if (bind(newsocket, ai->ai_addr, ai->ai_addrlen) < 0)
{ {
address.sin_port = 0; Com_Printf("NET_IPSocket: bind: %s\n", strerror(errno));
}
else
{
address.sin_port = htons((short)port);
}
address.sin_family = AF_INET;
if (bind(newsocket, (void *)&address, sizeof(address)) == -1)
{
Com_Printf("WARNING: UDP_OpenSocket: bind: %s\n", NET_ErrorString());
closesocket(newsocket); closesocket(newsocket);
}
else
{
memcpy(&ss, ai->ai_addr, ai->ai_addrlen);
break;
}
}
if (res != NULL)
{
freeaddrinfo(res);
}
if (ai == NULL)
{
return 0; return 0;
} }
switch (ss.ss_family)
{
case AF_INET:
break;
case AF_INET6:
/* Multicast outgoing interface is specified for
client and server (+set multicast <ifname>) */
mcast = Cvar_Get("multicast", "NULL", CVAR_NOSET);
multicast_interface =
(strcmp(mcast->string, "NULL") ? mcast->string : NULL);
if (multicast_interface != NULL)
{
/* multicast_interface is a global variable.
Also used in NET_SendPacket() */
mreq.ipv6mr_interface = (int)mcast->value;
if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_MULTICAST_IF,
(char *)&mreq.ipv6mr_interface,
sizeof(mreq.ipv6mr_interface)) < 0)
{
Com_Printf("NET_IPSocket: IPV6_MULTICAST_IF: %s\n",
strerror(errno));
}
/* Join multicast group ONLY if server */
if (type == NS_SERVER)
{
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;
if ((Error = getaddrinfo(QUAKE2MCAST, NULL, &hints, &res)))
{
Com_Printf("NET_IPSocket: getaddrinfo: %s\n",
gai_strerror(Error));
break;
}
memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
&((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr,
sizeof(mreq.ipv6mr_multiaddr.s6_addr));
freeaddrinfo(res);
Error = setsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP,
(char *)&mreq, sizeof(mreq));
if (Error)
{
Com_Printf("NET_IPSocket: IPV6_JOIN_GROUP: %s\n",
strerror(errno));
break;
}
}
}
break;
}
return newsocket; return newsocket;
} }
@ -642,9 +993,12 @@ NET_OpenIP(void)
} }
} }
ip_sockets[NS_SERVER] = NET_IPSocket(ip->string, port); ip6_sockets[NS_SERVER] = NET_IPSocket(ip->string, port,
NS_SERVER, AF_INET6);
ip_sockets[NS_SERVER] = NET_IPSocket(ip->string, port,
NS_SERVER, AF_INET);
if (!ip_sockets[NS_SERVER] && dedicated) if (!ip_sockets[NS_SERVER] && !ip6_sockets[NS_SERVER] && dedicated)
{ {
Com_Error(ERR_FATAL, "Couldn't allocate dedicated server IP port"); Com_Error(ERR_FATAL, "Couldn't allocate dedicated server IP port");
} }
@ -662,7 +1016,8 @@ NET_OpenIP(void)
if (!port) if (!port)
{ {
port = Cvar_Get("clientport", va("%i", PORT_CLIENT), CVAR_NOSET)->value; port = Cvar_Get("clientport",
va("%i", PORT_CLIENT), CVAR_NOSET)->value;
if (!port) if (!port)
{ {
@ -670,11 +1025,17 @@ NET_OpenIP(void)
} }
} }
ip_sockets[NS_CLIENT] = NET_IPSocket(ip->string, port); ip6_sockets[NS_CLIENT] = NET_IPSocket(ip->string,
port, NS_CLIENT, AF_INET6);
ip_sockets[NS_CLIENT] = NET_IPSocket(ip->string,
port, NS_CLIENT, AF_INET);
if (!ip_sockets[NS_CLIENT]) if (!ip_sockets[NS_CLIENT] && !ip6_sockets[NS_CLIENT])
{ {
ip_sockets[NS_CLIENT] = NET_IPSocket(ip->string, PORT_ANY); ip6_sockets[NS_CLIENT] = NET_IPSocket(ip->string,
PORT_ANY, NS_CLIENT, AF_INET6);
ip_sockets[NS_CLIENT] = NET_IPSocket(ip->string,
PORT_ANY, NS_CLIENT, AF_INET);
} }
} }
} }
@ -793,7 +1154,8 @@ NET_OpenIPX(void)
} }
/* /*
* A single player game will only use the loopback code * A single player game will
* only use the loopback code
*/ */
void void
NET_Config(qboolean multiplayer) NET_Config(qboolean multiplayer)
@ -819,6 +1181,12 @@ NET_Config(qboolean multiplayer)
ip_sockets[i] = 0; ip_sockets[i] = 0;
} }
if (ip6_sockets[i])
{
closesocket(ip6_sockets[i]);
ip6_sockets[i] = 0;
}
if (ipx_sockets[i]) if (ipx_sockets[i])
{ {
closesocket(ipx_sockets[i]); closesocket(ipx_sockets[i]);
@ -861,6 +1229,12 @@ NET_Sleep(int msec)
FD_ZERO(&fdset); FD_ZERO(&fdset);
i = 0; i = 0;
if (ip6_sockets[NS_SERVER])
{
FD_SET(ip6_sockets[NS_SERVER], &fdset); /* network socket */
i = ip6_sockets[NS_SERVER];
}
if (ip_sockets[NS_SERVER]) if (ip_sockets[NS_SERVER])
{ {
FD_SET(ip_sockets[NS_SERVER], &fdset); /* network socket */ FD_SET(ip_sockets[NS_SERVER], &fdset); /* network socket */
@ -879,6 +1253,8 @@ NET_Sleep(int msec)
timeout.tv_sec = msec / 1000; timeout.tv_sec = msec / 1000;
timeout.tv_usec = (msec % 1000) * 1000; timeout.tv_usec = (msec % 1000) * 1000;
i = max(ip_sockets[NS_SERVER], ip6_sockets[NS_SERVER]);
i = max(i, ipx_sockets[NS_SERVER]);
select(i + 1, &fdset, NULL, NULL, &timeout); select(i + 1, &fdset, NULL, NULL, &timeout);
} }