Index: code/unix/unix_net.c =================================================================== --- code/unix/unix_net.c (revision 90) +++ code/unix/unix_net.c (working copy) @@ -34,7 +34,9 @@ #include #include #include +#include #include +#include // for if_nametoindex #ifdef MACOS_X #import @@ -45,28 +47,39 @@ #import // for 'struct sockaddr_dl' #endif -static cvar_t *noudp; +static cvar_t *nov4; +static cvar_t *nov6; netadr_t net_local_adr; -int ip_socket; -int ipx_socket; +int ip_socket = 0; +int ip6_socket = 0; +int ipx_socket = 0; #define MAX_IPS 16 static int numIP; +static int numIPv6; static byte localIP[MAX_IPS][4]; +static byte localIPv6[MAX_IPS][16]; +static char multicast_address[INET6_ADDRSTRLEN+1]; +static int scope_id; int NET_Socket (char *net_interface, int port); char *NET_ErrorString (void); +qboolean StringToSockaddr (const char *s, struct sockaddr *sadr, int family, int flags); +const char * Addrv4ToStr(u_char * bytes); +const char * Addrv6ToStr(u_char * bytes); +const char * SockaddrToStr(struct sockaddr * s); //============================================================================= -void NetadrToSockadr (netadr_t *a, struct sockaddr_in *s) +void NetadrToSockadr (netadr_t *a, struct sockaddr *sa) { - memset (s, 0, sizeof(*s)); - if (a->type == NA_BROADCAST) { + struct sockaddr_in *s = (struct sockaddr_in*) sa; + memset (s, 0, sizeof(*s)); + s->sin_family = AF_INET; s->sin_port = a->port; @@ -74,26 +87,73 @@ } else if (a->type == NA_IP) { + struct sockaddr_in *s = (struct sockaddr_in*) sa; + memset (s, 0, sizeof(*s)); + s->sin_family = AF_INET; *(int *)&s->sin_addr = *(int *)&a->ip; s->sin_port = a->port; } +#ifdef ENABLE_IPV6 + else if( a->type == NA_IP6 || a->type == NA_MULTICAST_IP6) + { + struct sockaddr_in6 *s = (struct sockaddr_in6*) sa; + memset (s, 0, sizeof(*s)); + + s->sin6_family = AF_INET6; + s->sin6_port = a->port; + s->sin6_scope_id = a->scope_id; + memcpy(&s->sin6_addr, a->ip6, sizeof(a->ip6)); + } +#endif } -void SockadrToNetadr (struct sockaddr_in *s, netadr_t *a) +void SockadrToNetadr (struct sockaddr *sa, netadr_t *a) { + if(sa->sa_family == AF_INET) + { + struct sockaddr_in* s = (struct sockaddr_in*) sa; + *(int *)&a->ip = *(int *)&s->sin_addr; a->port = s->sin_port; a->type = NA_IP; + } +#ifdef ENABLE_IPV6 + else if(sa->sa_family == AF_INET6) + { + struct sockaddr_in6* s = (struct sockaddr_in6*) sa; + + memcpy(a->ip6, &s->sin6_addr, sizeof(a->ip6)); + a->port = s->sin6_port; + a->scope_id = s->sin6_scope_id; + if(IN6_IS_ADDR_MULTICAST(&s->sin6_addr)) { + a->type = NA_MULTICAST_IP6; + } + else { + a->type = NA_IP6; + } + } +#endif } +// Doesn't seem to be used anywhere char *NET_BaseAdrToString (netadr_t a) { static char s[64]; +#ifdef ENABLE_IPV6 + if(a.type == NA_IP6) + { + struct sockaddr_in6 sa; + NetadrToSockadr(&a, (struct sockaddr*) &sa); + getnameinfo((struct sockaddr*) &sa, sizeof(sa), s, sizeof(s), NULL, 0, NI_NUMERICHOST); + } + else +#endif // well, we can use getnameinfo for IPv4 too...but let's leave it as it is (for sure) Com_sprintf (s, sizeof(s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); + return s; } @@ -105,29 +165,36 @@ 192.246.40.70 ============= */ -qboolean Sys_StringToSockaddr (const char *s, struct sockaddr *sadr) +qboolean StringToSockaddr (const char *s, struct sockaddr *sadr, int family, int flags) { - struct hostent *h; + struct addrinfo hint; + struct addrinfo* res = NULL; //char *colon; // bk001204 - unused memset (sadr, 0, sizeof(*sadr)); - ((struct sockaddr_in *)sadr)->sin_family = AF_INET; + memset (&hint, 0, sizeof(hint)); - ((struct sockaddr_in *)sadr)->sin_port = 0; - - if ( s[0] >= '0' && s[0] <= '9') + hint.ai_family = family; + hint.ai_flags = flags; + + getaddrinfo(s, NULL, &hint, &res); + if(res != NULL) { - *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(s); + memcpy(sadr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + return qtrue; } else - { - if (! (h = gethostbyname(s)) ) - return qfalse; - *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0]; - } - - return qtrue; + return qfalse; } +qboolean Sys_StringToSockaddr (const char *s, struct sockaddr *sadr) +{ +#ifdef ENABLE_IPV6 + return StringToSockaddr(s,sadr,PF_UNSPEC, 0); +#else + return StringToSockaddr(s,sadr,PF_INET, 0); +#endif +} /* ============= @@ -142,12 +209,12 @@ */ qboolean Sys_StringToAdr (const char *s, netadr_t *a) { - struct sockaddr_in sadr; + struct sockaddr_storage sadr; - if (!Sys_StringToSockaddr (s, (struct sockaddr *)&sadr)) + if (!StringToSockaddr (s, (struct sockaddr *)&sadr, PF_UNSPEC, 0)) return qfalse; - SockadrToNetadr (&sadr, a); + SockadrToNetadr ((struct sockaddr*) &sadr, a); return qtrue; } @@ -158,16 +225,28 @@ qboolean Sys_GetPacket (netadr_t *net_from, msg_t *net_message) { int ret; - struct sockaddr_in from; + struct sockaddr_storage from; socklen_t fromlen; int net_socket; int protocol; + int protocol_max; int err; - for (protocol = 0 ; protocol < 2 ; protocol++) +#ifdef ENABLE_IPV6 + protocol_max = 3; +#else + protocol_max = 2; +#endif + + for (protocol = 0 ; protocol < protocol_max ; protocol++) { if (protocol == 0) net_socket = ip_socket; +#ifdef ENABLE_IPV6 + else if(protocol == 1) { + net_socket = ip6_socket; + } +#endif else net_socket = ipx_socket; @@ -178,7 +257,7 @@ ret = recvfrom (net_socket, net_message->data, net_message->maxsize , 0, (struct sockaddr *)&from, &fromlen); - SockadrToNetadr (&from, net_from); + SockadrToNetadr ((struct sockaddr*) &from, net_from); // bk000305: was missing net_message->readcount = 0; @@ -211,7 +290,7 @@ void Sys_SendPacket( int length, const void *data, netadr_t to ) { int ret; - struct sockaddr_in addr; + struct sockaddr_storage addr; int net_socket; if (to.type == NA_BROADCAST) @@ -230,6 +309,22 @@ { net_socket = ipx_socket; } +#ifdef ENABLE_IPV6 + else if (to.type == NA_IP6) + { + net_socket = ip6_socket; + } + else if(to.type == NA_MULTICAST_IP6) + { + StringToSockaddr(multicast_address, (struct sockaddr*) &addr, AF_INET6, AI_NUMERICHOST); + // Must specify interface when sending multicast + ((struct sockaddr_in6*) &addr)->sin6_scope_id = scope_id; + net_socket = ip6_socket; + + // Debugging - JFT + Com_Printf ("Sending IPv6 Multicast packet to : %s\n", SockaddrToStr((struct sockaddr *)&addr)); + } +#endif else { Com_Error (ERR_FATAL, "NET_SendPacket: bad address type"); return; @@ -238,7 +333,9 @@ if (!net_socket) return; - NetadrToSockadr (&to, &addr); + if(to.type != NA_MULTICAST_IP6) { + NetadrToSockadr (&to, (struct sockaddr*) &addr); + } ret = sendto (net_socket, data, length, 0, (struct sockaddr *)&addr, sizeof(addr) ); if (ret == -1) @@ -269,6 +366,15 @@ return qtrue; } +#ifdef ENABLE_IPV6 + if(adr.type == NA_IP6) + { + if(adr.ip6[0] == 0xfe && adr.ip6[1] == 0x80) // Link-local addresses start with fe80 + return qtrue; + return qfalse; + } +#endif + if( adr.type != NA_IP ) { return qfalse; } @@ -329,6 +435,33 @@ /* ================== +Addrv4ToStr +================== +*/ +const char * Addrv4ToStr(u_char * bytes) { + static char address[INET6_ADDRSTRLEN+1]; + + Com_sprintf( address, sizeof(address), "%i.%i.%i.%i", bytes[0], bytes[1], bytes[2], bytes[3]); + return address; // Return static buffer. Non-reentrant. +} + +/* +================== +Addrv6ToStr +================== +*/ +const char * Addrv6ToStr(u_char * bytes) { + static char address[INET6_ADDRSTRLEN+1]; + + Com_sprintf( address, sizeof(address), + "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); + return address; // Return static buffer. Non-reentrant. +} + +/* +================== Sys_ShowIP ================== */ @@ -336,11 +469,29 @@ int i; for (i = 0; i < numIP; i++) { - Com_Printf( "IP: %i.%i.%i.%i\n", localIP[i][0], localIP[i][1], localIP[i][2], localIP[i][3] ); + Com_Printf( "IP: %s\n", Addrv4ToStr(localIP[i])); } + + for (i = 0; i < numIPv6; i++) { + Com_Printf( "IPv6: %s\n", Addrv6ToStr(localIPv6[i])); + } } /* +================== +SockaddrToStr +================== +*/ +const char * SockaddrToStr(struct sockaddr * s) { + if(s->sa_family == AF_INET6) { + return Addrv6ToStr((u_char *)&((struct sockaddr_in6 *)s)->sin6_addr); + } + else { + return Addrv4ToStr((u_char *)&((struct sockaddr_in *)s)->sin_addr); + } +} + +/* ===================== NET_GetLocalAddress ===================== @@ -370,7 +521,7 @@ ifc.ifc_buf = (caddr_t)requestBuffer; // Since we get at this info via an ioctl, we need a temporary little socket. This will only get AF_INET interfaces, but we probably don't care about anything else. If we do end up caring later, we should add a ONAddressFamily and at a -interfaces method to it. - family = AF_INET; + family = AF_INET; // Tocheck - JFT if ((interfaceSocket = socket(family, SOCK_DGRAM, 0)) < 0) { Com_Printf("NET_GetLocalAddress: Unable to create temporary socket, errno = %d\n", errno); return; @@ -381,7 +532,6 @@ return; } - linkInterface = (struct ifreq *) ifc.ifc_buf; while ((char *) linkInterface < &ifc.ifc_buf[ifc.ifc_len]) { unsigned int nameLength; @@ -438,44 +588,42 @@ } #else + void NET_GetLocalAddress( void ) { char hostname[256]; - struct hostent *hostInfo; - // int error; // bk001204 - unused - char *p; - int ip; - int n; + struct addrinfo hint; + struct addrinfo * res = NULL; + struct addrinfo * current_ai = NULL; + struct sockaddr_in * sa; + struct sockaddr_in6 * sa6; if ( gethostname( hostname, 256 ) == -1 ) { return; } - - hostInfo = gethostbyname( hostname ); - if ( !hostInfo ) { + Com_Printf( "Hostname: %s\n", hostname ); + + memset(&hint, 0, sizeof(hint)); + if(getaddrinfo(hostname, NULL, &hint, &res)) { return; } - - Com_Printf( "Hostname: %s\n", hostInfo->h_name ); - n = 0; - while( ( p = hostInfo->h_aliases[n++] ) != NULL ) { - Com_Printf( "Alias: %s\n", p ); + + current_ai = res; + numIP = numIPv6 = 0; + while(current_ai != NULL && numIP < MAX_IPS && numIPv6 < MAX_IPS) { + if(current_ai->ai_family == AF_INET) { + sa = (struct sockaddr_in *) current_ai->ai_addr; + memcpy(localIP[numIP++], &sa->sin_addr, 4); + } + else if (current_ai->ai_family == AF_INET6) { + sa6 = (struct sockaddr_in6 *) current_ai->ai_addr; + memcpy(localIP[numIPv6++], &sa6->sin6_addr, 16); + } + current_ai = current_ai->ai_next; } - if ( hostInfo->h_addrtype != AF_INET ) { - return; - } - - numIP = 0; - while( ( p = hostInfo->h_addr_list[numIP++] ) != NULL && numIP < MAX_IPS ) { - ip = ntohl( *(int *)p ); - localIP[ numIP ][0] = p[0]; - localIP[ numIP ][1] = p[1]; - localIP[ numIP ][2] = p[2]; - localIP[ numIP ][3] = p[3]; - Com_Printf( "IP: %i.%i.%i.%i\n", ( ip >> 24 ) & 0xff, ( ip >> 16 ) & 0xff, ( ip >> 8 ) & 0xff, ip & 0xff ); - } + Sys_ShowIP(); } -#endif +#endif // !defined(MACOS_X) /* ==================== @@ -484,24 +632,82 @@ */ // bk001204 - prototype needed int NET_IPSocket (char *net_interface, int port); +int NET_IPv6Socket (char *net_interface, int port); void NET_OpenIP (void) { cvar_t *ip; - int port; - int i; + cvar_t *ipv6; + cvar_t *multicast; + cvar_t *interface_id; + char *interface_name; + int port; + int i; + struct sockaddr_in6 address; + qboolean use_default_multicast = qfalse; - ip = Cvar_Get ("net_ip", "localhost", 0); + ip = Cvar_Get( "net_ip", "localhost", CVAR_LATCH ); + port = Cvar_Get( "net_port", va( "%i", PORT_SERVER ), CVAR_LATCH )->integer; + ipv6 = Cvar_Get( "net_ipv6", "localhost", CVAR_LATCH ); + multicast = Cvar_Get( "net_multicast", va( "%i", 0 ), CVAR_LATCH ); + interface_id = Cvar_Get ("net_interface", "NULL", CVAR_LATCH ); - port = Cvar_Get("net_port", va("%i", PORT_SERVER), 0)->value; + // Process interface id + interface_name = (strcmp(interface_id->string, "NULL") ? interface_id->string : NULL); + if(interface_name != NULL) { + scope_id = if_nametoindex(interface_name); + if(!scope_id) { + Com_Printf( "WARNING: NET_OpenIP: Invalid interface index.\n"); + } + } + // Multicast parameters processing + strncpy(multicast_address, IPV6_MCAST_GROUP, sizeof(multicast_address)); + if(multicast->integer) { + // Verify we have an interface id + if(!scope_id) { + Com_Printf( "WARNING: NET_OpenIP: Multicast enabled without an interface index.\n"); + } + + // Check if we have an address + if(strcmp(ipv6->string, "localhost")) { + // Verify the address is multicast + memset(&address, 0, sizeof(address)); + Sys_StringToSockaddr( ipv6->string, (struct sockaddr *)&address ); + if(IN6_IS_ADDR_MULTICAST(&address.sin6_addr)) { + strncpy(multicast_address, ipv6->string, sizeof(multicast_address)); + } + else { + Com_Printf( "WARNING: NET_OpenIP: Multicast enabled but specified address is unicast.\n"); + } + } + else { + // Use default group + use_default_multicast = qtrue; + Com_Printf("Using default IPv6 multicast address %s.\n", multicast_address); + } + } for ( i = 0 ; i < 10 ; i++ ) { - ip_socket = NET_IPSocket (ip->string, port + i); - if ( ip_socket ) { - Cvar_SetValue( "net_port", port + i ); - NET_GetLocalAddress(); - return; + if (!ip_socket && !nov4->integer) { + ip_socket = NET_IPSocket (ip->string, port + i); + continue; } + +#ifdef ENABLE_IPV6 + if(!ip6_socket && !nov6->integer) { + if(use_default_multicast == qtrue) { + ip6_socket = NET_IPv6Socket (multicast_address, port + i); + } + else { + ip6_socket = NET_IPv6Socket (ipv6->string, port + i); + } + continue; + } +#endif + + NET_GetLocalAddress(); + return; } + Com_Error (ERR_FATAL, "Couldn't allocate IP port"); } @@ -513,9 +719,14 @@ */ void NET_Init (void) { - noudp = Cvar_Get ("net_noudp", "0", 0); + nov4 = Cvar_Get ("net_nov4", va( "%i", 0 ), CVAR_LATCH); + nov6 = Cvar_Get ("net_nov6", va( "%i", 0 ), CVAR_LATCH); + + ip_socket = 0; + ip6_socket = 0; + // open sockets - if (! noudp->value) { + if (!nov4->integer || !nov6->integer) { NET_OpenIP (); } } @@ -562,7 +773,7 @@ if (!net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost")) address.sin_addr.s_addr = INADDR_ANY; else - Sys_StringToSockaddr (net_interface, (struct sockaddr *)&address); + StringToSockaddr (net_interface, (struct sockaddr *)&address, AF_INET, 0); if (port == PORT_ANY) address.sin_port = 0; @@ -581,6 +792,86 @@ return newsocket; } +#ifdef ENABLE_IPV6 +int NET_IPv6Socket (char *net_interface, int port) +{ + int newsocket; + struct sockaddr_in6 address; + struct ipv6_mreq mreq; + qboolean _qtrue = qtrue; + int one = 1; + + if ( net_interface ) { + Com_Printf("Opening IPv6 socket: %s:%i\n", net_interface, port ); + } else { + Com_Printf("Opening IPv6 socket: IPv6 unspecified address:%i\n", port ); + } + + if ((newsocket = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) + { + Com_Printf ("ERROR: UDP_OpenSocket: socket: %s", NET_ErrorString()); + return 0; + } + + // make it non-blocking + if (ioctl (newsocket, FIONBIO, &_qtrue) == -1) + { + Com_Printf ("ERROR: UDP_OpenSocket: ioctl FIONBIO:%s\n", NET_ErrorString()); + return 0; + } + + if (setsockopt( newsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))) { + Com_Printf("NET_IPv6Socket: setsockopt(SO_REUSEADDR) failed: %s\n", NET_ErrorString()); + return 0; + } + + if (!net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost")) + { + StringToSockaddr(IPV6_UNSPECIFIED_ADDR, (struct sockaddr *) &address, AF_INET6, AI_PASSIVE); + } + else + StringToSockaddr (net_interface, (struct sockaddr *)&address, PF_INET6, 0); + + if (port == PORT_ANY) + address.sin6_port = 0; + else + address.sin6_port = htons((short)port); + + address.sin6_family = AF_INET6; + address.sin6_scope_id = scope_id; + + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + { + Com_Printf ("ERROR: UDP_OpenSocket: bind: %s\n", NET_ErrorString()); + close (newsocket); + return 0; + } + + if(IN6_IS_ADDR_MULTICAST(&address.sin6_addr)) { + // Fill in the ipv6_mreq + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, &address.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); + mreq.ipv6mr_interface = scope_id; + + Com_Printf( "NET_IPv6Socket: Opening multicast socket on interface index %i.\n", + mreq.ipv6mr_interface); // Debug - JFT + + if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char*)&mreq.ipv6mr_interface, + sizeof(mreq.ipv6mr_interface)) < 0) { + Com_Printf("NET_IPv6Socket: IPV6_MULTICAST_IF: %s\n", NET_ErrorString()); + } + + if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq, sizeof(mreq))) { + Com_Printf("NET_IPv6Socket: IPV6_JOIN_GROUP: %s\n", + strerror (errno)); + } + + } + + return newsocket; +} +#endif + /* ==================== NET_Shutdown @@ -621,7 +912,9 @@ FD_ZERO(&fdset); if (stdin_active) FD_SET(0, &fdset); // stdin is processed too + FD_SET(ip_socket, &fdset); // network socket + timeout.tv_sec = msec/1000; timeout.tv_usec = (msec%1000)*1000; select(ip_socket+1, &fdset, NULL, NULL, &timeout); Index: code/win32/win_net.c =================================================================== --- code/win32/win_net.c (revision 90) +++ code/win32/win_net.c (working copy) @@ -20,17 +20,18 @@ =========================================================================== */ // net_wins.c - #include "../game/q_shared.h" #include "../qcommon/qcommon.h" #include "win_local.h" +#include static WSADATA winsockdata; static qboolean winsockInitialized = qfalse; static qboolean usingSocks = qfalse; static qboolean networkingEnabled = qfalse; -static cvar_t *net_noudp; +static cvar_t *net_nov4; +static cvar_t *net_nov6; static cvar_t *net_noipx; static cvar_t *net_socksEnabled; @@ -41,13 +42,24 @@ static struct sockaddr socksRelayAddr; static SOCKET ip_socket; +static SOCKET ip6_socket; static SOCKET socks_socket; static SOCKET ipx_socket; -#define MAX_IPS 16 +char multicast_address[INET6_ADDRSTRLEN+1]; +int scope_id; + +#define MAX_IPS 16 static int numIP; +static int numIPv6; static byte localIP[MAX_IPS][4]; +static byte localIPv6[MAX_IPS][16]; +qboolean StringToSockaddr( const char *s, struct sockaddr *sadr, int family, int flags ); +const char * Addrv4ToStr(u_char * bytes); +const char * Addrv6ToStr(u_char * bytes); +const char * SockaddrToStr(struct sockaddr * s); + //============================================================================= @@ -134,9 +146,19 @@ memset( ((struct sockaddr_ipx *)s)->sa_nodenum, 0xff, 6 ); ((struct sockaddr_ipx *)s)->sa_socket = a->port; } +#ifdef ENABLE_IPV6 + else if( a->type == NA_IP6 || a->type == NA_MULTICAST_IP6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6*) s; + memset (s, 0, sizeof(*sa6)); + + sa6->sin6_family = AF_INET6; + sa6->sin6_port = a->port; + sa6->sin6_scope_id = a->scope_id; + memcpy(&(sa6->sin6_addr), a->ip6, sizeof((sa6->sin6_addr))); + } +#endif } - void SockadrToNetadr( struct sockaddr *s, netadr_t *a ) { if (s->sa_family == AF_INET) { a->type = NA_IP; @@ -149,6 +171,26 @@ memcpy( &a->ipx[4], ((struct sockaddr_ipx *)s)->sa_nodenum, 6 ); a->port = ((struct sockaddr_ipx *)s)->sa_socket; } +#ifdef ENABLE_IPV6 + else if(s->sa_family == AF_INET6) + { + struct sockaddr_in6* sa6 = (struct sockaddr_in6*) s; + + memcpy(a->ip6, &sa6->sin6_addr, sizeof(a->ip6)); + a->port = sa6->sin6_port; + a->scope_id = sa6->sin6_scope_id; + if(IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { + a->type = NA_MULTICAST_IP6; + } + else { + a->type = NA_IP6; + } + } +#endif + else + { + Com_Printf ("SockadrToNetadr: unknown family\n"); + } } @@ -167,12 +209,15 @@ sscanf (copy, "%x", &val); \ ((struct sockaddr_ipx *)sadr)->dest = val -qboolean Sys_StringToSockaddr( const char *s, struct sockaddr *sadr ) { - struct hostent *h; +qboolean StringToSockaddr( const char *s, struct sockaddr *sadr, int family, int flags ) { + char copy[MAX_STRING_CHARS]; int val; - char copy[MAX_STRING_CHARS]; - + int error; + struct addrinfo hint; + struct addrinfo* res = NULL; + memset( sadr, 0, sizeof( *sadr ) ); + memset(&hint, 0, sizeof(hint)); // check for an IPX address if( ( strlen( s ) == 21 ) && ( s[8] == '.' ) ) { @@ -191,22 +236,35 @@ DO(19, sa_nodenum[5]); } else { - ((struct sockaddr_in *)sadr)->sin_family = AF_INET; - ((struct sockaddr_in *)sadr)->sin_port = 0; + hint.ai_family = family; + hint.ai_flags = flags; - if( s[0] >= '0' && s[0] <= '9' ) { - *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(s); - } else { - if( ( h = gethostbyname( s ) ) == 0 ) { - return 0; - } - *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0]; + error = getaddrinfo(s, NULL, &hint, &res); + if(error) + { + Com_Printf( "StringToSockaddr: getaddrinfo returned %s\n", NET_ErrorString()); + return qfalse; } + + if(res != NULL) + { + memcpy(sadr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } } return qtrue; } +qboolean Sys_StringToSockaddr (const char *s, struct sockaddr *sadr) +{ +#ifdef ENABLE_IPV6 + return StringToSockaddr(s, sadr, AF_UNSPEC, 0); +#else + return StringToSockaddr(s, sadr, AF_INET, 0); +#endif +} + #undef DO /* @@ -218,13 +276,12 @@ ============= */ qboolean Sys_StringToAdr( const char *s, netadr_t *a ) { - struct sockaddr sadr; + struct sockaddr_storage sadr; - if ( !Sys_StringToSockaddr( s, &sadr ) ) { + if (!StringToSockaddr (s, (struct sockaddr *)&sadr, PF_UNSPEC, 0)) return qfalse; - } - - SockadrToNetadr( &sadr, a ); + + SockadrToNetadr ((struct sockaddr*) &sadr, a); return qtrue; } @@ -241,16 +298,31 @@ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { int ret; - struct sockaddr from; + struct sockaddr_storage from; int fromlen; int net_socket; int protocol; + int protocol_max; int err; - for( protocol = 0 ; protocol < 2 ; protocol++ ) { + memset(&from, 0, sizeof(from)); + +#ifdef ENABLE_IPV6 + protocol_max = 3; +#else + protocol_max = 2; +#endif + + for( protocol = 0 ; protocol < protocol_max ; protocol++ ) { if( protocol == 0 ) { net_socket = ip_socket; } + +#ifdef ENABLE_IPV6 + else if(protocol == 1) { + net_socket = ip6_socket; + } +#endif else { net_socket = ipx_socket; } @@ -273,10 +345,6 @@ continue; } - if ( net_socket == ip_socket ) { - memset( ((struct sockaddr_in *)&from)->sin_zero, 0, 8 ); - } - if ( usingSocks && net_socket == ip_socket && memcmp( &from, &socksRelayAddr, fromlen ) == 0 ) { if ( ret < 10 || net_message->data[0] != 0 || net_message->data[1] != 0 || net_message->data[2] != 0 || net_message->data[3] != 1 ) { continue; @@ -290,7 +358,7 @@ net_message->readcount = 10; } else { - SockadrToNetadr( &from, net_from ); + SockadrToNetadr((struct sockaddr*) &from, net_from ); net_message->readcount = 0; } @@ -317,7 +385,7 @@ */ void Sys_SendPacket( int length, const void *data, netadr_t to ) { int ret; - struct sockaddr addr; + struct sockaddr_storage addr; SOCKET net_socket; if( to.type == NA_BROADCAST ) { @@ -332,6 +400,22 @@ else if( to.type == NA_BROADCAST_IPX ) { net_socket = ipx_socket; } +#ifdef ENABLE_IPV6 + else if (to.type == NA_IP6) + { + net_socket = ip6_socket; + } + else if(to.type == NA_MULTICAST_IP6) + { + StringToSockaddr(multicast_address, (struct sockaddr*) &addr, AF_INET6, AI_NUMERICHOST); + // Must specify interface when sending multicast + ((struct sockaddr_in6*) &addr)->sin6_scope_id = scope_id; + net_socket = ip6_socket; + + // Debugging - JFT + Com_Printf ("Sending IPv6 Multicast packet to : %s\n", SockaddrToStr((struct sockaddr *)&addr)); + } +#endif else { Com_Error( ERR_FATAL, "Sys_SendPacket: bad address type" ); return; @@ -341,7 +425,9 @@ return; } - NetadrToSockadr( &to, &addr ); + if(to.type != NA_MULTICAST_IP6) { + NetadrToSockadr( &to, (struct sockaddr*) &addr ); + } if( usingSocks && to.type == NA_IP ) { socksBuf[0] = 0; // reserved @@ -354,7 +440,7 @@ ret = sendto( net_socket, socksBuf, length+10, 0, &socksRelayAddr, sizeof(socksRelayAddr) ); } else { - ret = sendto( net_socket, data, length, 0, &addr, sizeof(addr) ); + ret = sendto( net_socket, data, length, 0, (struct sockaddr*) &addr, sizeof(addr) ); } if( ret == SOCKET_ERROR ) { int err = WSAGetLastError(); @@ -369,7 +455,7 @@ return; } - Com_Printf( "NET_SendPacket: %s\n", NET_ErrorString() ); + Com_Printf( "NET_SendPacket: Error %s sending packet type %i\n", NET_ErrorString(), to.type ); } } @@ -394,6 +480,15 @@ return qtrue; } +#ifdef ENABLE_IPV6 + if(adr.type == NA_IP6) + { + if(adr.ip6[0] == 0xfe && adr.ip6[1] == 0x80) // Link-local addresses start with fe80 + return qtrue; + return qfalse; + } +#endif + if( adr.type != NA_IP ) { return qfalse; } @@ -445,6 +540,31 @@ /* ================== +Addrv4ToStr +================== +*/ +const char * Addrv4ToStr(u_char * bytes) { + static char address[INET6_ADDRSTRLEN+1]; + Com_sprintf( address, sizeof(address), "%i.%i.%i.%i", bytes[0], bytes[1], bytes[2], bytes[3]); + return address; // Return static buffer. Non-reentrant. +} + +/* +================== +Addrv6ToStr +================== +*/ +const char * Addrv6ToStr(u_char * bytes) { + static char address[INET6_ADDRSTRLEN+1]; + Com_sprintf( address, sizeof(address), + "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); + return address; // Return static buffer. Non-reentrant. +} + +/* +================== Sys_ShowIP ================== */ @@ -452,10 +572,27 @@ int i; for (i = 0; i < numIP; i++) { - Com_Printf( "IP: %i.%i.%i.%i\n", localIP[i][0], localIP[i][1], localIP[i][2], localIP[i][3] ); + Com_Printf( "IP: %s\n", Addrv4ToStr(localIP[i])); } + + for (i = 0; i < numIPv6; i++) { + Com_Printf( "IPv6: %s\n", Addrv6ToStr(localIPv6[i])); + } } +/* +================== +SockaddrToStr +================== +*/ +const char * SockaddrToStr(struct sockaddr * s) { + if(s->sa_family == AF_INET6) { + return Addrv6ToStr(((struct sockaddr_in6 *)s)->sin6_addr.u.Byte); + } + else { + return Addrv4ToStr((u_char *)&((struct sockaddr_in *)s)->sin_addr); + } +} //============================================================================= @@ -472,6 +609,8 @@ int i = 1; int err; + memset(&address, 0, sizeof(address)); + if( net_interface ) { Com_Printf( "Opening IP socket: %s:%i\n", net_interface, port ); } @@ -479,7 +618,7 @@ Com_Printf( "Opening IP socket: localhost:%i\n", port ); } - if( ( newsocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == INVALID_SOCKET ) { + if( ( newsocket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == INVALID_SOCKET ) { err = WSAGetLastError(); if( err != WSAEAFNOSUPPORT ) { Com_Printf( "WARNING: UDP_OpenSocket: socket: %s\n", NET_ErrorString() ); @@ -488,19 +627,19 @@ } // make it non-blocking - if( ioctlsocket( newsocket, FIONBIO, &_true ) == SOCKET_ERROR ) { + if( ioctlsocket( newsocket, FIONBIO, (u_long *)&_true ) == SOCKET_ERROR ) { Com_Printf( "WARNING: UDP_OpenSocket: ioctl FIONBIO: %s\n", NET_ErrorString() ); return 0; } - // make it broadcast capable + // make it broadcast capable if( setsockopt( newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i) ) == SOCKET_ERROR ) { Com_Printf( "WARNING: UDP_OpenSocket: setsockopt SO_BROADCAST: %s\n", NET_ErrorString() ); return 0; } - if( !net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost") ) { - address.sin_addr.s_addr = INADDR_ANY; + if( !net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost" ) ) { + ((struct sockaddr_in *)&address)->sin_addr.s_addr = INADDR_ANY; } else { Sys_StringToSockaddr( net_interface, (struct sockaddr *)&address ); @@ -513,9 +652,9 @@ address.sin_port = htons( (short)port ); } - address.sin_family = AF_INET; + address.sin_family = AF_INET; - if( bind( newsocket, (void *)&address, sizeof(address) ) == SOCKET_ERROR ) { + if( bind( newsocket, (struct sockaddr *)&address, sizeof(address) ) == SOCKET_ERROR ) { Com_Printf( "WARNING: UDP_OpenSocket: bind: %s\n", NET_ErrorString() ); closesocket( newsocket ); return 0; @@ -524,8 +663,98 @@ return newsocket; } +/* +==================== +NET_IPv6Socket +==================== +*/ +#ifdef ENABLE_IPV6 +int NET_IPv6Socket( char *net_interface, int port ) { + SOCKET newsocket; + struct sockaddr_in6 address; + qboolean _true = qtrue; + int err, one = 1, ttl=32; + struct ipv6_mreq mreq; + memset(&address, 0, sizeof(address)); + + if( net_interface ) { + Com_Printf( "Opening IPv6 socket: %s:%i\n", net_interface, port ); + } + else { + Com_Printf( "Opening IPv6 socket: Unspecified IPv6 address [::]:%i\n", port ); + } + + if( ( newsocket = socket( PF_INET6, SOCK_DGRAM, IPPROTO_UDP ) ) == INVALID_SOCKET ) { + err = WSAGetLastError(); + if( err != WSAEAFNOSUPPORT ) { + Com_Printf( "WARNING: UDP_OpenSocket: socket: %s\n", NET_ErrorString() ); + } + return 0; + } + + // make it non-blocking + if( ioctlsocket( newsocket, FIONBIO, (u_long *)&_true ) == SOCKET_ERROR ) { + Com_Printf( "WARNING: UDP_OpenSocket: ioctl FIONBIO: %s\n", NET_ErrorString() ); + return 0; + } /* + if (setsockopt( newsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))) { + Com_Printf("NET_IPv6Socket: setsockopt(SO_REUSEADDR) failed: %s\n", NET_ErrorString()); + return 0; + } +*/ + if( !net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost" ) ) { + StringToSockaddr(IPV6_UNSPECIFIED_ADDR, (struct sockaddr *) &address, AF_INET6, AI_PASSIVE|AI_NUMERICHOST); + } + else { + StringToSockaddr(net_interface, (struct sockaddr *) &address, AF_INET6, AI_PASSIVE); + } + + if( port == PORT_ANY ) { + address.sin6_port = 0; + } + else { + address.sin6_port = htons( (short)port ); + } + address.sin6_family = AF_INET6; + address.sin6_scope_id = scope_id; + + if( bind( newsocket, (struct sockaddr *)&address, sizeof(address) ) == SOCKET_ERROR ) { + Com_Printf( "WARNING: NET_IPv6Socket: bind: %s\n", NET_ErrorString() ); + closesocket( newsocket ); + return 0; + } + + if(IN6_IS_ADDR_MULTICAST(&address.sin6_addr)) { + // Fill in the ipv6_mreq + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, &address.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); + mreq.ipv6mr_interface = scope_id; + + Com_Printf( "NET_IPv6Socket: Opening multicast socket on interface is index %i.\n", + mreq.ipv6mr_interface); // Debug - JFT + + // Set source interface + if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char*)&mreq.ipv6mr_interface, + sizeof(mreq.ipv6mr_interface)) < 0) { + Com_Printf("NET_IPv6Socket: IPV6_MULTICAST_IF: %s\n", NET_ErrorString()); + } + + if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq, sizeof(mreq))) { + Com_Printf("NET_IPv6Socket: IPV6_JOIN_GROUP: %s\n", NET_ErrorString()); + } + + if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl))) { + Com_Printf("NET_IPv6Socket: IPV6_MULTICAST_HOPS: %s\n", NET_ErrorString()); + } + } + + return newsocket; +} +#endif /* ENABLE_IPV6 */ + +/* ==================== NET_OpenSocks ==================== @@ -709,43 +938,41 @@ */ void NET_GetLocalAddress( void ) { char hostname[256]; - struct hostent *hostInfo; - int error; - char *p; - int ip; - int n; + struct addrinfo hint; + struct addrinfo * res = NULL; + struct addrinfo * current_ai = NULL; + struct sockaddr_in * sa; + struct sockaddr_in6 * sa6; + int error; if( gethostname( hostname, 256 ) == SOCKET_ERROR ) { - error = WSAGetLastError(); + Com_Printf( "NET_GetLocalAddress: %s\n", NET_ErrorString()); return; } + Com_Printf( "Hostname: %s\n", hostname ); - hostInfo = gethostbyname( hostname ); - if( !hostInfo ) { - error = WSAGetLastError(); + memset(&hint, 0, sizeof(hint)); + error = getaddrinfo(hostname, NULL, &hint, &res); + if(error) { + Com_Printf( "NET_GetLocalAddress: %s\n", NET_ErrorString()); return; } - - Com_Printf( "Hostname: %s\n", hostInfo->h_name ); - n = 0; - while( ( p = hostInfo->h_aliases[n++] ) != NULL ) { - Com_Printf( "Alias: %s\n", p ); + + current_ai = res; + numIP = numIPv6 = 0; + while(current_ai != NULL && numIP < MAX_IPS && numIPv6 < MAX_IPS) { + if(current_ai->ai_family == AF_INET) { + sa = (struct sockaddr_in *) current_ai->ai_addr; + memcpy(localIP[numIP++], &sa->sin_addr, 4); + } + else if (current_ai->ai_family == AF_INET6) { + sa6 = (struct sockaddr_in6 *) current_ai->ai_addr; + memcpy(localIP[numIPv6++], &sa6->sin6_addr, 16); + } + current_ai = current_ai->ai_next; } - if ( hostInfo->h_addrtype != AF_INET ) { - return; - } - - numIP = 0; - while( ( p = hostInfo->h_addr_list[numIP] ) != NULL && numIP < MAX_IPS ) { - ip = ntohl( *(int *)p ); - localIP[ numIP ][0] = p[0]; - localIP[ numIP ][1] = p[1]; - localIP[ numIP ][2] = p[2]; - localIP[ numIP ][3] = p[3]; - Com_Printf( "IP: %i.%i.%i.%i\n", ( ip >> 24 ) & 0xff, ( ip >> 16 ) & 0xff, ( ip >> 8 ) & 0xff, ip & 0xff ); - numIP++; - } + Sys_ShowIP(); } /* @@ -755,26 +982,84 @@ */ void NET_OpenIP( void ) { cvar_t *ip; + cvar_t *ipv6; + cvar_t *multicast; + cvar_t *interface_id; + char *interface_name; int port; int i; + struct sockaddr_in6 address; + qboolean use_default_multicast = qfalse; ip = Cvar_Get( "net_ip", "localhost", CVAR_LATCH ); port = Cvar_Get( "net_port", va( "%i", PORT_SERVER ), CVAR_LATCH )->integer; + ipv6 = Cvar_Get( "net_ipv6", "localhost", CVAR_LATCH ); + multicast = Cvar_Get( "net_multicast", va( "%i", 0 ), CVAR_LATCH ); + interface_id = Cvar_Get ("net_interface", "NULL", CVAR_LATCH ); + // Process interface id + interface_name = (strcmp(interface_id->string, "NULL") ? interface_id->string : NULL); + if(interface_name != NULL) { + scope_id = atoi(interface_name); + if(!scope_id) { + Com_Printf( "WARNING: NET_OpenIP: Invalid interface index.\n"); + } + } + + // Multicast parameters processing + strncpy(multicast_address, IPV6_MCAST_GROUP, sizeof(multicast_address)); + if(multicast->integer) { + // Verify we have an interface id + if(!scope_id) { + Com_Printf( "WARNING: NET_OpenIP: Multicast enabled without an interface index.\n"); + } + + // Check if we have an address + if(strcmp(ipv6->string, "localhost")) { + // Verify the address is multicast + memset(&address, 0, sizeof(address)); + Sys_StringToSockaddr( ipv6->string, (struct sockaddr *)&address ); + if(IN6_IS_ADDR_MULTICAST(&address.sin6_addr)) { + strncpy(multicast_address, ipv6->string, sizeof(multicast_address)); + } + else { + Com_Printf( "WARNING: NET_OpenIP: Multicast enabled but specified address is unicast.\n"); + } + } + else { + // Use default group + use_default_multicast = qtrue; + } + } + // automatically scan for a valid port, so multiple // dedicated servers can be started without requiring // a different net_port for each one for( i = 0 ; i < 10 ; i++ ) { - ip_socket = NET_IPSocket( ip->string, port + i ); - if ( ip_socket ) { - Cvar_SetValue( "net_port", port + i ); - if ( net_socksEnabled->integer ) { - NET_OpenSocks( port + i ); + if (!ip_socket && !net_nov4->integer) { + ip_socket = NET_IPSocket (ip->string, port + i); + continue; + } + +#ifdef ENABLE_IPV6 + if(!ip6_socket && !net_nov6->integer) { + if(use_default_multicast == qtrue) { + ip6_socket = NET_IPv6Socket (multicast_address, port + i); } - NET_GetLocalAddress(); - return; + else { + ip6_socket = NET_IPv6Socket (ipv6->string, port + i); + } + continue; } +#endif + if ( net_socksEnabled->integer ) { + NET_OpenSocks( port + i ); + } + + NET_GetLocalAddress(); + return; } + Com_Printf( "WARNING: Couldn't allocate IP port\n"); } @@ -857,17 +1142,21 @@ modified = qfalse; - if( net_noudp && net_noudp->modified ) { + if( net_nov4 && net_nov4->modified ) { modified = qtrue; } - net_noudp = Cvar_Get( "net_noudp", "0", CVAR_LATCH | CVAR_ARCHIVE ); + net_nov4 = Cvar_Get( "net_nov4", va( "%i", 0 ), CVAR_LATCH | CVAR_ARCHIVE ); + if( net_nov6 && net_nov6->modified ) { + modified = qtrue; + } + net_nov6 = Cvar_Get( "net_nov6", va( "%i", 0 ), CVAR_LATCH | CVAR_ARCHIVE ); + if( net_noipx && net_noipx->modified ) { modified = qtrue; } net_noipx = Cvar_Get( "net_noipx", "0", CVAR_LATCH | CVAR_ARCHIVE ); - if( net_socksEnabled && net_socksEnabled->modified ) { modified = qtrue; } @@ -904,14 +1193,14 @@ ==================== */ void NET_Config( qboolean enableNetworking ) { - qboolean modified; + qboolean modified; qboolean stop; qboolean start; // get any latched changes to cvars modified = NET_GetCvars(); - if( net_noudp->integer && net_noipx->integer ) { + if( net_nov4->integer && net_nov6->integer && net_noipx->integer ) { enableNetworking = qfalse; } @@ -948,6 +1237,13 @@ ip_socket = 0; } +#ifdef ENABLE_IPV6 + if ( ip6_socket && ip6_socket != INVALID_SOCKET ) { + closesocket( ip6_socket ); + ip6_socket = 0; + } +#endif + if ( socks_socket && socks_socket != INVALID_SOCKET ) { closesocket( socks_socket ); socks_socket = 0; @@ -960,9 +1256,10 @@ } if( start ) { - if (! net_noudp->integer ) { + if ( !net_nov4->integer || !net_nov6->integer ) { NET_OpenIP(); } + if (! net_noipx->integer ) { NET_OpenIPX(); } Index: code/win32/win_local.h =================================================================== --- code/win32/win_local.h (revision 90) +++ code/win32/win_local.h (working copy) @@ -25,6 +25,7 @@ #pragma warning(disable : 4201) #pragma warning( push ) #endif +#include // Winsock2 required for IPv6. Must be before windows.h. #include #if defined (_MSC_VER) && (_MSC_VER >= 1200) #pragma warning( pop ) @@ -35,7 +36,7 @@ #include #include -#include + #include void IN_MouseEvent (int mstate); Index: code/server/sv_ccmds.c =================================================================== --- code/server/sv_ccmds.c (revision 90) +++ code/server/sv_ccmds.c (working copy) @@ -420,17 +420,13 @@ return; } svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, - svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], - svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], - BigShort( svs.authorizeAddress.port ) ); + Com_Printf( "%s resolved to %s\n", AUTHORIZE_SERVER_NAME, NET_AdrToString(svs.authorizeAddress) ); } // otherwise send their ip to the authorize server if ( svs.authorizeAddress.type != NA_BAD ) { NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, - "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], - cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); + "banUser %s", NET_AdrToString(cl->netchan.remoteAddress) ); Com_Printf("%s was banned from coming back\n", cl->name); } } @@ -474,10 +470,7 @@ return; } svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, - svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], - svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], - BigShort( svs.authorizeAddress.port ) ); + Com_Printf( "%s resolved to %s\n", AUTHORIZE_SERVER_NAME, NET_AdrToString(svs.authorizeAddress) ); } // otherwise send their ip to the authorize server Index: code/server/sv_client.c =================================================================== --- code/server/sv_client.c (revision 90) +++ code/server/sv_client.c (working copy) @@ -96,10 +96,7 @@ return; } svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, - svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], - svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], - BigShort( svs.authorizeAddress.port ) ); + Com_Printf( "%s resolved to %s\n", AUTHORIZE_SERVER_NAME, NET_AdrToString(svs.authorizeAddress) ); } // if they have been challenging for a long time and we Index: code/server/sv_main.c =================================================================== --- code/server/sv_main.c (revision 90) +++ code/server/sv_main.c (working copy) @@ -253,9 +253,7 @@ if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = BigShort( PORT_MASTER ); } - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, - adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], - BigShort( adr[i].port ) ); + Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToString(adr[i]) ); } Index: code/renderer/qgl.h =================================================================== --- code/renderer/qgl.h (revision 90) +++ code/renderer/qgl.h (working copy) @@ -38,6 +38,7 @@ #pragma warning (disable: 4032) #pragma warning (disable: 4201) #pragma warning (disable: 4214) +#include // Winsock2 required for IPv6. Must be before windows.h #include #include Index: code/qcommon/qcommon.h =================================================================== --- code/qcommon/qcommon.h (revision 90) +++ code/qcommon/qcommon.h (working copy) @@ -131,7 +131,9 @@ NA_BROADCAST, NA_IP, NA_IPX, - NA_BROADCAST_IPX + NA_BROADCAST_IPX, + NA_IP6, + NA_MULTICAST_IP6 } netadrtype_t; typedef enum { @@ -144,7 +146,9 @@ byte ip[4]; byte ipx[10]; + byte ip6[16]; + unsigned long scope_id; // Mirrors sin6_scope_id. Required for IPv6 link-local and multicast. unsigned short port; } netadr_t; @@ -165,13 +169,13 @@ qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message); void NET_Sleep(int msec); - #define MAX_MSGLEN 16384 // max length of a message, which may // be fragmented into multiple packets #define MAX_DOWNLOAD_WINDOW 8 // max of eight download frames #define MAX_DOWNLOAD_BLKSIZE 2048 // 2048 byte block chunks - +#define IPV6_MCAST_GROUP "FF12::666" // IPv6 link-local multicast group address +#define IPV6_UNSPECIFIED_ADDR "::" // IPv6 unspecified address, used to bind to all addresses /* Netchan handles packet fragmentation and out of order / duplicate suppression Index: code/qcommon/net_chan.c =================================================================== --- code/qcommon/net_chan.c (revision 90) +++ code/qcommon/net_chan.c (working copy) @@ -23,6 +23,12 @@ #include "../game/q_shared.h" #include "qcommon.h" +#ifdef ENABLE_IPV6 +#ifndef _WINDOWS +#include // for if_nametoindex +#endif +#endif + /* packet header @@ -476,6 +482,9 @@ if (a.type == NA_LOOPBACK) return qtrue; + if (a.type == NA_BOT && b.type == NA_BOT) + return qtrue; + if (a.type == NA_IP) { 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]) @@ -489,8 +498,15 @@ return qtrue; return qfalse; } +#ifdef ENABLE_IPV6 + if(a.type == NA_IP6) + { + if(memcmp(a.ip6, b.ip6, 16) == 0) + return qtrue; + return qfalse; + } +#endif - Com_Printf ("NET_CompareBaseAdr: bad address type\n"); return qfalse; } @@ -506,6 +522,13 @@ } else if (a.type == NA_IP) { Com_sprintf (s, sizeof(s), "%i.%i.%i.%i:%hu", a.ip[0], a.ip[1], a.ip[2], a.ip[3], BigShort(a.port)); +#ifdef ENABLE_IPV6 + } else if (a.type == NA_IP6 || a.type == NA_MULTICAST_IP6) { + Com_sprintf(s, sizeof(s), "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%hu", + a.ip6[0], a.ip6[1], a.ip6[2], a.ip6[3], a.ip6[4], a.ip6[5], a.ip6[6], a.ip6[7], a.ip6[8], + a.ip6[9], a.ip6[10], a.ip6[11], a.ip6[12], a.ip6[13], a.ip6[14], a.ip6[15], + BigShort(a.port)); +#endif } else { Com_sprintf (s, sizeof(s), "%02x%02x%02x%02x.%02x%02x%02x%02x%02x%02x:%hu", a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9], @@ -524,6 +547,9 @@ if (a.type == NA_LOOPBACK) return qtrue; + if (a.type == NA_BOT && b.type == NA_BOT) + return qtrue; + if (a.type == NA_IP) { 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) @@ -537,8 +563,16 @@ return qtrue; return qfalse; } +#ifdef ENABLE_IPV6 + if(a.type == NA_IP6 || a.type == NA_MULTICAST_IP6) + { + if(memcmp(a.ip6, b.ip6, 16) == 0 && a.port == b.port) + return qtrue; + return qfalse; + } +#endif - Com_Printf ("NET_CompareAdr: bad address type\n"); + Com_Printf ("NET_CompareAdr: bad address type %i A:%s B:%s\n", a.type, NET_AdrToString(a), NET_AdrToString(b)); return qfalse; } @@ -699,10 +733,19 @@ Traps "localhost" for loopback, passes everything else to system ============= */ +/*strnbchr: helper to count the number of occurences of a character in a string */ +int strnbchr(char * p, char c) { + int i = 0; + + while(*p) + if(*p++==c) ++i; + return i; +} + qboolean NET_StringToAdr( const char *s, netadr_t *a ) { qboolean r; char base[MAX_STRING_CHARS]; - char *port; + char *port, *interface_id; if (!strcmp (s, "localhost")) { Com_Memset (a, 0, sizeof(*a)); @@ -710,13 +753,52 @@ return qtrue; } - // look for a port number Q_strncpyz( base, s, sizeof( base ) ); - port = strstr( base, ":" ); + +#ifdef ENABLE_IPV6 + // Look for an interface identifier - useful for multicast and IPv6 link-local + interface_id = strrchr(base, '%'); + if(interface_id) { + *interface_id = 0; + interface_id++; + } +#endif + + // Look for a port number - Q3 just adds :port to address + port = strrchr( base, ':' ); // find last ':' + + Com_Printf("NET_StringToAdr: address is %s\n", base); + if ( port ) { - *port = 0; - port++; +#ifdef ENABLE_IPV6 + if(port > base && *port == ':') + { + if(*(port-1) == ':') // Last ':' is double. No port there. + { + port = NULL; + } + else if(*(port-1) == ']') // Check for literal with port + { + // getaddrinfo doesn't accept literal addresses with [square brackets] + memmove(base,base+1,sizeof(base)-1); // remove '[' + port--; + *(port-1) = 0; // remove ']' + } + else if(strchr(base, ':') != port) // More than one :, got IPv6 + { + // Accept the last segment as a port if more than 4 digits or if there are 8 ':' + if(strnbchr(base ,':') < 8 && strlen(port) <= 5) // Use 5 here because of the leading ':' + port = NULL; + } + } + +#endif + if(port) { + *port = 0; + port++; + } } + Com_Printf("NET_StringToAdr: address cleaned is %s\n", base); r = Sys_StringToAdr( base, a ); @@ -725,18 +807,24 @@ return qfalse; } - // inet_addr returns this if out of range - if ( a->ip[0] == 255 && a->ip[1] == 255 && a->ip[2] == 255 && a->ip[3] == 255 ) { - a->type = NA_BAD; - return qfalse; - } - if ( port ) { a->port = BigShort( (short)atoi( port ) ); } else { a->port = BigShort( PORT_SERVER ); } +#ifdef ENABLE_IPV6 + if ( interface_id ) { +#ifdef _WINDOWS + // Interface id is an index on Windows + a->scope_id = atoi(interface_id); +#else + // Other systems use the interface name + a->scope_id = if_nametoindex(interface_id); +#endif /* _WINDOWS */ + } +#endif + return qtrue; } Index: code/qcommon/common.c =================================================================== --- code/qcommon/common.c (revision 90) +++ code/qcommon/common.c (working copy) @@ -30,7 +30,7 @@ #if defined(MACOS_X) #include #else -#include +#include #endif #endif Index: code/run.bat =================================================================== --- code/run.bat (revision 90) +++ code/run.bat (working copy) @@ -1 +1 @@ -debug\quake3 +set fs_basepath \quake3 +set fs_cdpath g:\quake3 + %1 %2 %3 %4 %5 %6 %7 %8 %9 +debug\quake3 +set fs_basepath \quake3 +set logfile 1 +set fs_cdpath c:\quake3 + %1 %2 %3 %4 %5 %6 %7 %8 %9 Index: code/client/cl_main.c =================================================================== --- code/client/cl_main.c (revision 90) +++ code/client/cl_main.c (working copy) @@ -824,10 +824,7 @@ return; } cls.updateServer.port = BigShort( PORT_UPDATE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", UPDATE_SERVER_NAME, - cls.updateServer.ip[0], cls.updateServer.ip[1], - cls.updateServer.ip[2], cls.updateServer.ip[3], - BigShort( cls.updateServer.port ) ); + Com_Printf( "%s resolved to %s\n", UPDATE_SERVER_NAME, NET_AdrToString(cls.updateServer) ); info[0] = 0; // NOTE TTimo xoring against Com_Milliseconds, otherwise we may not have a true randomization @@ -895,10 +892,7 @@ } cls.authorizeServer.port = BigShort( PORT_AUTHORIZE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, - cls.authorizeServer.ip[0], cls.authorizeServer.ip[1], - cls.authorizeServer.ip[2], cls.authorizeServer.ip[3], - BigShort( cls.authorizeServer.port ) ); + Com_Printf( "%s resolved to %s\n", AUTHORIZE_SERVER_NAME, NET_AdrToString( cls.authorizeServer) ); } if ( cls.authorizeServer.type == NA_BAD ) { return; @@ -1069,10 +1063,7 @@ if (clc.serverAddress.port == 0) { clc.serverAddress.port = BigShort( PORT_SERVER ); } - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", cls.servername, - clc.serverAddress.ip[0], clc.serverAddress.ip[1], - clc.serverAddress.ip[2], clc.serverAddress.ip[3], - BigShort( clc.serverAddress.port ) ); + Com_Printf( "%s resolved to %s\n", cls.servername, NET_AdrToString(clc.serverAddress) ); // if we aren't playing on a lan, we need to authenticate // with the cd key @@ -2548,6 +2539,13 @@ type = 1; break; +#ifdef ENABLE_IPV6 + case NA_MULTICAST_IP6: + case NA_IP6: + str = "udp6"; + type = 3; + break; +#endif case NA_IPX: case NA_BROADCAST_IPX: str = "ipx"; @@ -2842,6 +2840,10 @@ to.type = NA_BROADCAST; NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); +#ifdef ENABLE_IPV6 + to.type = NA_MULTICAST_IP6; + NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); +#endif to.type = NA_BROADCAST_IPX; NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); }