diff options
-rw-r--r-- | src/game/g_admin.c | 260 | ||||
-rw-r--r-- | src/game/g_admin.h | 4 | ||||
-rw-r--r-- | src/game/g_client.c | 8 | ||||
-rw-r--r-- | src/game/g_local.h | 14 | ||||
-rw-r--r-- | src/game/g_utils.c | 189 |
5 files changed, 361 insertions, 114 deletions
diff --git a/src/game/g_admin.c b/src/game/g_admin.c index d2125dd7..bcd2b8e7 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -44,7 +44,7 @@ g_admin_cmd_t g_admin_cmds[ ] = "'m' (minutes), or seconds if no units are specified. if the duration is" " preceded by a + or -, the ban duration will be extended or shortened by" " the specified amount", - "[^3ban#^7] (^5duration^7) (^5reason^7)" + "[^3ban#^7] (^5/mask^7) (^5duration^7) (^5reason^7)" }, {"admintest", G_admin_admintest, "admintest", @@ -67,7 +67,7 @@ g_admin_cmd_t g_admin_cmds[ ] = " duration is specified as numbers followed by units 'w' (weeks), 'd' " "(days), 'h' (hours) or 'm' (minutes), or seconds if no units are " "specified", - "[^3name|slot#|IP^7] (^5duration^7) (^5reason^7)" + "[^3name|slot#|IP(/mask)^7] (^5duration^7) (^5reason^7)" }, {"cancelvote", G_admin_endvote, "cancelvote", @@ -122,7 +122,7 @@ g_admin_cmd_t g_admin_cmds[ ] = {"namelog", G_admin_namelog, "namelog", "display a list of names used by recently connected players", - "(^5name^7)" + "(^5name|IP(/mask)^7)" }, {"nextmap", G_admin_nextmap, "nextmap", @@ -162,7 +162,7 @@ g_admin_cmd_t g_admin_cmds[ ] = {"showbans", G_admin_showbans, "showbans", "display a (partial) list of active bans", - "(^5start at ban#^7) (^5name|IP^7)" + "(^5start at ban#^7) (^5name|IP(/mask)^7)" }, {"spec999", G_admin_spec999, "spec999", @@ -803,40 +803,42 @@ void G_admin_duration( int secs, char *duration, int dursize ) Com_sprintf( duration, dursize, "%i seconds", secs ); } -qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ) +qboolean G_admin_ban_check( gentity_t *ent, char *reason, int rlen ) { - char *guid, *ip; int i; int t; + addr_t ip, test; + int mask = -1; *reason = '\0'; t = trap_RealTime( NULL ); - if( !*userinfo ) + // this happens when ip = localhost + if( !G_AddressParse( ent->client->pers.ip, &ip, NULL ) ) return qfalse; - ip = Info_ValueForKey( userinfo, "ip" ); - if( !*ip ) - return qfalse; - guid = Info_ValueForKey( userinfo, "cl_guid" ); for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) { // 0 is for perm ban if( g_admin_bans[ i ]->expires != 0 && g_admin_bans[ i ]->expires <= t ) continue; - if( strstr( ip, g_admin_bans[ i ]->ip ) || - ( *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) ) ) + + if( !Q_stricmp( g_admin_bans[ i ]->guid, ent->client->pers.guid ) || + ( !G_admin_permission( ent, ADMF_IMMUNITY ) && + G_AddressParse( g_admin_bans[ i ]->ip, &test, &mask ) && + G_AddressCompare( &ip, &test, mask ) ) ) { char duration[ 13 ]; G_admin_duration( g_admin_bans[ i ]->expires - t, duration, sizeof( duration ) ); - Com_sprintf( - reason, - rlen, - "You have been banned by %s^7 reason: %s^7 expires: %s", - g_admin_bans[ i ]->banner, - g_admin_bans[ i ]->reason, - duration - ); - G_Printf( "Banned player (#%d) tried to connect from %s\n", i + 1, ip ); + if( reason ) + Com_sprintf( + reason, + rlen, + "You have been banned by %s^7 reason: %s^7 expires: %s", + g_admin_bans[ i ]->banner, + g_admin_bans[ i ]->reason, + duration + ); + G_Printf( "%s matches ban #%d\n", ip, i + 1 ); return qtrue; } } @@ -1433,6 +1435,21 @@ static qboolean admin_create_ban( gentity_t *ent, Q_strncpyz( b->reason, "banned by admin", sizeof( b->reason ) ); else Q_strncpyz( b->reason, reason, sizeof( b->reason ) ); + + for( i = 0; i < level.maxclients; i++ ) + { + if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) + continue; + if( G_admin_ban_check( &g_entities[ i ], NULL, 0 ) ) + { + trap_SendServerCommand( i, + va( "disconnect \"You have been kicked by %s\nreason:\n%s\"", + b->banner, b->reason ) ); + + trap_DropClient( i, va( "has been kicked by %s^7. reason: %s", + b->banner, b->reason ) ); + } + } return qtrue; } @@ -1509,15 +1526,6 @@ qboolean G_admin_kick( gentity_t *ent, int skiparg ) ( *reason ) ? reason : "kicked by admin" ); admin_writeconfig(); - trap_SendServerCommand( pids[ 0 ], - va( "disconnect \"You have been kicked.\n%s^7\nreason:\n%s\"", - ( ent ) ? va( "admin:\n%s", ent->client->pers.netname ) : "", - ( *reason ) ? reason : "kicked by admin" ) ); - - trap_DropClient( pids[ 0 ], va( "has been kicked%s^7. reason: %s", - ( ent ) ? va( " by %s", ent->client->pers.netname ) : "", - ( *reason ) ? reason : "kicked by admin" ) ); - return qtrue; } @@ -1533,10 +1541,13 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) qboolean exactmatch = qfalse; char n2[ MAX_NAME_LENGTH ]; char s2[ MAX_NAME_LENGTH ]; + int netmask = -1; + addr_t ip, cmp; + qboolean ipmatch = qfalse; if( G_SayArgc() < 2 + skiparg ) { - ADMP( "^3!ban: ^7usage: !ban [name|slot|ip] [duration] [reason]\n" ); + ADMP( "^3!ban: ^7usage: !ban [name|slot|IP(/mask)] [duration] [reason]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, search, sizeof( search ) ); @@ -1581,6 +1592,18 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) for( logmatch = 0; g_admin_namelog[ logmatch ]->slot != i; logmatch++ ); } } + else if( G_AddressParse( search, &ip, &netmask ) ) + { + int max = ip.type == IPv4 ? 32 : 128; + int min = ent ? max / 2 : 1; + if( netmask < min || netmask > max ) + { + ADMP( va( "^3!ban: ^7invalid netmask (%d is not one of %d-%d)\n", + netmask, min, max ) ); + return qfalse; + } + ipmatch = qtrue; + } for( i = 0; !exactmatch && i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; @@ -1590,12 +1613,21 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) if( g_admin_namelog[ i ]->banned ) continue; - if( !Q_stricmp( g_admin_namelog[ i ]->ip, search ) ) + if( ipmatch ) { - logmatches = 1; - logmatch = i; - exactmatch = qtrue; - break; + if( G_AddressParse( g_admin_namelog[ i ]->ip, &cmp, NULL ) && + G_AddressCompare( &ip, &cmp, netmask ) ) + { + logmatches++; + logmatch = i; + if( ( ip.type == IPv4 && netmask == 32 ) || + ( ip.type == IPv6 && netmask == 128 ) ) + { + exactmatch = qtrue; + break; + } + } + continue; } for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) @@ -1615,7 +1647,7 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) ADMP( "^3!ban: ^7no player found by that name, IP, or slot number\n" ); return qfalse; } - else if( logmatches > 1 ) + if( !ipmatch && logmatches > 1 ) { ADMBP_begin(); ADMBP( "^3!ban: ^7multiple recent clients match name, use IP or slot#:\n" ); @@ -1660,7 +1692,7 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) admin_create_ban( ent, g_admin_namelog[ logmatch ]->name[ 0 ], g_admin_namelog[ logmatch ]->guid, - g_admin_namelog[ logmatch ]->ip, + ipmatch ? search : g_admin_namelog[ logmatch ]->ip, seconds, reason ); g_admin_namelog[ logmatch ]->banned = qtrue; @@ -1670,30 +1702,6 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) else admin_writeconfig(); - if( g_admin_namelog[ logmatch ]->slot == -1 ) - { - // client is already disconnected so stop here - AP( va( "print \"^3!ban:^7 %s^7 has been banned by %s^7, " - "duration: %s, reason: %s\n\"", - g_admin_namelog[ logmatch ]->name[ 0 ], - ( ent ) ? ent->client->pers.netname : "console", - duration, - ( *reason ) ? reason : "banned by admin" ) ); - return qtrue; - } - - trap_SendServerCommand( g_admin_namelog[ logmatch ]->slot, - va( "disconnect \"You have been banned.\n" - "admin:\n%s^7\nduration:\n%s\nreason:\n%s\"", - ( ent ) ? ent->client->pers.netname : "console", - duration, - ( *reason ) ? reason : "banned by admin" ) ); - - trap_DropClient( g_admin_namelog[ logmatch ]->slot, - va( "has been banned by %s^7, duration: %s, reason: %s", - ( ent ) ? ent->client->pers.netname : "console", - duration, - ( *reason ) ? reason : "banned by admin" ) ); return qtrue; } @@ -1744,10 +1752,12 @@ qboolean G_admin_adjustban( gentity_t *ent, int skiparg ) char secs[ MAX_TOKEN_CHARS ]; char mode = '\0'; g_admin_ban_t *ban; + int mask = 0; if( G_SayArgc() < 3 + skiparg ) { - ADMP( "^3!adjustban: ^7usage: !adjustban [ban#] [duration] [reason]\n" ); + ADMP( "^3!adjustban: ^7usage: !adjustban [ban#] [/mask] [duration] [reason]" + "\n" ); return qfalse; } G_SayArgv( 1 + skiparg, bs, sizeof( bs ) ); @@ -1766,6 +1776,19 @@ qboolean G_admin_adjustban( gentity_t *ent, int skiparg ) return qfalse; } G_SayArgv( 2 + skiparg, secs, sizeof( secs ) ); + if( secs[ 0 ] == '/' ) + { + int max = strchr( ban->ip, ':' ) ? 128 : 32; + int min = ent ? max / 2 : 1; + mask = atoi( secs + 1 ); + if( mask < min || mask > max ) + { + ADMP( va( "^3!adjustban: ^7invalid netmask (%d is not one of %d-%d)\n", + mask, min, max ) ); + return qfalse; + } + G_SayArgv( 3 + skiparg++, secs, sizeof( secs ) ); + } if( secs[ 0 ] == '+' || secs[ 0 ] == '-' ) mode = secs[ 0 ]; length = G_admin_parse_time( &secs[ mode ? 1 : 0 ] ); @@ -1805,14 +1828,24 @@ qboolean G_admin_adjustban( gentity_t *ent, int skiparg ) G_admin_duration( ( expires ) ? expires - time : -1, duration, sizeof( duration ) ); } + if( mask ) + { + char *p = strchr( ban->ip, '/' ); + if( !p ) + p = ban->ip + strlen( ban->ip ); + Com_sprintf( p, sizeof( ban->ip ) - ( p - ban->ip ), "/%d", mask ); + } reason = G_SayConcatArgs( 3 + skiparg ); if( *reason ) Q_strncpyz( ban->reason, reason, sizeof( ban->reason ) ); AP( va( "print \"^3!adjustban: ^7ban #%d for %s^7 has been updated by %s^7 " - "%s%s%s%s%s\n\"", + "%s%s%s%s%s%s\n\"", bnum, ban->name, ( ent ) ? ent->client->pers.netname : "console", + ( mask ) ? + va( "netmask: /%d%s", mask, + ( length >= 0 || *reason ) ? ", " : "" ) : "", ( length >= 0 ) ? "duration: " : "", duration, ( length >= 0 && *reason ) ? ", " : "", @@ -2249,9 +2282,9 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) char date[ 11 ]; char *made; char n1[ MAX_NAME_LENGTH * 2 ] = {""}; - qboolean numeric = qtrue; - char *ip_match = NULL; - int ip_match_len = 0; + qboolean ipmatch = qfalse; + addr_t ipa, ipb; + int neta, netb; char name_match[ MAX_NAME_LENGTH ] = {""}; t = trap_RealTime( NULL ); @@ -2278,28 +2311,13 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) start = atoi( filter ); G_SayArgv( 2 + skiparg, filter, sizeof( filter ) ); } - for( i = 0; i < sizeof( filter ) && filter[ i ] ; i++ ) - { - if( !isdigit( filter[ i ] ) && - filter[ i ] != '.' && filter[ i ] != '-' ) - { - numeric = qfalse; - break; - } - } - if( !numeric ) - { - G_SanitiseString( filter, name_match, sizeof( name_match ) ); - } - else if( strchr( filter, '.' ) ) - { - ip_match = filter; - ip_match_len = strlen(ip_match); - } else { - start = atoi( filter ); - filter[ 0 ] = '\0'; + for( i = 0; filter[ i ] && isdigit( filter[ i ] ); i++ ); + if( filter[ i ] ) + G_SanitiseString( filter, name_match, sizeof( name_match ) ); + else + start = atoi( filter ); } // showbans 1 means start with ban 0 if( start > 0 ) @@ -2314,6 +2332,8 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) start = i + 1; } + else + ipmatch = G_AddressParse( filter, &ipa, &neta ); } if( start > max ) @@ -2327,15 +2347,20 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) if( g_admin_bans[ i ]->expires != 0 && g_admin_bans[ i ]->expires <= t ) continue; - if( name_match[ 0 ] ) + if( ipmatch ) + { + if( !G_AddressParse( g_admin_bans[ i ]->ip, &ipb, &netb ) ) + continue; + if( !G_AddressCompare( &ipa, &ipb, neta ) && + !G_AddressCompare( &ipa, &ipb, netb ) ) + continue; + } + else if( name_match[ 0 ] ) { G_SanitiseString( g_admin_bans[ i ]->name, n1, sizeof( n1 ) ); if( !strstr( n1, name_match) ) continue; } - if( ip_match && - Q_strncmp( ip_match, g_admin_bans[ i ]->ip, ip_match_len ) ) - continue; count++; @@ -2354,15 +2379,20 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) if( g_admin_bans[ i ]->expires != 0 && g_admin_bans[ i ]->expires <= t ) continue; - if( name_match[ 0 ] ) + if( ipmatch ) + { + if( !G_AddressParse( g_admin_bans[ i ]->ip, &ipb, &netb ) ) + continue; + if( !G_AddressCompare( &ipa, &ipb, neta ) && + !G_AddressCompare( &ipa, &ipb, netb ) ) + continue; + } + else if( name_match[ 0 ] ) { G_SanitiseString( g_admin_bans[ i ]->name, n1, sizeof( n1 ) ); if( !strstr( n1, name_match) ) continue; } - if( ip_match && - Q_strncmp( ip_match, g_admin_bans[ i ]->ip, ip_match_len ) ) - continue; count++; @@ -2401,11 +2431,11 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) g_admin_bans[ i ]->reason ) ); } - if( name_match[ 0 ] || ip_match ) + if( name_match[ 0 ] || ipmatch ) { ADMBP( va( "^3!showbans:^7 found %d matching bans by %s. ", count, - ( ip_match ) ? "IP" : "name" ) ); + ( ipmatch ) ? "IP" : "name" ) ); } else { @@ -2419,8 +2449,8 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) if( i <= max ) ADMBP( va( " run !showbans %d%s%s to see more", i + 1, - ( filter[ 0 ] ) ? " " : "", - ( filter[ 0 ] ) ? filter : "" ) ); + ( name_match[ 0 ] ) ? " " : "", + ( name_match[ 0 ] ) ? filter : "" ) ); ADMBP( "\n" ); ADMBP_end(); return qtrue; @@ -2764,28 +2794,42 @@ qboolean G_admin_namelog( gentity_t *ent, int skiparg ) char s2[ MAX_NAME_LENGTH ] = {""}; char n2[ MAX_NAME_LENGTH ] = {""}; int printed = 0; + addr_t a, b; + int mask = -1; + qboolean ipmatch = qfalse; if( G_SayArgc() > 1 + skiparg ) { G_SayArgv( 1 + skiparg, search, sizeof( search ) ); - G_SanitiseString( search, s2, sizeof( s2 ) ); + ipmatch = G_AddressParse( search, &a, &mask ); + if( !ipmatch ) + G_SanitiseString( search, s2, sizeof( s2 ) ); } ADMBP_begin(); for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) { if( search[ 0 ] ) { - for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && - g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) + if( ipmatch ) { - G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); - if( strstr( n2, s2 ) ) + if( !G_AddressParse( g_admin_namelog[ i ]->ip, &b, NULL ) || + !G_AddressCompare( &a, &b, mask ) ) + continue; + } + else + { + for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && + g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) { - break; + G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); + if( strstr( n2, s2 ) ) + { + break; + } } + if( j == MAX_ADMIN_NAMELOG_NAMES || !g_admin_namelog[ i ]->name[ j ][ 0 ] ) + continue; } - if( j == MAX_ADMIN_NAMELOG_NAMES || !g_admin_namelog[ i ]->name[ j ][ 0 ] ) - continue; } printed++; ADMBP( va( "%s (*%s) %15s^7", diff --git a/src/game/g_admin.h b/src/game/g_admin.h index 5ac3c668..7917b2b3 100644 --- a/src/game/g_admin.h +++ b/src/game/g_admin.h @@ -109,7 +109,7 @@ typedef struct g_admin_ban { char name[ MAX_NAME_LENGTH ]; char guid[ 33 ]; - char ip[ 40 ]; + char ip[ 44 ]; // big enough for IPv6 CIDR notation (without brackets) char reason[ MAX_ADMIN_BAN_REASON ]; char made[ 18 ]; // big enough for strftime() %c int expires; @@ -136,7 +136,7 @@ typedef struct g_admin_namelog } g_admin_namelog_t; -qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ); +qboolean G_admin_ban_check( gentity_t *ent, char *reason, int rlen ); qboolean G_admin_cmd_check( gentity_t *ent, qboolean say ); qboolean G_admin_readconfig( gentity_t *ent, int skiparg ); qboolean G_admin_permission( gentity_t *ent, const char *flag ); diff --git a/src/game/g_client.c b/src/game/g_client.c index f0eaeeb0..78440563 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -1221,15 +1221,15 @@ char *ClientConnect( int clientNum, qboolean firstTime ) value = Info_ValueForKey( userinfo, "cl_guid" ); Q_strncpyz( client->pers.guid, value, sizeof( client->pers.guid ) ); + value = Info_ValueForKey( userinfo, "ip" ); + Q_strncpyz( client->pers.ip, value, sizeof( client->pers.ip ) ); + // check for admin ban - if( G_admin_ban_check( userinfo, reason, sizeof( reason ) ) ) + if( G_admin_ban_check( ent, reason, sizeof( reason ) ) ) { return va( "%s", reason ); } - value = Info_ValueForKey( userinfo, "ip" ); - Q_strncpyz( client->pers.ip, value, sizeof( client->pers.ip ) ); - // check for a password value = Info_ValueForKey( userinfo, "password" ); diff --git a/src/game/g_local.h b/src/game/g_local.h index 46a75c62..59d76f12 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -773,6 +773,20 @@ qboolean G_InPowerZone( gentity_t *self ); // // g_utils.c // +#define ADDRLEN 16 +typedef struct +{ + enum + { + IPv4, + IPv6 + } type; + byte addr[ ADDRLEN ]; +} addr_t; +qboolean G_AddressParse( const char *str, addr_t *addr, int *netmask ); +qboolean G_AddressCompare( const addr_t *a, const addr_t *b, int netmask ); +qboolean G_AdrCmpStr( const char *a, const char *b ); + int G_ParticleSystemIndex( char *name ); int G_ShaderIndex( char *name ); int G_ModelIndex( char *name ); diff --git a/src/game/g_utils.c b/src/game/g_utils.c index 62fcc2a4..8b76704b 100644 --- a/src/game/g_utils.c +++ b/src/game/g_utils.c @@ -857,3 +857,192 @@ void G_CloseMenus( int clientNum ) Com_sprintf( buffer, 32, "serverclosemenus" ); trap_SendServerCommand( clientNum, buffer ); } + + +/* +=============== +G_AddressParse + +Make an IP address more usable +=============== +*/ +static const char *addr4parse( const char *str, addr_t *addr ) +{ + int i; + int octet = 0; + int num = 0; + memset( addr, 0, sizeof( addr_t ) ); + addr->type = IPv4; + for( i = 0; octet < 4; i++ ) + { + if( isdigit( str[ i ] ) ) + num = num * 10 + str[ i ] - '0'; + else + { + if( num < 0 || num > 255 ) + return NULL; + addr->addr[ octet ] = (byte)num; + octet++; + if( str[ i ] != '.' || str[ i + 1 ] == '.' ) + break; + num = 0; + } + } + if( octet < 1 ) + return NULL; + return str + i; +} + +static const char *addr6parse( const char *str, addr_t *addr ) +{ + int i; + qboolean seen = qfalse; + /* keep track of the parts before and after the :: + it's either this or even uglier hacks */ + byte a[ ADDRLEN ], b[ ADDRLEN ]; + size_t before = 0, after = 0; + int num = 0; + /* 8 hexadectets unless :: is present */ + for( i = 0; before + after <= 8; i++ ) + { + //num = num << 4 | str[ i ] - '0'; + if( isdigit( str[ i ] ) ) + num = num * 16 + str[ i ] - '0'; + else if( str[ i ] >= 'A' && str[ i ] <= 'F' ) + num = num * 16 + 10 + str[ i ] - 'A'; + else if( str[ i ] >= 'a' && str[ i ] <= 'f' ) + num = num * 16 + 10 + str[ i ] - 'a'; + else + { + if( num < 0 || num > 65535 ) + return NULL; + if( i == 0 ) + { + // + } + else if( seen ) // :: has been seen already + { + b[ after * 2 ] = num >> 8; + b[ after * 2 + 1 ] = num & 0xff; + after++; + } + else + { + a[ before * 2 ] = num >> 8; + a[ before * 2 + 1 ] = num & 0xff; + before++; + } + if( !str[ i ] ) + break; + if( str[ i ] != ':' || i == 8 ) + break; + if( str[ i + 1 ] == ':' ) + { + // ::: or multiple :: + if( seen || str[ i + 2 ] == ':' ) + break; + seen = qtrue; + i++; + } + else if( i == 0 ) // starts with : but not :: + return NULL; + num = 0; + } + } + if( seen ) + { + // there have to be fewer than 8 hexadectets when :: is present + if( before + after == 8 ) + return NULL; + } + else if( before + after < 8 ) // require exactly 8 hexadectets + return NULL; + memset( addr, 0, sizeof( addr_t ) ); + addr->type = IPv6; + if( before ) + memcpy( addr->addr, a, before * 2 ); + if( after ) + memcpy( addr->addr + ADDRLEN - 2 * after, b, after * 2 ); + return str + i; +} + +qboolean G_AddressParse( const char *str, addr_t *addr, int *netmask ) +{ + const char *p; + int max; + if( strchr( str, ':' ) ) + { + p = addr6parse( str, addr ); + max = 128; + } + else + { + p = addr4parse( str, addr ); + max = 32; + } + if( !p ) + return qfalse; + if( *p == '/' ) + { + if( netmask ) + { + *netmask = atoi( p + 1 ); + if( *netmask < 1 || *netmask > max ) + *netmask = max; + } + } + else if( *p ) + return qfalse; + return qtrue; +} + +/* +=============== +G_AddressCompare + +Based largely on NET_CompareBaseAdrMask from ioq3 revision 1557 +=============== +*/ +qboolean G_AddressCompare( const addr_t *a, const addr_t *b, int netmask ) +{ + int i; + if( a->type != b->type ) + return qfalse; + if( a->type == IPv4 ) + { + if( netmask < 1 || netmask > 32 ) + netmask = 32; + } + else if( a->type == IPv6 ) + { + if( netmask < 1 || netmask > 128 ) + netmask = 128; + } + for( i = 0; netmask > 7; i++, netmask -= 8 ) + if( a->addr[ i ] != b->addr[ i ] ) + return qfalse; + if( netmask ) + { + netmask = ( ( 1 << netmask ) - 1 ) << ( 8 - netmask ); + return ( a->addr[ i ] & netmask ) == ( b->addr[ i ] & netmask ); + } + return qtrue; +} + +/* +=============== +G_AdrCmpStr + +The first argument may be in CIDR notation +=============== +*/ +qboolean G_AdrCmpStr( const char *a, const char *b ) +{ + int netmask = -1; + addr_t cmpa, cmpb; + if( !G_AddressParse( a, &cmpa, &netmask ) ) + return qfalse; + if( !G_AddressParse( b, &cmpb, NULL ) ) + return qfalse; + return G_AddressCompare( &cmpa, &cmpb, netmask ); +} |