Browse Source

Updated 4th party mods: m_geoip_whois

Wazakindjes 3 years ago
parent
commit
8cb41ec84b
1 changed files with 273 additions and 43 deletions
  1. 273 43
      4thparty/m_geoip_whois.c

+ 273 - 43
4thparty/m_geoip_whois.c

@@ -2,11 +2,26 @@
 
 #define BUFLEN 8191
 
-// data file paths; place them in conf directory
+// default data file paths; place them in conf directory
 #define IPv4PATH "GeoLite2-Country-Blocks-IPv4.csv"
 #define COUNTRIESPATH "GeoLite2-Country-Locations-en.csv"
 #define IPv6PATH "GeoLite2-Country-Blocks-IPv6.csv"
 
+/*
+// Config example:
+geoip-whois {
+	ipv4-blocks-file "GeoLite2-Country-Blocks-IPv4.csv";
+	ipv6-blocks-file "GeoLite2-Country-Blocks-IPv6.csv";
+	countries-file "GeoLite2-Country-Locations-en.csv";
+	display-name; // Poland
+	display-code; // PL
+	display-continent; // EU
+	info-string "connected from "; // remember the trailing space!
+};
+*/
+
+#define MYCONF "geoip-whois"
+
 struct ip_range {
 	uint32_t addr;
 	uint32_t mask;
@@ -24,13 +39,17 @@ struct ip6_range {
 struct country {
 	char code[10];
 	char name[100];
+	char continent[25];
 	int id;
 	struct country *next;
 };
 
 struct ip_range *ip_range_list[256]; // we are keeping a separate list for each possible first octet to speed up searching
-struct ip6_range *ip6_range_list; // for ipv6 there would be too many separate lists so just use a single one
-struct country *country_list;
+struct ip6_range *ip6_range_list = NULL; // for ipv6 there would be too many separate lists so just use a single one
+struct country *country_list = NULL;
+int display_name = 0, display_code = 0, display_continent = 0;
+int have_config = 0;
+char *info_string = NULL;
 
 // function declarations here
 static int geoip_userconnect(aClient *);
@@ -39,22 +58,214 @@ static void free_ipv6(void);
 static void free_countries(void);
 static void free_all(void);
 int hexval(char c);
-static int read_ipv4(void);
+static int read_ipv4(char *file);
 static int ip6_convert(char *ip, uint16_t out[8]);
-static int read_ipv6(void);
-static int read_countries(void);
+static int read_ipv6(char *file);
+static int read_countries(char *file);
 static struct country *get_country(int id);
 static int get_v4_geoid(char *iip);
 static int get_v6_geoid(char *iip);
 static char *get_country_text(char *iip);
+int geoip_whois_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
+int geoip_whois_configposttest(int *errs);
+int geoip_whois_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
 
 ModuleHeader MOD_HEADER(m_geoip_whois) = {
 	"m_geoip_whois",
-	"$Id: v1.05 2018/12/03 k4be$",
+	"$Id: v1.06 2018/12/16 k4be$",
 	"add country info to /whois", 
 	"3.2-b8-1",
 	NULL 
 };
+	
+// config file stuff, based on Gottem's module
+int geoip_whois_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
+	ConfigEntry *cep; // For looping through our bl0cc
+	int errors = 0; // Error count
+	int i; // iter8or m8
+	int have_blocks_file = 0;
+	int have_countries_file = 0;
+	int display_anything = 0;
+
+	// Since we'll add a new top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
+	if(type != CONFIG_MAIN)
+		return 0; // Returning 0 means idgaf bout dis
+
+	// Check for valid config entries first
+	if(!ce || !ce->ce_varname)
+		return 0;
+
+	// If it isn't our bl0ck, idc
+	if(strcmp(ce->ce_varname, MYCONF))
+		return 0;
+
+	// Loop dat shyte fam
+	for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
+		// Do we even have a valid name l0l?
+		if(!cep->ce_varname) {
+			config_error("%s:%i: blank %s item", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF); // Rep0t error
+			errors++; // Increment err0r count fam
+			continue; // Next iteration imo tbh
+		}
+
+		if(!strcmp(cep->ce_varname, "ipv4-blocks-file")) {
+			if(!cep->ce_vardata) {
+				config_error("%s:%i: %s::%s must be a filename", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+				errors++; // Increment err0r count fam
+				continue;
+			}
+			char *filename = strdup(cep->ce_vardata);
+			convert_to_absolute_path(&filename, CONFDIR);
+			if(access(filename, R_OK)){
+				config_error("%s:%i: %s::%s: cannot open file \"%s\" for reading", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname, cep->ce_vardata);
+				errors++;
+				MyFree(filename);
+				continue;
+			}
+			MyFree(filename);
+			have_blocks_file = 1;
+			continue;
+		}
+
+		if(!strcmp(cep->ce_varname, "ipv6-blocks-file")) {
+			if(!cep->ce_vardata) {
+				config_error("%s:%i: %s::%s must be a filename", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+				errors++; // Increment err0r count fam
+				continue;
+			}
+			char *filename = strdup(cep->ce_vardata);
+			convert_to_absolute_path(&filename, CONFDIR);
+			if(access(filename, R_OK)){
+				config_error("%s:%i: %s::%s: cannot open file \"%s\" for reading", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname, cep->ce_vardata);
+				errors++;
+				MyFree(filename);
+				continue;
+			}
+			MyFree(filename);
+			have_blocks_file = 1;
+			continue;
+		}
+
+		if(!strcmp(cep->ce_varname, "countries-file")) {
+			if(!cep->ce_vardata) {
+				config_error("%s:%i: %s::%s must be a filename", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+				errors++; // Increment err0r count fam
+				continue;
+			}
+			char *filename = strdup(cep->ce_vardata);
+			convert_to_absolute_path(&filename, CONFDIR);
+			if(access(filename, R_OK)){
+				config_error("%s:%i: %s::%s: cannot open file \"%s\" for reading", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname, cep->ce_vardata);
+				errors++;
+				MyFree(filename);
+				continue;
+			}
+			MyFree(filename);
+			have_countries_file = 1;
+			continue;
+		}
+
+		if(!strcmp(cep->ce_varname, "display-name")) { // no value expected
+			continue;
+		}
+		if(!strcmp(cep->ce_varname, "display-code")) {
+			continue;
+		}
+		if(!strcmp(cep->ce_varname, "display-continent")) {
+			continue;
+		}
+		
+		if(!strcmp(cep->ce_varname, "info-string")) {
+			if(!cep->ce_vardata) {
+				config_error("%s:%i: %s::%s must be a string", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname);
+				errors++; // Increment err0r count fam
+			}
+			continue;
+		}
+
+		// Anything else is unknown to us =]
+		config_warn("%s:%i: unknown item %s::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, MYCONF, cep->ce_varname); // So display just a warning
+	}
+	
+	// note that we will get here only if the config block is present
+	if(!have_blocks_file){
+		config_error("m_geoip_whois: no (correct) address blocks file specified.");
+		errors++;
+	}
+
+	if(!have_countries_file){
+		config_error("m_geoip_whois: no (correct) countries file specified.");
+		errors++;
+	}
+	
+	*errs = errors;
+	return errors ? -1 : 1; // Returning 1 means "all good", -1 means we shat our panties
+}
+
+// required for some reason
+int geoip_whois_configposttest(int *errs) {
+	return 1;
+}
+
+// "Run" the config (everything should be valid at this point)
+int geoip_whois_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
+	ConfigEntry *cep; // For looping through our bl0cc
+
+	// Since we'll add a new top-level block to unrealircd.conf, need to filter on CONFIG_MAIN lmao
+	if(type != CONFIG_MAIN)
+		return 0; // Returning 0 means idgaf bout dis
+
+	// Check for valid config entries first
+	if(!ce || !ce->ce_varname)
+		return 0;
+
+	// If it isn't our bl0cc, idc
+	if(strcmp(ce->ce_varname, MYCONF))
+		return 0;
+
+	have_config = 1;
+	
+	// Loop dat shyte fam
+	for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
+		// Do we even have a valid name l0l?
+		if(!cep->ce_varname)
+			continue; // Next iteration imo tbh
+
+		if(cep->ce_vardata && !strcmp(cep->ce_varname, "ipv4-blocks-file")) {
+			if(read_ipv4(cep->ce_vardata)) return -1;
+			continue;
+		}
+
+		if(cep->ce_vardata && !strcmp(cep->ce_varname, "ipv6-blocks-file")) {
+			if(read_ipv6(cep->ce_vardata)) return -1;
+			continue;
+		}
+
+		if(cep->ce_vardata && !strcmp(cep->ce_varname, "countries-file")) {
+			if(read_countries(cep->ce_vardata)) return -1;
+			continue;
+		}
+
+		if(!strcmp(cep->ce_varname, "display-name")) {
+			display_name = 1;
+			continue;
+		}
+		if(!strcmp(cep->ce_varname, "display-code")) {
+			display_code = 1;
+			continue;
+		}
+		if(!strcmp(cep->ce_varname, "display-continent")) {
+			display_continent = 1;
+			continue;
+		}
+		
+		if(cep->ce_vardata && !strcmp(cep->ce_varname, "info-string")) {
+			info_string = strdup(cep->ce_vardata);
+			continue;
+		}
+	}
+	return 1; // We good
+}
 
 // functions for freeing allocated memory
 
@@ -111,7 +322,7 @@ int hexval(char c){
 
 // reading data from files
 
-static int read_ipv4(void){
+static int read_ipv4(char *file){
 	FILE *u;
 	char buf[BUFLEN+1];
 	int ip[4], cidr, geoid;
@@ -122,9 +333,7 @@ static int read_ipv4(void){
 	memset(curr, 0, sizeof(curr));
 	int i;
 	
-	char *filename;
-	filename = MyMallocEx(strlen(IPv4PATH) + 2);
-	strcpy(filename, IPv4PATH);
+	char *filename = strdup(file);
 	convert_to_absolute_path(&filename, CONFDIR);
 	u = fopen(filename, "r");
 	MyFree(filename);
@@ -245,7 +454,7 @@ static int ip6_convert(char *ip, uint16_t out[8]){ // convert text to binary for
 	}
 }
 
-static int read_ipv6(void){
+static int read_ipv6(char *file){
 	FILE *u;
 	char buf[BUFLEN+1];
 	char *bptr, *optr;
@@ -257,9 +466,7 @@ static int read_ipv6(void){
 	struct ip6_range *ptr;
 	int error;
 
-	char *filename;
-	filename = MyMallocEx(strlen(IPv6PATH) + 2);
-	strcpy(filename, IPv6PATH);
+	char *filename = strdup(file);
 	convert_to_absolute_path(&filename, CONFDIR);
 	u = fopen(filename, "r");
 	MyFree(filename);
@@ -322,18 +529,17 @@ static int read_ipv6(void){
 
 }
 
-static int read_countries(void){
+static int read_countries(char *file){
 	FILE *u;
 	char code[10];
+	char continent[25];
 	char name[100];
 	char buf[BUFLEN+1];
 	int i;
 	int id;
 	struct country *curr;
 	
-	char *filename;
-	filename = MyMallocEx(strlen(COUNTRIESPATH) + 2);
-	strcpy(filename, COUNTRIESPATH);
+	char *filename = strdup(file);
 	convert_to_absolute_path(&filename, CONFDIR);
 	u = fopen(filename, "r");
 	MyFree(filename);
@@ -348,16 +554,25 @@ static int read_countries(void){
 	}
 	while(fscanf(u, "%d,%[^\n]", &id, buf) == 2){ //getting country ID integer and all other data in string
 		char *ptr = buf;
-		char *optr = code;
+		char *codeptr = code;
+		char *contptr = continent;
+		char *nptr = name;
 		int quote_open = 0;
 		i=0;
 		while(*ptr){
+			if(i == 2){
+				if(*ptr == ','){
+					goto next_line; // no continent?
+				}
+				*contptr = *ptr; // scan for continent name
+				contptr++;
+			}
 			if(i == 3){
 				if(*ptr == ','){	// country code is empty
 					goto next_line;	// -- that means only the continent is specified - we ignore it completely
 				}
-				*optr = *ptr; // scan for country code (DE, PL, US etc)
-				optr++;
+				*codeptr = *ptr; // scan for country code (DE, PL, US etc)
+				codeptr++;
 			}
 			ptr++;
 			if(*ptr == ','){
@@ -366,17 +581,17 @@ static int read_countries(void){
 				if(i == 4) break; // look for country name entry
 			}
 		}
-		*optr = '\0';
-		optr = name;
+		*codeptr = '\0';
+		*contptr = '\0';
 		while(*ptr){
 			switch(*ptr){
 				case '"': quote_open = !quote_open; ptr++; continue;
 				case ',': if(!quote_open) goto end_country_name; // we reached the end of current CSV field
-				default: *optr++ = *ptr++; break; // scan for country name
+				default: *nptr++ = *ptr++; break; // scan for country name
 			}
 		}
 		end_country_name:
-		*optr = '\0';
+		*nptr = '\0';
 		if(country_list){
 			curr->next = MyMallocEx(sizeof(struct country));
 			curr = curr->next;
@@ -387,8 +602,9 @@ static int read_countries(void){
 		curr->next = NULL;
 		strcpy(curr->code, code);
 		strcpy(curr->name, name);
+		strcpy(curr->continent, continent);
 		curr->id = id;
-		next_line: continue;		
+		next_line: continue;
 	}
 	fclose(u);
 	return 0;
@@ -488,12 +704,22 @@ static char *get_country_text(char *iip){
 	if(geoid == 0) return NULL;
 	curr_country = get_country(geoid);
 	if(!curr_country) return NULL;
-	sprintf(buf, "%s (%s)\n", curr_country->name, curr_country->code);
+	if(display_name) strcpy(buf, curr_country->name);
+	if(display_code) sprintf(buf + strlen(buf), "%s(%s)", display_name?" ":"", curr_country->code);
+	if(display_continent) sprintf(buf + strlen(buf), "%s%s", (display_name || display_code)?", ":"", curr_country->continent);
 	return buf;
 }
 
+MOD_TEST(m_geoip_whois)
+{
+	HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_whois_configtest);
+	HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, geoip_whois_configposttest);
+	return MOD_SUCCESS;
+}
+
 MOD_INIT(m_geoip_whois)
 {
+	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_whois_configrun);
 	HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CONNECT, 0, geoip_userconnect);
 	HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, geoip_userconnect);
 	return MOD_SUCCESS;
@@ -502,21 +728,25 @@ MOD_INIT(m_geoip_whois)
 MOD_LOAD(m_geoip_whois)
 {
 	aClient *acptr;
-	if(read_ipv4()){
-		free_ipv4();
-		return MOD_FAILED;
-	}
-	if(read_countries()){
-		free_ipv4();
-		free_countries();
-		return MOD_FAILED;
-	}
-	if(read_ipv6()){
-		free_ipv4();
-		free_countries();
-		free_ipv6();
-		return MOD_FAILED;
+	if(!have_config){
+		sendto_realops("Warning: no configuration for m_geoip_whois, using default options");
+		display_name = 1;
+		display_code = 1;
+		if(read_ipv4(IPv4PATH)){
+			free_all();
+			return MOD_FAILED;
+		}
+		if(read_countries(COUNTRIESPATH)){
+			free_all();
+			return MOD_FAILED;
+		}
+		if(read_ipv6(IPv6PATH)){
+			free_all();
+			return MOD_FAILED;
+		}
+		info_string = strdup("connected from ");
 	}
+	
 	list_for_each_entry(acptr, &client_list, client_node){
 		if (!IsPerson(acptr)) continue;
 		geoip_userconnect(acptr); // add info for all users upon module loading
@@ -539,7 +769,7 @@ static int geoip_userconnect(aClient *cptr) {
 	char *data = get_country_text(cptr->ip);
 	if(!data) return HOOK_CONTINUE;
 	char buf[BUFLEN+1];
-	sprintf(buf, "connected from %s", data);
+	sprintf(buf, "%s%s", info_string, data);
 	swhois_delete(cptr, "geoip", "*", &me, NULL); //somehow has it already set
 	swhois_add(cptr, "geoip", 0, buf, &me, NULL);
 	return HOOK_CONTINUE;