From 7d8fb5901eb0b7a221fbf465f8cdd9a36c721c56 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Mon, 15 Jan 2001 04:09:07 +0000 Subject: [PATCH] ipv6 support. TOTALLY untested except for compile time. --- configure.in | 19 ++ include/net.h | 2 +- source/Makefile.am | 44 ++-- source/net_udp6.c | 578 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 624 insertions(+), 19 deletions(-) create mode 100644 source/net_udp6.c diff --git a/configure.in b/configure.in index 8e9e22a..39097b7 100644 --- a/configure.in +++ b/configure.in @@ -236,6 +236,24 @@ if test "x$enable_zlib" != "xno"; then fi fi + +AC_ARG_WITH(ipv6, +[ --with-ipv6 enable IPv6 support. Optional argument specifies + location of inet6 libraries.], + if test "x$withval" = xno ; then + NETTYPE_IPV6=no + else + AC_DEFINE(HAVE_IPV6) + NETTYPE_IPV6=yes + if test "x$withval" != xyes ; then + LIBS="$LIBS -L${withval}" + fi + fi +, + NETTYPE_IPV6=no +) +AM_CONDITIONAL(NETTYPE_IPV6, test "x$NETTYPE_IPV6" = "xyes") + dnl Checks for MGL support AC_ARG_WITH(mgl, [ --with-mgl[=DIR] use MGL found in DIR], @@ -1267,6 +1285,7 @@ AC_MSG_RESULT([ Server support: $SV_TARGETS Client support:$CL_TARGETS Sound system: $SNDTYPE + IPv6 networking: $NETTYPE_IPV6 Shared game data directory: $sharepath Per-user game data directory: $userpath diff --git a/include/net.h b/include/net.h index 4887550..7a703d8 100644 --- a/include/net.h +++ b/include/net.h @@ -39,7 +39,7 @@ typedef struct { byte ip[4]; unsigned short port; - unsigned short pad; + unsigned short family; // used to be pad, before IPV6 } netadr_t; extern netadr_t net_local_adr; diff --git a/source/Makefile.am b/source/Makefile.am index 08ceeda..1dc9648 100644 --- a/source/Makefile.am +++ b/source/Makefile.am @@ -41,17 +41,25 @@ EXTRA_PROGRAMS= qf-server \ qf-client-x11 -noinst_LIBRARIES= libqfcd.a libqfjs.a libqfsnd.a libqfsys_cl.a libqfsys_sv.a +noinst_LIBRARIES= libqfcd.a libqfjs.a libqfnet.a libqfsnd.a libqfsys_cl.a libqfsys_sv.a if ASM_ARCH math_ASM = math.S sys_x86.S endif common_SOURCES= buildnum.c checksum.c cmd.c com.c crc.c cvar.c hash.c \ - info.c link.c \ - locs.c mathlib.c mdfour.c model.c model_brush.c msg.c \ - net_chan.c net_com.c net_udp.c pmove.c pmovetst.c qargs.c \ - qendian.c quakefs.c quakeio.c sizebuf.c va.c zone.c \ - $(math_ASM) + info.c link.c locs.c mathlib.c mdfour.c model.c model_brush.c \ + msg.c pmove.c pmovetst.c qargs.c qendian.c quakefs.c quakeio.c \ + sizebuf.c va.c zone.c $(math_ASM) + +# +# ... Network type +# +if NETTYPE_IPV6 +libqfnet_a_SOURCES= net_chan.c net_com.c net_udp6.c +else +libqfnet_a_SOURCES= net_chan.c net_com.c net_udp.c +endif +EXTRA_libqfnet_a_SOURCES= net_chan.c net_com.c net_udp.c net_udp6.c # # Server builds @@ -75,8 +83,8 @@ server_SOURCES= pr_edict.c pr_exec.c pr_offs.c sv_ccmds.c sv_cvar.c \ sv_user.c ver_check.c world.c $(world_ASM) qf_server_SOURCES= $(common_SOURCES) $(server_SOURCES) -qf_server_LDADD= -L. -lqfsys_sv $(NET_LIBS) $(Z_LIBS) $(DL_LIBS) -qf_server_DEPENDENCIES= libqfsys_sv.a +qf_server_LDADD= -L. -lqfnet -lqfsys_sv $(NET_LIBS) $(Z_LIBS) $(DL_LIBS) +qf_server_DEPENDENCIES= libqfnet.a libqfsys_sv.a # # Client builds @@ -159,7 +167,7 @@ endif libqfjs_a_CFLAGS= $(JOY_CFLAGS) EXTRA_libqfjs_a_SOURCES= joy_linux.c joy_win.c joy_null.c -CLIENT_LIBS= -L. -lqfsys_cl -lqfsnd -lqfcd -lqfjs $(SOUND_LIBS) $(NET_LIBS) $(CD_LIBS) $(JOY_LIBS) $(Z_LIBS) +CLIENT_LIBS= -L. -lqfnet -lqfsys_cl -lqfsnd -lqfcd -lqfjs $(SOUND_LIBS) $(NET_LIBS) $(CD_LIBS) $(JOY_LIBS) $(Z_LIBS) if ASM_ARCH client_ASM= snd_mixa.S cl_math.S @@ -196,7 +204,7 @@ ggi_SOURCES= vid_ggi.c qf_client_ggi_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(ggi_SOURCES) qf_client_ggi_LDADD= $(GGI_LIBS) $(CLIENT_LIBS) -qf_client_ggi_DEPENDENCIES=libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a +qf_client_ggi_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a # # ... SciTech MGL @@ -205,7 +213,7 @@ mgl_SOURCES= vid_mgl.c in_win.c qf_client_mgl_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(mgl_SOURCES) qf_client_mgl_LDADD= $(MGL_LIBS) $(CLIENT_LIBS) -qf_client_mgl_DEPENDENCIES=libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a +qf_client_mgl_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a # # ... Sam Lantinga's Simple DirectMedia Layer, version 1.0 and higher @@ -214,7 +222,7 @@ sdl_SOURCES= vid_sdl.c in_sdl.c qf_client_sdl_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(sdl_SOURCES) qf_client_sdl_LDADD= $(SDL_LIBS) $(CLIENT_LIBS) -qf_client_sdl_DEPENDENCIES=libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a +qf_client_sdl_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a # # ... Linux SVGAlib @@ -223,7 +231,7 @@ svga_SOURCES= d_copy.S vid_svgalib.c in_svgalib.c qf_client_svga_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(svga_SOURCES) qf_client_svga_LDADD= $(SVGA_LIBS) $(CLIENT_LIBS) -qf_client_svga_DEPENDENCIES=libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a +qf_client_svga_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a # # ... X11 @@ -232,7 +240,7 @@ x11_SOURCES= vid_x11.c in_x11.c context_x11.c dga_check.c qf_client_x11_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(x11_SOURCES) qf_client_x11_LDADD= $(X_PRE_LIBS) $(VIDMODE_LIBS) $(DGA_LIBS) $(X_LIBS) -lX11 $(X_EXTRA_LIBS) $(X_SHM_LIB) $(CLIENT_LIBS) -qf_client_x11_DEPENDENCIES=libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a +qf_client_x11_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a # @@ -254,7 +262,7 @@ tdfx_SOURCES= vid_3dfxsvga.c vid_common_gl.c in_svgalib.c qf_client_3dfx_SOURCES= $(common_SOURCES) $(client_SOURCES) $(ogl_SOURCES) $(tdfx_SOURCES) qf_client_3dfx_LDADD= $(TDFXGL_LIBS) $(SVGA_LIBS) $(CLIENT_LIBS) $(DL_LIBS) -qf_client_3dfx_DEPENDENCIES=libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a +qf_client_3dfx_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a # # ... OpenGL in X Window @@ -263,7 +271,7 @@ glx_SOURCES= vid_glx.c vid_common_gl.c in_x11.c context_x11.c dga_check.c qf_client_glx_SOURCES= $(common_SOURCES) $(client_SOURCES) $(ogl_SOURCES) $(glx_SOURCES) qf_client_glx_LDADD= $(GLX_LIBS) $(X_PRE_LIBS) $(VIDMODE_LIBS) $(DGA_LIBS) $(X_LIBS) -lX11 $(X_EXTRA_LIBS) $(CLIENT_LIBS) $(DL_LIBS) -qf_client_glx_DEPENDENCIES=libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a +qf_client_glx_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a # # ... Sam Lantinga's Simple DirectMedia Layer, version 1.1 and higher, in GL mode @@ -272,7 +280,7 @@ sgl_SOURCES= vid_sgl.c vid_common_gl.c in_sdl.c qf_client_sgl_SOURCES= $(common_SOURCES) $(client_SOURCES) $(ogl_SOURCES) $(sgl_SOURCES) qf_client_sgl_LDADD= $(SDL_LIBS) $(X_LIBS) $(GLX_LIBS) $(CLIENT_LIBS) $(DL_LIBS) -qf_client_sgl_DEPENDENCIES=libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a +qf_client_sgl_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a # # SGI/Microsoft WGL (Windows OpenGL) @@ -281,7 +289,7 @@ wgl_SOURCES= vid_wgl.c qf_client_wgl_SOURCES= $(common_SOURCES) $(client_SOURCES) $(ogl_SOURCES) $(wgl_SOURCES) qf_client_wgl_LDADD= $(CLIENT_LIBS) -qf_client_wgl_DEPENDENCIES=libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a +qf_client_wgl_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a # # Stuff that doesn't get linked into an executable NEEDS to be mentioned here, diff --git a/source/net_udp6.c b/source/net_udp6.c new file mode 100644 index 0000000..da522d3 --- /dev/null +++ b/source/net_udp6.c @@ -0,0 +1,578 @@ +/* + net_udp6.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Nelson Rush. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef _WIN32 +# ifdef HAVE_IPV6 +# include +# include +# include +# define _WINSOCKAPI_ +# define HAVE_SOCKLEN_T +# endif +#endif + +#include +#include + +/* Sun's model_t in sys/model.h conflicts w/ Quake's model_t */ +#define model_t quakeforgemodel_t + +#ifdef _WIN32 +# include +# undef EWOULDBLOCK +# define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef _WIN32 +#include +#endif +#include + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#undef model_t + +#ifdef NeXT +#include +#endif + +#include "console.h" +#include "net.h" +#include "qargs.h" +#include "qtypes.h" +#include "sys.h" + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 512 +#endif + +#ifdef __GLIBC__ // glibc macro +#define s6_addr32 in6_u.u6_addr32 +#define ss_family __ss_family +#endif + +netadr_t net_local_adr; + +netadr_t net_from; +sizebuf_t net_message; +int net_socket; + +#define MAX_UDP_PACKET (MAX_MSGLEN*2) +byte net_message_buffer[MAX_UDP_PACKET]; + +#ifdef _WIN32 +WSADATA winsockdata; +#endif + +//============================================================================= + +void +NetadrToSockadr (netadr_t *a, struct sockaddr_in6 *s) +{ + memset (s, 0, sizeof (*s)); + + s->sin6_family = AF_INET6; + memcpy (&s->sin6_addr, a->ip, sizeof (s->sin6_addr)); + s->sin6_port = a->port; +#ifdef HAVE_SIN6_LEN + s->sin6_len = sizeof (struct sockaddr_in6); +#endif +} + +void +SockadrToNetadr (struct sockaddr_in6 *s, netadr_t *a) +{ + memcpy (a->ip, &s->sin6_addr, sizeof (s->sin6_addr)); + a->port = s->sin6_port; + a->family = s->sin6_family; +} + +qboolean +NET_AdrIsLoopback (netadr_t a) +{ + if (IN6_IS_ADDR_LOOPBACK ((struct in6_addr *) &a.ip)) + return true; + else if (IN6_IS_ADDR_V4MAPPED ((struct in6_addr *) &a.ip) && + ((struct in_addr *) &a.ip[3])->s_addr == htonl (INADDR_LOOPBACK)) + return true; + + return false; +} + +qboolean +NET_CompareBaseAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] + && a.ip[3] == b.ip[3]) + return true; + return false; +} + + +qboolean +NET_CompareAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] + && a.ip[3] == b.ip[3] && a.port == b.port) + return true; + return false; +} + +char * +NET_AdrToString (netadr_t a) +{ + static char s[64]; + char *base; + + base = NET_BaseAdrToString (a); + sprintf (s, "[%s]:%d", base, ntohs (a.port)); + return s; +} + +char * +NET_BaseAdrToString (netadr_t a) +{ + static char s[64]; + struct sockaddr_storage ss; + + // NetadrToSockadr(&a,&sa); + + memset (&ss, 0, sizeof (ss)); + if (IN6_IS_ADDR_V4MAPPED ((struct in6_addr *) a.ip)) { +#ifdef HAVE_SS_LEN + ss.ss_len = sizeof (struct sockaddr_in); +#endif + ss.ss_family = AF_INET; + memcpy (&((struct sockaddr_in *) &ss)->sin_addr, + &((struct in6_addr *) a.ip)->s6_addr[12], + + sizeof (struct in_addr)); + } else { +#ifdef HAVE_SS_LEN + ss.ss_len = sizeof (struct sockaddr_in6); +#endif + ss.ss_family = AF_INET6; + memcpy (&((struct sockaddr_in6 *) &ss)->sin6_addr, + + a.ip, sizeof (struct in6_addr)); + } +#ifdef HAVE_SS_LEN + if (getnameinfo ((struct sockaddr *) &ss, ss.ss_len, s, sizeof (s), + NULL, 0, NI_NUMERICHOST)) strcpy (s, ""); +#else + // maybe needs switch for AF_INET6 or AF_INET? + if (getnameinfo ((struct sockaddr *) &ss, sizeof (ss), s, sizeof (s), + NULL, 0, NI_NUMERICHOST)) + strcpy (s, ""); +#endif + return s; +} + +/* +============= +NET_StringToAdr + +idnewt +idnewt:28000 +192.246.40.70 +192.246.40.70:28000 +============= +*/ +qboolean +NET_StringToAdr (char *s, netadr_t *a) +{ + + struct addrinfo hints; + struct addrinfo *resultp; + char *space; + char *ports = NULL; + char copy[128]; + char *addrs; + int err; + struct sockaddr_storage ss; + struct sockaddr_in6 *ss6; + struct sockaddr_in *ss4; + + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = PF_UNSPEC; + + strcpy (copy, s); + addrs = space = copy; + if (*addrs == '[') { + addrs++; + for (; *space && *space != ']'; space++); + if (!*space) { + Con_Printf ("NET_StringToAdr: invalid IPv6 address %s\n", s); + return 0; + } + *space++ = '\0'; + } + + for (; *space; space++) { + if (*space == ':') { + *space = '\0'; + ports = space + 1; + } + } + + // Con_Printf ("NET_StringToAdr: addrs %s ports %s\n",addrs, ports); + + if ((err = getaddrinfo (addrs, ports, &hints, &resultp))) { + // Error + Con_Printf ("NET_StringToAdr: string %s:\n%s\n", s, gai_strerror (err)); + return 0; + } + + switch (resultp->ai_family) { + case AF_INET: + // convert to ipv6 addr + memset (&ss, 0, sizeof (ss)); + ss6 = (struct sockaddr_in6 *) &ss; + ss4 = (struct sockaddr_in *) resultp->ai_addr; + ss6->sin6_family = AF_INET6; + + memset (&ss6->sin6_addr, 0, sizeof (ss6->sin6_addr)); + ss6->sin6_addr.s6_addr[10] = ss6->sin6_addr.s6_addr[11] = 0xff; + memcpy (&ss6->sin6_addr.s6_addr[12], &ss4->sin_addr, + sizeof (ss4->sin_addr)); + + ss6->sin6_port = ss4->sin_port; + break; + case AF_INET6: + memcpy (&ss, resultp->ai_addr, sizeof (struct sockaddr_in6)); + + break; + default: + Con_Printf + ("NET_StringToAdr: string %s:\nprotocol family %d not supported\n", + s, resultp->ai_family); + return 0; + } + SockadrToNetadr ((struct sockaddr_in6 *) &ss, a); + + return true; +} + +// Returns true if we can't bind the address locally--in other words, +// the IP is NOT one of our interfaces. +qboolean +NET_IsClientLegal (netadr_t *adr) +{ +#if 0 + struct sockaddr_in sadr; + int newsocket; + + if (adr->ip[0] == 127) + return false; // no local connections period + + NetadrToSockadr (adr, &sadr); + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("NET_IsClientLegal: socket: %s", strerror (errno)); + + sadr.sin_port = 0; + + if (bind (newsocket, (void *) &sadr, sizeof (sadr)) == -1) { + // It is not a local address + close (newsocket); + return true; + } + close (newsocket); + return false; +#else + return true; +#endif +} + + +//============================================================================= + +qboolean +NET_GetPacket (void) +{ + int ret; + struct sockaddr_in6 from; + unsigned int fromlen; + + fromlen = sizeof (from); + ret = + recvfrom (net_socket, (void *) net_message_buffer, + sizeof (net_message_buffer), 0, (struct sockaddr *) &from, + &fromlen); + SockadrToNetadr (&from, &net_from); + + if (ret == -1) { +#ifdef _WIN32 + int err = WSAGetLastError (); + + if (err == WSAEMSGSIZE) { + Con_Printf ("Warning: Oversize packet from %s\n", + NET_AdrToString (net_from)); + return false; + } +#else /* _WIN32 */ + int err = errno; + + if (err == ECONNREFUSED) + return false; +#endif /* _WIN32 */ + if (err == EWOULDBLOCK) + return false; + Sys_Printf ("NET_GetPacket: %s\n", strerror (err)); + return false; + } + + net_message.cursize = ret; + if (ret == sizeof (net_message_buffer)) { + Con_Printf ("Oversize packet from %s\n", NET_AdrToString (net_from)); + return false; + } + + return ret; +} + +//============================================================================= + +void +NET_SendPacket (int length, void *data, netadr_t to) +{ + int ret; + struct sockaddr_in6 addr; + + NetadrToSockadr (&to, &addr); + + ret = + sendto (net_socket, data, length, 0, (struct sockaddr *) &addr, + sizeof (addr)); + if (ret == -1) { +#ifdef _WIN32 + int err = WSAGetLastError (); + +#ifndef SERVERONLY + if (err == WSAEADDRNOTAVAIL) + Con_DPrintf ("NET_SendPacket Warning: %i\n", err); +#endif +#else /* _WIN32 */ + int err = errno; + + if (err == ECONNREFUSED) + return; +#endif /* _WIN32 */ + if (err == EWOULDBLOCK) + return; + + Sys_Printf ("NET_SendPacket: %s\n", strerror (err)); + } +} + +//============================================================================= + +int +UDP_OpenSocket (int port) +{ + int newsocket; + struct sockaddr_in6 address; + struct addrinfo hints, *res; + int Error; + char Buf[BUFSIZ], *Host, *Service; + +#ifdef IPV6_BINDV6ONLY + int dummy; +#endif +#ifdef _WIN32 +#define ioctl ioctlsocket + unsigned long _true = true; +#else + int _true = 1; +#endif + int i; + + if ((newsocket = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("UDP_OpenSocket: socket: %s", strerror (errno)); + if (ioctl (newsocket, FIONBIO, &_true) == -1) + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror (errno)); + memset (&address, 0, sizeof (address)); + address.sin6_family = AF_INET6; +//ZOID -- check for interface binding option + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_PASSIVE; + + if ((i = COM_CheckParm ("-ip")) != 0 && i < com_argc) { + Host = com_argv[i + 1]; + } else { + Host = "0::0"; + } + Con_Printf ("Binding to IP Interface Address of %s\n", Host); + + if (port == PORT_ANY) + Service = NULL; + else { + sprintf (Buf, "%5d", port); + Service = Buf; + } + + if ((Error = getaddrinfo (Host, Service, &hints, &res))) + Sys_Error ("UDP_OpenSocket: getaddrinfo: %s", gai_strerror (Error)); + + if ( + (newsocket = + socket (res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) + Sys_Error ("UDP_OpenSocket: socket: %s", strerror (errno)); + // FIONBIO sets non-blocking IO for this socket +#ifdef _WIN32 + if (ioctl (newsocket, FIONBIO, &_true) == -1) +#else /* _WIN32 */ + if (ioctl (newsocket, FIONBIO, (char *) &_true) == -1) +#endif /* _WIN32 */ + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror (errno)); + +#ifdef IPV6_BINDV6ONLY + if (setsockopt (newsocket, IPPROTO_IPV6, IPV6_BINDV6ONLY, &dummy, + sizeof (dummy)) < 0) { + /* I don't care */ + } +#endif + + if (bind (newsocket, res->ai_addr, res->ai_addrlen) < 0) + Sys_Error ("UDP_OpenSocket: bind: %s", strerror (errno)); + + freeaddrinfo (res); + + return newsocket; +} + +void +NET_GetLocalAddress (void) +{ + char buff[MAXHOSTNAMELEN]; + struct sockaddr_in6 address; + unsigned int namelen; + + if (gethostname (buff, MAXHOSTNAMELEN) == -1) + Sys_Error ("Net_GetLocalAddress: gethostname: %s", strerror (errno)); + buff[MAXHOSTNAMELEN - 1] = 0; + + NET_StringToAdr (buff, &net_local_adr); + + namelen = sizeof (address); + if (getsockname (net_socket, (struct sockaddr *) &address, &namelen) == -1) + Sys_Error ("NET_GetLocalAddress: getsockname: %s", strerror (errno)); + net_local_adr.port = address.sin6_port; + + Con_Printf ("IP address %s\n", NET_AdrToString (net_local_adr)); +} + +/* +==================== +NET_Init +==================== +*/ +void +NET_Init (int port) +{ +#ifdef _WIN32 + WORD wVersionRequested; + int r; + + wVersionRequested = MAKEWORD (1, 1); + + r = WSAStartup (MAKEWORD (1, 1), &winsockdata); + if (r) + Sys_Error ("Winsock initialization failed."); +#endif /* _WIN32 */ + + // + // open the single socket to be used for all communications + // + net_socket = UDP_OpenSocket (port); + + // + // init the message buffer + // + net_message.maxsize = sizeof (net_message_buffer); + net_message.data = net_message_buffer; + + // + // determine my name & address + // + NET_GetLocalAddress (); + + Con_Printf ("UDP Initialized\n"); +} + +/* +==================== +NET_Shutdown +==================== +*/ +void +NET_Shutdown (void) +{ +#ifdef _WIN32 + closesocket (net_socket); + WSACleanup (); +#else + close (net_socket); +#endif +}