Files
neorelay/files/neoguard/neoguard_plugin.c
T
NIA Sanitized Release Publisher 7141326ea0 Sanitized public release v1.0.3
Source-Tag: v1.0.3
Manifest-SHA256: b7b09bf48f9e092ed8ad82f99c0319d6c3837d0a1549bd0cc4a374276c0f3896
2026-07-20 22:35:36 +00:00

187 lines
6.8 KiB
C

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <weechat/weechat-plugin.h>
WEECHAT_PLUGIN_NAME("neoguard")
WEECHAT_PLUGIN_DESCRIPTION("NeoGuard abuse-control buffer")
WEECHAT_PLUGIN_AUTHOR("{{PRODUCT_NAME}}")
WEECHAT_PLUGIN_VERSION("1.1")
WEECHAT_PLUGIN_LICENSE("GPL3")
struct t_weechat_plugin *weechat_plugin;
static struct t_gui_buffer *guard_buffer;
static struct t_hook *fd_hook;
static int agent_fd = -1;
static char receive_buffer[16384];
static size_t receive_length;
static void print_line(const char *line)
{
const char *color = "chat";
if (strncmp(line, "EVENT\tBAN", 9) == 0 || strncmp(line, "EVENT\tPERM", 10) == 0)
color = "lightred";
else if (strncmp(line, "EVENT\tWARN", 10) == 0)
color = "yellow";
else if (strncmp(line, "ERROR\t", 6) == 0)
color = "red";
weechat_printf(guard_buffer, "%s%s", weechat_color(color), line);
}
static void disconnect_agent(void)
{
if (fd_hook) { weechat_unhook(fd_hook); fd_hook = NULL; }
if (agent_fd >= 0) close(agent_fd);
agent_fd = -1;
receive_length = 0;
}
static int agent_read_cb(const void *pointer, void *data, int fd)
{
(void)pointer; (void)data; (void)fd;
ssize_t count = read(agent_fd, receive_buffer + receive_length,
sizeof(receive_buffer) - receive_length - 1);
if (count <= 0) { disconnect_agent(); return WEECHAT_RC_OK; }
receive_length += (size_t)count;
receive_buffer[receive_length] = '\0';
char *start = receive_buffer;
char *newline;
while ((newline = strchr(start, '\n'))) {
*newline = '\0';
print_line(start);
start = newline + 1;
}
receive_length = strlen(start);
memmove(receive_buffer, start, receive_length);
return WEECHAT_RC_OK;
}
static int connect_agent(void)
{
struct sockaddr_un address;
if (agent_fd >= 0) return 1;
agent_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (agent_fd < 0) return 0;
memset(&address, 0, sizeof(address));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, sizeof(address.sun_path), "%s", "{{NEOGUARD_CONTROL_SOCKET_PATH}}");
if (connect(agent_fd, (struct sockaddr *)&address, sizeof(address)) != 0) {
close(agent_fd); agent_fd = -1; return 0;
}
fd_hook = weechat_hook_fd(agent_fd, 1, 0, 0, &agent_read_cb, NULL, NULL);
return 1;
}
static void send_agent(const char *kind, const char *first, const char *second)
{
char message[4096];
if (!connect_agent()) return;
snprintf(message, sizeof(message), "%s\t%s%s%s\n", kind, first,
second ? "\t" : "", second ? second : "");
if (write(agent_fd, message, strlen(message)) < 0) disconnect_agent();
}
static void send_attempt(const char *address, int port, const char *detail)
{
char message[4096];
if (!connect_agent()) return;
if (port >= 1 && port <= 65535)
snprintf(message, sizeof(message), "ATTEMPT\t%s\t%d\t%s\n", address, port, detail);
else
snprintf(message, sizeof(message), "ATTEMPT\t%s\t%s\n", address, detail);
if (write(agent_fd, message, strlen(message)) < 0) disconnect_agent();
}
static int reconnect_cb(const void *pointer, void *data, int remaining_calls)
{
(void)pointer; (void)data; (void)remaining_calls;
connect_agent();
return WEECHAT_RC_OK;
}
static int input_cb(const void *pointer, void *data, struct t_gui_buffer *buffer, const char *input)
{
(void)pointer; (void)data; (void)buffer;
if (input && input[0]) send_agent("COMMAND", input, NULL);
return WEECHAT_RC_OK;
}
static int close_cb(const void *pointer, void *data, struct t_gui_buffer *buffer)
{
(void)pointer; (void)data; (void)buffer;
guard_buffer = NULL;
return WEECHAT_RC_OK;
}
static int ensure_buffer(void)
{
if (!guard_buffer)
guard_buffer = weechat_buffer_search("neoguard", "control");
if (!guard_buffer)
guard_buffer = weechat_buffer_new("control", &input_cb, NULL, NULL, &close_cb, NULL, NULL);
if (!guard_buffer) return 0;
weechat_buffer_set_pointer(guard_buffer, "input_callback", &input_cb);
weechat_buffer_set_pointer(guard_buffer, "close_callback", &close_cb);
weechat_buffer_set(guard_buffer, "short_name", "NeoGuard");
weechat_buffer_set(guard_buffer, "title", "NeoGuard | status/list | show/add/remove/extend IP | action IP redirect|message");
weechat_buffer_set(guard_buffer, "hidden", "0");
weechat_buffer_set(guard_buffer, "notify", "3");
weechat_buffer_set(guard_buffer, "localvar_set_no_log", "1");
weechat_buffer_set(guard_buffer, "localvar_set_pinned", "true");
weechat_buffer_set(guard_buffer, "localvar_set_type", "other");
return 1;
}
static int command_cb(const void *pointer, void *data, struct t_gui_buffer *buffer,
int argc, char **argv, char **argv_eol)
{
(void)pointer; (void)data; (void)buffer; (void)argc; (void)argv;
if (ensure_buffer()) {
weechat_buffer_set(guard_buffer, "display", "1");
if (argv_eol[1] && argv_eol[1][0]) send_agent("COMMAND", argv_eol[1], NULL);
}
return WEECHAT_RC_OK;
}
static int relay_failure_cb(const void *pointer, void *data, const char *signal,
const char *type_data, void *signal_data)
{
(void)pointer; (void)data; (void)signal; (void)type_data;
struct t_infolist *list = weechat_infolist_get("relay", signal_data, NULL);
if (list && weechat_infolist_next(list)) {
const char *real_ip = weechat_infolist_string(list, "real_ip");
const char *address = weechat_infolist_string(list, "address");
int port = weechat_infolist_integer(list, "server_port");
send_attempt((real_ip && real_ip[0]) ? real_ip : address, port,
"relay authentication failed");
}
if (list) weechat_infolist_free(list);
return WEECHAT_RC_OK;
}
int weechat_plugin_init(struct t_weechat_plugin *plugin, int argc, char *argv[])
{
(void)argc; (void)argv;
weechat_plugin = plugin;
weechat_hook_command("neoguard", "open/control NeoGuard",
"list || status || show <ip> || add <ip> [<duration>] || remove <ip> || extend <ip> <duration> || action <ip> redirect <https-url> || action <ip> message <text>",
"Inspect and control NeoGuard bans and per-address actions",
"list|status|show|add|remove|extend|action",
&command_cb, NULL, NULL);
weechat_hook_signal("relay_client_auth_failed", &relay_failure_cb, NULL, NULL);
weechat_hook_timer(5000, 0, 0, &reconnect_cb, NULL, NULL);
command_cb(NULL, NULL, NULL, 0, NULL, (char *[]){"", "status"});
return WEECHAT_RC_OK;
}
int weechat_plugin_end(struct t_weechat_plugin *plugin)
{
(void)plugin;
disconnect_agent();
return WEECHAT_RC_OK;
}