Add Multicast capabilities for LAN server scanning.

This commit is contained in:
Thilo Schulz 2008-04-09 14:37:42 +00:00
parent 32bd0ab5bd
commit eb3b59308b
10 changed files with 351 additions and 153 deletions

View file

@ -1975,7 +1975,7 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
c = Cmd_Argv(0); c = Cmd_Argv(0);
Com_DPrintf ("CL packet %s: %s\n", NET_AdrToString(from), c); Com_DPrintf ("CL packet %s: %s\n", NET_AdrToStringwPort(from), c);
// challenge from the server we are connecting to // challenge from the server we are connecting to
if ( !Q_stricmp(c, "challengeResponse") ) { if ( !Q_stricmp(c, "challengeResponse") ) {
@ -2008,8 +2008,8 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
} }
if ( !NET_CompareBaseAdr( from, clc.serverAddress ) ) { if ( !NET_CompareBaseAdr( from, clc.serverAddress ) ) {
Com_Printf( "connectResponse from a different address. Ignored.\n" ); Com_Printf( "connectResponse from a different address. Ignored.\n" );
Com_Printf( "%s should have been %s\n", NET_AdrToString( from ), Com_Printf( "%s should have been %s\n", NET_AdrToStringwPort( from ),
NET_AdrToString( clc.serverAddress ) ); NET_AdrToStringwPort( clc.serverAddress ) );
return; return;
} }
Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) ); Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) );
@ -2095,7 +2095,7 @@ void CL_PacketEvent( netadr_t from, msg_t *msg ) {
} }
if ( msg->cursize < 4 ) { if ( msg->cursize < 4 ) {
Com_Printf ("%s: Runt packet\n",NET_AdrToString( from )); Com_Printf ("%s: Runt packet\n", NET_AdrToStringwPort( from ));
return; return;
} }
@ -2104,7 +2104,7 @@ void CL_PacketEvent( netadr_t from, msg_t *msg ) {
// //
if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) { if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
Com_DPrintf ("%s:sequenced packet without connection\n" Com_DPrintf ("%s:sequenced packet without connection\n"
,NET_AdrToString( from ) ); , NET_AdrToStringwPort( from ) );
// FIXME: send a client disconnect? // FIXME: send a client disconnect?
return; return;
} }
@ -2924,7 +2924,6 @@ CL_ServerInfoPacket
void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) { void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
int i, type; int i, type;
char info[MAX_INFO_STRING]; char info[MAX_INFO_STRING];
char* str;
char *infoString; char *infoString;
int prot; int prot;
@ -2955,12 +2954,12 @@ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
{ {
case NA_BROADCAST: case NA_BROADCAST:
case NA_IP: case NA_IP:
str = "udp";
type = 1; type = 1;
break; break;
case NA_IP6:
type = 2;
break;
default: default:
str = "???";
type = 0; type = 0;
break; break;
} }
@ -3013,7 +3012,7 @@ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
if (info[strlen(info)-1] != '\n') { if (info[strlen(info)-1] != '\n') {
strncat(info, "\n", sizeof(info) - 1); strncat(info, "\n", sizeof(info) - 1);
} }
Com_Printf( "%s: %s", NET_AdrToString( from ), info ); Com_Printf( "%s: %s", NET_AdrToStringwPort( from ), info );
} }
} }
@ -3246,6 +3245,8 @@ void CL_LocalServers_f( void ) {
to.type = NA_BROADCAST; to.type = NA_BROADCAST;
NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
to.type = NA_MULTICAST6;
NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
} }
} }
} }
@ -3318,7 +3319,7 @@ void CL_GetPing( int n, char *buf, int buflen, int *pingtime )
return; return;
} }
str = NET_AdrToString( cl_pinglist[n].adr ); str = NET_AdrToStringwPort( cl_pinglist[n].adr );
Q_strncpyz( buf, str, buflen ); Q_strncpyz( buf, str, buflen );
time = cl_pinglist[n].time; time = cl_pinglist[n].time;

View file

@ -251,25 +251,25 @@ static void LAN_GetServerAddressString( int source, int n, char *buf, int buflen
switch (source) { switch (source) {
case AS_LOCAL : case AS_LOCAL :
if (n >= 0 && n < MAX_OTHER_SERVERS) { if (n >= 0 && n < MAX_OTHER_SERVERS) {
Q_strncpyz(buf, NET_AdrToString( cls.localServers[n].adr) , buflen ); Q_strncpyz(buf, NET_AdrToStringwPort( cls.localServers[n].adr) , buflen );
return; return;
} }
break; break;
case AS_MPLAYER : case AS_MPLAYER :
if (n >= 0 && n < MAX_OTHER_SERVERS) { if (n >= 0 && n < MAX_OTHER_SERVERS) {
Q_strncpyz(buf, NET_AdrToString( cls.mplayerServers[n].adr) , buflen ); Q_strncpyz(buf, NET_AdrToStringwPort( cls.mplayerServers[n].adr) , buflen );
return; return;
} }
break; break;
case AS_GLOBAL : case AS_GLOBAL :
if (n >= 0 && n < MAX_GLOBAL_SERVERS) { if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
Q_strncpyz(buf, NET_AdrToString( cls.globalServers[n].adr) , buflen ); Q_strncpyz(buf, NET_AdrToStringwPort( cls.globalServers[n].adr) , buflen );
return; return;
} }
break; break;
case AS_FAVORITES : case AS_FAVORITES :
if (n >= 0 && n < MAX_OTHER_SERVERS) { if (n >= 0 && n < MAX_OTHER_SERVERS) {
Q_strncpyz(buf, NET_AdrToString( cls.favoriteServers[n].adr) , buflen ); Q_strncpyz(buf, NET_AdrToStringwPort( cls.favoriteServers[n].adr) , buflen );
return; return;
} }
break; break;

View file

@ -142,6 +142,7 @@ static char* gamenames[] = {
static char* netnames[] = { static char* netnames[] = {
"???", "???",
"UDP", "UDP",
"UDP6",
NULL NULL
}; };

View file

@ -2475,7 +2475,7 @@ qboolean FS_idPak( char *pak, char *base ) {
/* /*
================ ================
FS_idPak FS_CheckDirTraversal
Check whether the string contains stuff like "../" to prevent directory traversal bugs Check whether the string contains stuff like "../" to prevent directory traversal bugs
and return qtrue if it does. and return qtrue if it does.

View file

@ -686,10 +686,9 @@ qboolean NET_StringToAdr( const char *s, netadr_t *a, netadrtype_t family ) {
return qtrue; return qtrue;
} }
// look for a port number
Q_strncpyz( base, s, sizeof( base ) ); Q_strncpyz( base, s, sizeof( base ) );
if(*base == '[') if(*base == '[' || Q_CountChar(base, ':') > 1)
{ {
// This is an ipv6 address, handle it specially. // This is an ipv6 address, handle it specially.
search = strchr(base, ']'); search = strchr(base, ']');
@ -702,10 +701,14 @@ qboolean NET_StringToAdr( const char *s, netadr_t *a, netadrtype_t family ) {
port = search + 1; port = search + 1;
} }
search = base + 1; if(*base == '[')
search = base + 1;
else
search = base;
} }
else else
{ {
// look for a port number
port = strchr( base, ':' ); port = strchr( base, ':' );
if ( port ) { if ( port ) {

View file

@ -33,7 +33,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#endif #endif
typedef int socklen_t; typedef int socklen_t;
#ifdef ADDRESS_FAMILY
#define sa_family_t ADDRESS_FAMILY
#else
typedef unsigned short sa_family_t; typedef unsigned short sa_family_t;
#endif
#define EAGAIN WSAEWOULDBLOCK #define EAGAIN WSAEWOULDBLOCK
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL #define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#define EAFNOSUPPORT WSAEAFNOSUPPORT #define EAFNOSUPPORT WSAEAFNOSUPPORT
@ -54,6 +59,7 @@ static qboolean winsockInitialized = qfalse;
#include <errno.h> #include <errno.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
@ -79,10 +85,13 @@ typedef int SOCKET;
static qboolean usingSocks = qfalse; static qboolean usingSocks = qfalse;
static int networkingEnabled = 0; static int networkingEnabled = 0;
#define NET_ENABLEV4 0x01 #define NET_ENABLEV4 0x01
#define NET_ENABLEV6 0x02 #define NET_ENABLEV6 0x02
// if this flag is set, always attempt ipv6 connections instead of ipv4 if a v6 address is found. // if this flag is set, always attempt ipv6 connections instead of ipv4 if a v6 address is found.
#define NET_PRIOV6 0x04 #define NET_PRIOV6 0x04
// disables ipv6 multicast support if set.
#define NET_DISABLEMCAST 0x08
static cvar_t *net_enabled; static cvar_t *net_enabled;
static cvar_t *net_socksEnabled; static cvar_t *net_socksEnabled;
@ -95,21 +104,43 @@ static cvar_t *net_ip;
static cvar_t *net_ip6; static cvar_t *net_ip6;
static cvar_t *net_port; static cvar_t *net_port;
static cvar_t *net_port6; static cvar_t *net_port6;
static cvar_t *net_mcast6addr;
static cvar_t *net_mcast6iface;
static struct sockaddr socksRelayAddr; static struct sockaddr socksRelayAddr;
static SOCKET ip_socket = INVALID_SOCKET; static SOCKET ip_socket = INVALID_SOCKET;
static SOCKET ip6_socket = INVALID_SOCKET; static SOCKET ip6_socket = INVALID_SOCKET;
static SOCKET socks_socket = INVALID_SOCKET; static SOCKET socks_socket = INVALID_SOCKET;
static SOCKET multicast6_socket = INVALID_SOCKET;
#define MAX_IPS 16 // Keep track of currently joined multicast group.
static int numIP; static struct ipv6_mreq curgroup;
static int numIP6; // And the currently bound address.
static struct sockaddr_in6 boundto;
#ifndef IF_NAMESIZE
#define IF_NAMESIZE 16
#endif
// use an admin local address per default so that network admins can decide on how to handle quake3 traffic.
#define NET_MULTICAST_IP6 "ff04::696f:7175:616b:6533"
#define MAX_IPS 32
typedef struct
{
char ifname[IF_NAMESIZE];
netadrtype_t type;
sa_family_t family;
struct sockaddr_storage addr;
struct sockaddr_storage netmask;
} nip_localaddr_t;
static nip_localaddr_t localIP[MAX_IPS];
static int numIP;
static byte localIP[MAX_IPS][4];
static byte localIPmask[MAX_IPS][4];
static byte localIP6[MAX_IPS][16];
static byte localIP6mask[MAX_IPS][16];
//============================================================================= //=============================================================================
@ -190,6 +221,12 @@ static void NetadrToSockadr( netadr_t *a, struct sockaddr *s ) {
((struct sockaddr_in6 *)s)->sin6_addr = * ((struct in6_addr *) &a->ip6); ((struct sockaddr_in6 *)s)->sin6_addr = * ((struct in6_addr *) &a->ip6);
((struct sockaddr_in6 *)s)->sin6_port = a->port; ((struct sockaddr_in6 *)s)->sin6_port = a->port;
} }
else if(a->type == NA_MULTICAST6)
{
((struct sockaddr_in6 *)s)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)s)->sin6_addr = curgroup.ipv6mr_multiaddr;
((struct sockaddr_in6 *)s)->sin6_port = a->port;
}
} }
@ -446,7 +483,7 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) {
if(ip_socket != INVALID_SOCKET) if(ip_socket != INVALID_SOCKET)
{ {
fromlen = sizeof(from); fromlen = sizeof(from);
ret = recvfrom( ip_socket, net_message->data, net_message->maxsize, 0, (struct sockaddr *)&from, &fromlen ); ret = recvfrom( ip_socket, net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen );
if (ret == SOCKET_ERROR) if (ret == SOCKET_ERROR)
{ {
@ -490,7 +527,35 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) {
if(ip6_socket != INVALID_SOCKET) if(ip6_socket != INVALID_SOCKET)
{ {
fromlen = sizeof(from); fromlen = sizeof(from);
ret = recvfrom(ip6_socket, net_message->data, net_message->maxsize, 0, (struct sockaddr *)&from, &fromlen); ret = recvfrom(ip6_socket, net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen);
if (ret == SOCKET_ERROR)
{
err = socketError;
if( err != EAGAIN && err != ECONNRESET )
Com_Printf( "NET_GetPacket: %s\n", NET_ErrorString() );
}
else
{
SockadrToNetadr((struct sockaddr *) &from, net_from);
net_message->readcount = 0;
if(ret == net_message->maxsize)
{
Com_Printf( "Oversize packet from %s\n", NET_AdrToString (*net_from) );
return qfalse;
}
net_message->cursize = ret;
return qtrue;
}
}
if(multicast6_socket != INVALID_SOCKET && multicast6_socket != ip6_socket)
{
fromlen = sizeof(from);
ret = recvfrom(multicast6_socket, net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen);
if (ret == SOCKET_ERROR) if (ret == SOCKET_ERROR)
{ {
@ -515,6 +580,7 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) {
} }
} }
return qfalse; return qfalse;
} }
@ -531,12 +597,18 @@ void Sys_SendPacket( int length, const void *data, netadr_t to ) {
int ret = SOCKET_ERROR; int ret = SOCKET_ERROR;
struct sockaddr_storage addr; struct sockaddr_storage addr;
if( to.type != NA_BROADCAST && to.type != NA_IP && to.type != NA_IP6 ) { if( to.type != NA_BROADCAST && to.type != NA_IP && to.type != NA_IP6 && to.type != NA_MULTICAST6)
{
Com_Error( ERR_FATAL, "Sys_SendPacket: bad address type" ); Com_Error( ERR_FATAL, "Sys_SendPacket: bad address type" );
return; return;
} }
if((ip_socket == INVALID_SOCKET && to.type == NA_IP) || (ip6_socket == INVALID_SOCKET && to.type == NA_IP6)) if( (ip_socket == INVALID_SOCKET && to.type == NA_IP) ||
(ip6_socket == INVALID_SOCKET && to.type == NA_IP6) ||
(ip6_socket == INVALID_SOCKET && to.type == NA_MULTICAST6) )
return;
if(net_enabled->integer & NET_DISABLEMCAST)
return; return;
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
@ -586,8 +658,9 @@ LAN clients will have their rate var ignored
================== ==================
*/ */
qboolean Sys_IsLANAddress( netadr_t adr ) { qboolean Sys_IsLANAddress( netadr_t adr ) {
int index, run; int index, run, addrsize;
qboolean differed; qboolean differed;
byte *compareadr, *comparemask, *compareip;
if( adr.type == NA_LOOPBACK ) { if( adr.type == NA_LOOPBACK ) {
return qtrue; return qtrue;
@ -608,23 +681,6 @@ qboolean Sys_IsLANAddress( netadr_t adr ) {
if(adr.ip[0] == 127) if(adr.ip[0] == 127)
return qtrue; return qtrue;
for(index = 0; index < numIP; index++)
{
differed = qfalse;
for(run = 0; run < sizeof(*localIP); run++)
{
if((localIP[index][run] & localIPmask[index][run]) != (adr.ip[run] & localIPmask[index][run]))
{
differed = qtrue;
break;
}
}
if(!differed)
return qtrue;
}
} }
else if(adr.type == NA_IP6) else if(adr.type == NA_IP6)
{ {
@ -632,14 +688,34 @@ qboolean Sys_IsLANAddress( netadr_t adr ) {
return qtrue; return qtrue;
if((adr.ip6[0] & 0xfe) == 0xfc) if((adr.ip6[0] & 0xfe) == 0xfc)
return qtrue; return qtrue;
}
for(index = 0; index < numIP6; index++)
// Now compare against the networks this computer is member of.
for(index = 0; index < numIP; index++)
{
if(localIP[index].type == adr.type)
{ {
differed = qfalse; if(adr.type == NA_IP)
for(run = 0; run < sizeof(*localIP6); run++)
{ {
if((localIP6[index][run] & localIP6mask[index][run]) != (adr.ip6[run] & localIP6mask[index][run])) compareip = (byte *) &((struct sockaddr_in *) &localIP[index].addr)->sin_addr.s_addr;
comparemask = (byte *) &((struct sockaddr_in *) &localIP[index].netmask)->sin_addr.s_addr;
compareadr = adr.ip;
addrsize = sizeof(adr.ip);
}
else if(adr.type == NA_IP6)
{
compareip = (byte *) &((struct sockaddr_in6 *) &localIP[index].addr)->sin6_addr;
comparemask = (byte *) &((struct sockaddr_in6 *) &localIP[index].netmask)->sin6_addr;
compareadr = adr.ip6;
addrsize = sizeof(adr.ip6);
}
differed = qfalse;
for(run = 0; run < addrsize; run++)
{
if((compareip[run] & comparemask[run]) != (compareadr[run] & comparemask[run]))
{ {
differed = qtrue; differed = qtrue;
break; break;
@ -648,6 +724,7 @@ qboolean Sys_IsLANAddress( netadr_t adr ) {
if(!differed) if(!differed)
return qtrue; return qtrue;
} }
} }
@ -661,27 +738,16 @@ Sys_ShowIP
*/ */
void Sys_ShowIP(void) { void Sys_ShowIP(void) {
int i; int i;
char buf[NET_ADDRSTRMAXLEN]; char addrbuf[NET_ADDRSTRMAXLEN];
struct sockaddr_in sa;
struct sockaddr_in6 sa6;
for (i = 0; i < numIP; i++) for(i = 0; i < numIP; i++)
{ {
memset(&sa, 0, sizeof(sa)); Sys_SockaddrToString(addrbuf, sizeof(addrbuf), (struct sockaddr *) &localIP[i].addr, sizeof((*localIP).addr));
memcpy( &sa.sin_addr.s_addr, localIP[i], sizeof(sa.sin_addr.s_addr) ); if(localIP[i].type == NA_IP)
sa.sin_family = AF_INET; Com_Printf( "IP: %s\n", addrbuf);
Sys_SockaddrToString(buf, sizeof(buf), (struct sockaddr *) &sa, sizeof(sa)); else if(localIP[i].type == NA_IP6)
Com_Printf( "IP: %s\n", buf); Com_Printf( "IP6: %s\n", addrbuf);
}
for (i = 0; i < numIP6; i++)
{
memset(&sa6, 0, sizeof(sa6));
memcpy( &sa6.sin6_addr, localIP6[i], sizeof(sa6.sin6_addr) );
sa6.sin6_family = AF_INET6;
Sys_SockaddrToString(buf, sizeof(buf), (struct sockaddr *) &sa6, sizeof(sa6));
Com_Printf( "IP6: %s\n", buf);
} }
} }
@ -765,7 +831,7 @@ int NET_IPSocket( char *net_interface, int port, int *err ) {
NET_IP6Socket NET_IP6Socket
==================== ====================
*/ */
int NET_IP6Socket( char *net_interface, int port, int *err ) { int NET_IP6Socket( char *net_interface, int port, struct sockaddr_in6 *bindto, int *err ) {
SOCKET newsocket; SOCKET newsocket;
struct sockaddr_in6 address; struct sockaddr_in6 address;
qboolean _true = qtrue; qboolean _true = qtrue;
@ -773,12 +839,16 @@ int NET_IP6Socket( char *net_interface, int port, int *err ) {
*err = 0; *err = 0;
if( net_interface ) { if( net_interface )
Com_Printf( "Opening IP6 socket: [%s]:%i\n", net_interface, port ); {
// Print the name in brackets if there is a colon:
if(Q_CountChar(net_interface, ':'))
Com_Printf( "Opening IP6 socket: [%s]:%i\n", net_interface, port );
else
Com_Printf( "Opening IP6 socket: %s:%i\n", net_interface, port );
} }
else { else
Com_Printf( "Opening IP6 socket: localhost:%i\n", port ); Com_Printf( "Opening IP6 socket: localhost:%i\n", port );
}
if( ( newsocket = socket( PF_INET6, SOCK_DGRAM, IPPROTO_UDP ) ) == INVALID_SOCKET ) { if( ( newsocket = socket( PF_INET6, SOCK_DGRAM, IPPROTO_UDP ) ) == INVALID_SOCKET ) {
*err = socketError; *err = socketError;
@ -803,12 +873,6 @@ int NET_IP6Socket( char *net_interface, int port, int *err ) {
} }
#endif #endif
// make it broadcast capable
if( setsockopt( newsocket, SOL_SOCKET, SO_BROADCAST, (char *) &i, sizeof(i) ) == SOCKET_ERROR ) {
Com_Printf( "WARNING: NET_IP6Socket: setsockopt SO_BROADCAST: %s\n", NET_ErrorString() );
// return newsocket;
}
if( !net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost") ) { if( !net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost") ) {
address.sin6_family = AF_INET6; address.sin6_family = AF_INET6;
address.sin6_addr = in6addr_any; address.sin6_addr = in6addr_any;
@ -835,10 +899,115 @@ int NET_IP6Socket( char *net_interface, int port, int *err ) {
closesocket( newsocket ); closesocket( newsocket );
return INVALID_SOCKET; return INVALID_SOCKET;
} }
if(bindto)
*bindto = address;
return newsocket; return newsocket;
} }
/*
====================
NET_SetMulticast
Set the current multicast group
====================
*/
void NET_SetMulticast6(void)
{
struct sockaddr_in6 addr;
if(!*net_mcast6addr->string || !Sys_StringToSockaddr(net_mcast6addr->string, (struct sockaddr *) &addr, sizeof(addr), AF_INET6))
{
Com_Printf("WARNING: NET_JoinMulticast6: Incorrect multicast address given, "
"please set cvar %s to a sane value.\n", net_mcast6addr->name);
Cvar_Set(net_enabled->name, va("%d", net_enabled->integer | NET_DISABLEMCAST));
return;
}
memcpy(&curgroup.ipv6mr_multiaddr, &addr.sin6_addr, sizeof(curgroup.ipv6mr_multiaddr));
if(!*net_mcast6iface->string)
{
#ifdef _WIN32
curgroup.ipv6mr_interface = atoi(net_mcast6iface->string);
#else
curgroup.ipv6mr_interface = if_nametoindex(net_mcast6iface->string);
#endif
}
else
curgroup.ipv6mr_interface = 0;
}
/*
====================
NET_JoinMulticast
Join an ipv6 multicast group
====================
*/
void NET_JoinMulticast6(void)
{
int err;
if(ip6_socket == INVALID_SOCKET || multicast6_socket != INVALID_SOCKET || net_enabled->integer & NET_DISABLEMCAST)
return;
if(IN6_IS_ADDR_MULTICAST(&boundto.sin6_addr) || IN6_IS_ADDR_UNSPECIFIED(&boundto.sin6_addr))
{
// The way the socket was bound does not prohibit receiving multi-cast packets. So we don't need to open a new one.
multicast6_socket = ip6_socket;
}
else
{
if((multicast6_socket = NET_IP6Socket(net_mcast6addr->string, ntohs(boundto.sin6_port), NULL, &err)) == INVALID_SOCKET)
{
// If the OS does not support binding to multicast addresses, like WinXP, at least try with the normal file descriptor.
multicast6_socket = ip6_socket;
}
}
if(curgroup.ipv6mr_interface)
{
if (setsockopt(multicast6_socket, IPPROTO_IPV6, IPV6_MULTICAST_IF,
(char *) &curgroup.ipv6mr_interface, sizeof(curgroup.ipv6mr_interface)) < 0)
{
Com_Printf("NET_JoinMulticast6: Couldn't set scope on multicast socket: %s\n", NET_ErrorString());
if(multicast6_socket != ip6_socket)
{
closesocket(multicast6_socket);
multicast6_socket = INVALID_SOCKET;
return;
}
}
}
if (setsockopt(multicast6_socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &curgroup, sizeof(curgroup)))
{
Com_Printf("NET_JoinMulticast6: Couldn't join multicast group: %s\n", NET_ErrorString());
if(multicast6_socket != ip6_socket)
{
closesocket(multicast6_socket);
multicast6_socket = INVALID_SOCKET;
return;
}
}
}
void NET_LeaveMulticast6()
{
if(multicast6_socket != INVALID_SOCKET)
{
if(multicast6_socket != ip6_socket)
closesocket(multicast6_socket);
else
setsockopt(multicast6_socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (char *) &curgroup, sizeof(curgroup));
multicast6_socket = INVALID_SOCKET;
}
}
/* /*
==================== ====================
@ -1022,68 +1191,34 @@ void NET_OpenSocks( int port ) {
NET_GetLocalAddress NET_GetLocalAddress
===================== =====================
*/ */
int NET_AddLocalAddress(struct sockaddr *sa) void NET_AddLocalAddress(char *ifname, struct sockaddr *addr, struct sockaddr *netmask)
{ {
struct sockaddr_in *sin; int addrlen;
struct sockaddr_in6 *sin6; sa_family_t family = addr->sa_family;
int index;
if(sa->sa_family == AF_INET) if(numIP < MAX_IPS)
{ {
if(numIP > MAX_IPS) if(family == AF_INET)
return -1;
sin = (struct sockaddr_in *) sa;
for(index = 0; index < numIP; index++)
{ {
if( !memcmp(&localIP[index], &sin->sin_addr.s_addr, sizeof(*localIP)) ) addrlen = sizeof(struct sockaddr_in);
break; localIP[numIP].type = NA_IP;
} }
else if(family == AF_INET6)
if(index >= numIP) {
memcpy(localIP[numIP++], &sin->sin_addr.s_addr, sizeof(*localIP)); addrlen = sizeof(struct sockaddr_in6);
localIP[numIP].type = NA_IP6;
}
else
return;
Q_strncpyz(localIP[numIP].ifname, ifname, sizeof(localIP[numIP].ifname));
return numIP - 1; localIP[numIP].family = family;
}
else if(sa->sa_family == AF_INET6)
{
if(numIP6 > MAX_IPS)
return -1;
sin6 = (struct sockaddr_in6 *) sa; memcpy(&localIP[numIP].addr, addr, addrlen);
memcpy(&localIP[numIP].netmask, netmask, addrlen);
for(index = 0; index < numIP6; index++) numIP++;
{
if( !memcmp(&localIP6[index], &sin6->sin6_addr, sizeof(*localIP6)) )
break;
}
if(index >= numIP6)
memcpy(localIP6[numIP6++], &sin6->sin6_addr, sizeof(*localIP6));
return numIP6 - 1;
}
return -1;
}
void NET_AddLocalNetmask(struct sockaddr *sa, int position)
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
if(sa->sa_family == AF_INET)
{
sin = (struct sockaddr_in *) sa;
memcpy(localIPmask[position], &sin->sin_addr.s_addr, sizeof(*localIPmask));
}
else if(sa->sa_family == AF_INET6)
{
sin6 = (struct sockaddr_in6 *) sa;
memcpy(localIP6mask[position], &sin6->sin6_addr, sizeof(*localIP6mask));
} }
} }
@ -1091,7 +1226,6 @@ void NET_AddLocalNetmask(struct sockaddr *sa, int position)
void NET_GetLocalAddress(void) void NET_GetLocalAddress(void)
{ {
struct ifaddrs *ifap, *search; struct ifaddrs *ifap, *search;
int retval;
if(getifaddrs(&ifap)) if(getifaddrs(&ifap))
Com_Printf("NET_GetLocalAddress: Unable to get list of network interfaces: %s\n", NET_ErrorString()); Com_Printf("NET_GetLocalAddress: Unable to get list of network interfaces: %s\n", NET_ErrorString());
@ -1099,8 +1233,9 @@ void NET_GetLocalAddress(void)
{ {
for(search = ifap; search; search = search->ifa_next) for(search = ifap; search; search = search->ifa_next)
{ {
if((retval = NET_AddLocalAddress(search->ifa_addr)) >= 0) // Only add interfaces that are up.
NET_AddLocalNetmask(search->ifa_netmask, retval); if(ifap->ifa_flags & IFF_UP)
NET_AddLocalAddress(search->ifa_name, search->ifa_addr, search->ifa_netmask);
} }
freeifaddrs(ifap); freeifaddrs(ifap);
@ -1114,6 +1249,8 @@ void NET_GetLocalAddress( void ) {
struct addrinfo hint; struct addrinfo hint;
struct addrinfo *res = NULL; struct addrinfo *res = NULL;
struct addrinfo *search; struct addrinfo *search;
struct sockaddr_in mask4;
struct sockaddr_in6 mask6;
if(gethostname( hostname, 256 ) == SOCKET_ERROR) if(gethostname( hostname, 256 ) == SOCKET_ERROR)
return; return;
@ -1128,13 +1265,25 @@ void NET_GetLocalAddress( void ) {
if(getaddrinfo(hostname, NULL, &hint, &res)) if(getaddrinfo(hostname, NULL, &hint, &res))
return; return;
/* On operating systems where it's more difficult to find out the configured interfaces, we'll just assume a
* netmask with all bits set. */
memset(&mask4, 0, sizeof(mask4));
memset(&mask6, 0, sizeof(mask6));
mask4.sin_family = AF_INET;
memset(&mask4.sin_addr.s_addr, 0xFF, sizeof(mask4.sin_addr.s_addr));
mask6.sin6_family = AF_INET6;
memset(&mask6.sin6_addr, 0xFF, sizeof(mask6.sin6_addr));
// add all IPs from returned list. // add all IPs from returned list.
for(search = res; search; search = search->ai_next) for(search = res; search; search = search->ai_next)
NET_AddLocalAddress(search->ai_addr); {
if(search->ai_family == AF_INET)
NET_AddLocalAddress("", search->ai_addr, (struct sockaddr *) &mask4);
else if(search->ai_family == AF_INET6)
NET_AddLocalAddress("", search->ai_addr, (struct sockaddr *) &mask6);
}
memset(localIPmask, 0, sizeof(localIPmask));
memset(localIP6mask, 0xFF, sizeof(localIP6mask));
Sys_ShowIP(); Sys_ShowIP();
} }
#endif #endif
@ -1158,6 +1307,8 @@ void NET_OpenIP( void ) {
port = net_port->integer; port = net_port->integer;
port6 = net_port6->integer; port6 = net_port6->integer;
NET_GetLocalAddress();
// automatically scan for a valid port, so multiple // automatically scan for a valid port, so multiple
// dedicated servers can be started without requiring // dedicated servers can be started without requiring
// a different net_port for each one // a different net_port for each one
@ -1188,7 +1339,7 @@ void NET_OpenIP( void ) {
{ {
for( i = 0 ; i < 10 ; i++ ) for( i = 0 ; i < 10 ; i++ )
{ {
ip6_socket = NET_IP6Socket(net_ip6->string, port6 + i, &err); ip6_socket = NET_IP6Socket(net_ip6->string, port6 + i, &boundto, &err);
if (ip6_socket != INVALID_SOCKET) if (ip6_socket != INVALID_SOCKET)
{ {
Cvar_SetValue( "net_port6", port6 + i ); Cvar_SetValue( "net_port6", port6 + i );
@ -1203,8 +1354,6 @@ void NET_OpenIP( void ) {
if(ip6_socket == INVALID_SOCKET) if(ip6_socket == INVALID_SOCKET)
Com_Printf( "WARNING: Couldn't bind to a v6 ip address.\n"); Com_Printf( "WARNING: Couldn't bind to a v6 ip address.\n");
} }
NET_GetLocalAddress();
} }
@ -1226,7 +1375,7 @@ static qboolean NET_GetCvars( void ) {
} }
#ifdef DEDICATED #ifdef DEDICATED
// I want server owners to explicitly turn ipv6 support on. // I want server owners to explicitly turn on ipv6 support.
net_enabled = Cvar_Get( "net_enabled", "1", CVAR_LATCH | CVAR_ARCHIVE ); net_enabled = Cvar_Get( "net_enabled", "1", CVAR_LATCH | CVAR_ARCHIVE );
#else #else
/* End users have it enabled so they can connect to ipv6-only hosts, but ipv4 will be /* End users have it enabled so they can connect to ipv6-only hosts, but ipv4 will be
@ -1234,6 +1383,17 @@ static qboolean NET_GetCvars( void ) {
net_enabled = Cvar_Get( "net_enabled", "3", CVAR_LATCH | CVAR_ARCHIVE ); net_enabled = Cvar_Get( "net_enabled", "3", CVAR_LATCH | CVAR_ARCHIVE );
#endif #endif
// Some cvars for configuring multicast options which facilitates scanning for servers on local subnets.
if( net_mcast6addr && net_mcast6addr->modified ) {
modified = qtrue;
}
net_mcast6addr = Cvar_Get( "net_mcast6addr", NET_MULTICAST_IP6, CVAR_LATCH | CVAR_ARCHIVE );
if( net_mcast6iface && net_mcast6iface->modified ) {
modified = qtrue;
}
net_mcast6iface = Cvar_Get( "net_mcast6iface", "0", CVAR_LATCH | CVAR_ARCHIVE );
if( net_socksEnabled && net_socksEnabled->modified ) { if( net_socksEnabled && net_socksEnabled->modified ) {
modified = qtrue; modified = qtrue;
} }
@ -1314,6 +1474,14 @@ void NET_Config( qboolean enableNetworking ) {
ip_socket = INVALID_SOCKET; ip_socket = INVALID_SOCKET;
} }
if(multicast6_socket)
{
if(multicast6_socket != ip6_socket)
closesocket(multicast6_socket);
multicast6_socket = INVALID_SOCKET;
}
if ( ip6_socket != INVALID_SOCKET ) { if ( ip6_socket != INVALID_SOCKET ) {
closesocket( ip6_socket ); closesocket( ip6_socket );
ip6_socket = INVALID_SOCKET; ip6_socket = INVALID_SOCKET;
@ -1326,9 +1494,12 @@ void NET_Config( qboolean enableNetworking ) {
} }
if( start ) { if( start )
if (net_enabled->integer) { {
if (net_enabled->integer)
{
NET_OpenIP(); NET_OpenIP();
NET_SetMulticast6();
} }
} }
} }

View file

@ -921,6 +921,18 @@ char *Q_CleanStr( char *string ) {
return string; return string;
} }
int Q_CountChar(const char *string, char tocount)
{
int count;
for(count = 0; *string; string++)
{
if(*string == tocount)
count++;
}
return count;
}
void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) { void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
int len; int len;

View file

@ -693,6 +693,8 @@ void Q_strcat( char *dest, int size, const char *src );
int Q_PrintStrlen( const char *string ); int Q_PrintStrlen( const char *string );
// removes color sequences from string // removes color sequences from string
char *Q_CleanStr( char *string ); char *Q_CleanStr( char *string );
// Count the number of char tocount encountered in string
int Q_CountChar(const char *string, char tocount);
//============================================= //=============================================

View file

@ -138,6 +138,7 @@ typedef enum {
NA_BROADCAST, NA_BROADCAST,
NA_IP, NA_IP,
NA_IP6, NA_IP6,
NA_MULTICAST6,
NA_UNSPEC NA_UNSPEC
} netadrtype_t; } netadrtype_t;
@ -172,6 +173,8 @@ const char *NET_AdrToString (netadr_t a);
const char *NET_AdrToStringwPort (netadr_t a); const char *NET_AdrToStringwPort (netadr_t a);
qboolean NET_StringToAdr ( const char *s, netadr_t *a, netadrtype_t family); qboolean NET_StringToAdr ( const char *s, netadr_t *a, netadrtype_t family);
qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message); qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message);
void NET_JoinMulticast6(void);
void NET_LeaveMulticast6(void);
void NET_Sleep(int msec); void NET_Sleep(int msec);

View file

@ -286,6 +286,9 @@ void SV_Startup( void ) {
} }
Cvar_Set( "sv_running", "1" ); Cvar_Set( "sv_running", "1" );
// Join the ipv6 multicast group now that a map is running so clients can scan for us on the local network.
NET_JoinMulticast6();
} }
@ -734,6 +737,8 @@ void SV_Shutdown( char *finalmsg ) {
Com_Printf( "----- Server Shutdown (%s) -----\n", finalmsg ); Com_Printf( "----- Server Shutdown (%s) -----\n", finalmsg );
NET_LeaveMulticast6();
if ( svs.clients && !com_errorEntered ) { if ( svs.clients && !com_errorEntered ) {
SV_FinalMessage( finalmsg ); SV_FinalMessage( finalmsg );
} }