#include #include #include #include #include #include #include #include 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 || add [] || remove || extend || action redirect || action message ", "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; }