Skip to content

Commit

Permalink
Add third/ipinfo_whois, third/listsg and third/welcomemessages
Browse files Browse the repository at this point in the history
Merge pull request #111 from revrsedev/unreal6

I did not actually try the modules.
  • Loading branch information
syzop authored Jun 26, 2024
2 parents 5499d41 + 67d1efa commit 577887c
Show file tree
Hide file tree
Showing 3 changed files with 577 additions and 0 deletions.
271 changes: 271 additions & 0 deletions files/ipinfo_whois.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
/*
Licence: GPLv3 or later
Copyright Ⓒ 2024 Jean Chevronnet
*/
/*** <<<MODULE MANAGER START>>>
module
{
documentation "https://github.com/revrsedev/UnrealIRCD-6-mods-contrib/blob/main/ipinfo_whois/README.md";
troubleshooting "In case of problems, documentation or e-mail me at [email protected]";
min-unrealircd-version "6.*";
post-install-text {
"The module is installed, now all you need to do is add a 'loadmodule' line to your config file:";
"loadmodule \"third/ipinfo_whois\";";
"Add the TOKEN API from ipinfo.io to the block ipinfo_whois in you're config file";
"Then /rehash the IRCd.";
"For usage information, refer to the module's documentation found at: https://github.com/revrsedev/UnrealIRCD-6-mods-contrib/blob/main/ipinfo_whois/README.md";
}
}
*** <<<MODULE MANAGER END>>>
*/

#include "unrealircd.h"
#include <curl/curl.h>
#include <jansson.h>
#include <uthash.h>

#define MYCONF "ipinfo_whois"
#define API_URL "https://ipinfo.io/"

typedef struct {
char *apikey;
} cfgstruct;

static cfgstruct muhcfg;

typedef struct {
char ip[46]; // Supports both IPv4 and IPv6
char info[256];
time_t timestamp;
UT_hash_handle hh;
} CacheEntry;

CacheEntry *cache = NULL;
time_t cache_duration = 86400; // 24 hours

ModuleHeader MOD_HEADER = {
"third/ipinfo_whois",
"1.0.0",
"Show IPinfo.io information in WHOIS",
"reverse",
"unrealircd-6",
};

int ipinfo_whois_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
int ipinfo_whois_configposttest(int *errs);
int ipinfo_whois_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
int ipinfo_whois_whois(Client *requester, Client *acptr, NameValuePrioList **list);
void free_cache();

MOD_TEST() {
memset(&muhcfg, 0, sizeof(muhcfg));
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, ipinfo_whois_configtest);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, ipinfo_whois_configposttest);
return MOD_SUCCESS;
}

MOD_INIT() {
MARK_AS_GLOBAL_MODULE(modinfo);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, ipinfo_whois_configrun);
HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, ipinfo_whois_whois);
return MOD_SUCCESS;
}

MOD_LOAD() {
return MOD_SUCCESS;
}

MOD_UNLOAD() {
safe_free(muhcfg.apikey);
free_cache();
return MOD_SUCCESS;
}

int ipinfo_whois_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
int errors = 0;
ConfigEntry *cep;

if (type != CONFIG_MAIN)
return 0;

if (!ce || !ce->name)
return 0;

if (strcmp(ce->name, MYCONF))
return 0;

for (cep = ce->items; cep; cep = cep->next) {
if (!cep->name) {
config_error("%s:%i: blank %s item", cep->file->filename, cep->line_number, MYCONF);
errors++;
continue;
}

if (!strcmp(cep->name, "apikey")) {
if (muhcfg.apikey)
config_warn("%s:%i: duplicate directive %s::%s, will use the last encountered one", cep->file->filename, cep->line_number, MYCONF, cep->name);
safe_strdup(muhcfg.apikey, cep->value);
continue;
}
}

*errs = errors;
return errors ? -1 : 1;
}

int ipinfo_whois_configposttest(int *errs) {
int errors = 0;

if (!muhcfg.apikey) {
config_error("No API key found for %s::apikey", MYCONF);
errors++;
}

*errs = errors;
return errors ? -1 : 1;
}

int ipinfo_whois_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
ConfigEntry *cep;

if (type != CONFIG_MAIN)
return 0;

if (!ce || !ce->name)
return 0;

if (strcmp(ce->name, MYCONF))
return 0;

for (cep = ce->items; cep; cep = cep->next) {
if (!cep->name)
continue;

if (!strcmp(cep->name, "apikey")) {
safe_strdup(muhcfg.apikey, cep->value);
continue;
}
}
return 1;
}

struct MemoryStruct {
char *memory;
size_t size;
};

static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;

char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if (ptr == NULL) {
return 0; // out of memory!
}

mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;

return realsize;
}

void add_to_cache(const char *ip, const char *info) {
CacheEntry *entry = malloc(sizeof(CacheEntry));
strcpy(entry->ip, ip);
strcpy(entry->info, info);
entry->timestamp = time(NULL);
HASH_ADD_STR(cache, ip, entry);
}

CacheEntry *find_in_cache(const char *ip) {
CacheEntry *entry;
HASH_FIND_STR(cache, ip, entry);
if (entry && (time(NULL) - entry->timestamp) > cache_duration) {
HASH_DEL(cache, entry);
free(entry);
entry = NULL;
}
return entry;
}

void free_cache() {
CacheEntry *current_entry, *tmp;
HASH_ITER(hh, cache, current_entry, tmp) {
HASH_DEL(cache, current_entry);
free(current_entry);
}
}

int ipinfo_whois_whois(Client *requester, Client *acptr, NameValuePrioList **list) {
if (!IsOper(requester) || IsULine(acptr) || IsServer(acptr)) {
return 0; // Only opers can see the IP info, and ignore service clients and servers
}

CacheEntry *cached = find_in_cache(acptr->ip);
if (cached) {
add_nvplist_numeric_fmt(list, 320, "city", acptr, 320, "%s :is connecting from %s", acptr->name, cached->info);
return 0;
}

CURL *curl_handle;
CURLcode res;
struct MemoryStruct chunk;

chunk.memory = malloc(1); // will be grown as needed by the realloc above
chunk.size = 0; // no data at this point

curl_global_init(CURL_GLOBAL_ALL);

curl_handle = curl_easy_init();

if (curl_handle) {
char url[256];
snprintf(url, sizeof(url), API_URL "%s?token=%s", acptr->ip, muhcfg.apikey);

curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);

res = curl_easy_perform(curl_handle);

if (res == CURLE_OK) {
json_t *root;
json_error_t error;
json_t *city, *region, *country, *org;

root = json_loads(chunk.memory, 0, &error);

if (root) {
city = json_object_get(root, "city");
region = json_object_get(root, "region");
country = json_object_get(root, "country");
org = json_object_get(root, "org");

if (json_is_string(city) && json_is_string(region) && json_is_string(country) && json_is_string(org)) {
char result_info[256];
snprintf(result_info, sizeof(result_info), "City: %s, Region: %s, Country: %s, Org: %s",
json_string_value(city),
json_string_value(region),
json_string_value(country),
json_string_value(org));

add_to_cache(acptr->ip, result_info);

add_nvplist_numeric_fmt(list, 320, "city", acptr, 320, "%s :is connecting from %s", acptr->name, result_info);
}

json_decref(root);
}
}

curl_easy_cleanup(curl_handle);
free(chunk.memory);
}

curl_global_cleanup();

return 0;
}
124 changes: 124 additions & 0 deletions files/listsg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
Licence: GPLv3 or later
Copyright Ⓒ 2024 Jean Chevronnet
*/
/*** <<<MODULE MANAGER START>>>
module
{
documentation "https://github.com/revrsedev/UnrealIRCD-6-mods-contrib/blob/main/listsg/README.md";
troubleshooting "In case of problems, documentation or e-mail me at [email protected]";
min-unrealircd-version "6.*";
post-install-text {
"The module is installed, now all you need to do is add a 'loadmodule' line to your config file:";
"loadmodule \"third/listsg\";";
"Then /rehash the IRCd.";
"For usage information, refer to the module's documentation found at: https://github.com/revrsedev/UnrealIRCD-6-mods-contrib/blob/main/listsg/README.md";
}
}
*** <<<MODULE MANAGER END>>>
*/

#include "unrealircd.h"

#define MSG_SG "SG"
#define MAX_BUFFER_SIZE 512
#define MAX_NICKNAMES_PER_LINE 10

CMD_FUNC(cmd_sg);

// Forward declarations
void list_security_groups_for_user(Client *client, Client *user);
void list_members_of_security_group(Client *client, const char *groupname);

ModuleHeader MOD_HEADER = {
"third/listsg", // Module name
"1.0", // Version
"Command /SG to list security groups and their members", // Description
"reverse", // Author
"unrealircd-6", // UnrealIRCd version
};

MOD_INIT() {
CommandAdd(modinfo->handle, MSG_SG, cmd_sg, 1, CMD_USER); // Adding the command
return MOD_SUCCESS;
}

MOD_LOAD() {
return MOD_SUCCESS;
}

MOD_UNLOAD() {
return MOD_SUCCESS;
}

CMD_FUNC(cmd_sg) {
if (parc < 2) {
sendnotice(client, "Usage: /SG <nickname|groupname>");
return;
}

const char *arg = parv[1];
Client *target_user = find_client(arg, NULL);
if (target_user) {
list_security_groups_for_user(client, target_user);
} else {
list_members_of_security_group(client, arg);
}
}

// Function to list security groups a user is part of
void list_security_groups_for_user(Client *client, Client *user) {
const char *groups = get_security_groups(user);
if (!groups || *groups == '\0') {
sendnotice(client, "User %s is not part of any security groups.", user->name);
return;
}

sendnotice(client, "Security groups for user %s:", user->name);
sendnotice(client, "- %s", groups);
}

// Function to list members of a security group
void list_members_of_security_group(Client *client, const char *groupname) {
SecurityGroup *group = find_security_group(groupname);
if (!group) {
sendnotice(client, "Security group %s does not exist.", groupname);
return;
}

sendnotice(client, "Members of security group %s:", groupname);

Client *target;
int member_found = 0;
char buffer[MAX_BUFFER_SIZE];
buffer[0] = '\0';
int nickname_count = 0;

list_for_each_entry(target, &lclient_list, lclient_node) {
if (user_allowed_by_security_group_name(target, groupname)) {
if (nickname_count > 0) {
strlcat(buffer, ", ", sizeof(buffer));
}
strlcat(buffer, target->name, sizeof(buffer));
nickname_count++;
member_found = 1;

// Check if the buffer is near its limit or if we reached the max nicknames per line
if (strlen(buffer) >= MAX_BUFFER_SIZE - 50 || nickname_count >= MAX_NICKNAMES_PER_LINE) {
sendnotice(client, "- %s", buffer);
buffer[0] = '\0'; // Reset buffer
nickname_count = 0; // Reset nickname count
}
}
}

// Send any remaining nicknames in the buffer
if (nickname_count > 0) {
sendnotice(client, "- %s", buffer);
}

if (!member_found) {
sendnotice(client, "Security group %s has no members.", groupname);
}
}
Loading

0 comments on commit 577887c

Please sign in to comment.