Phase 1: Incoming Protocol-Level Protection - Added MAX_LINES_PER_POLL = 50 constant to limit lines processed per poll iteration in irc_feed(), preventing burst flooding. - Added poll_lines_left counter to Session struct, reset each poll cycle. - Changed RateLimitEntry to track by user@host instead of nick, preventing trivial bypass via nick cycling. - Parse userhost from IRC prefix (nick!user@host) in irc_handle(). - Added violation tracking to RateLimitEntry for escalating penalties. Phase 2: Outgoing Message Throttling - Added OUTGOING_DELAY_MS = 100 constant for minimum delay between sends. - Modified net_send() with timestamp-based throttling using clock_gettime(). - Added 200ms delay between lines in !gitlog output (cmd_gitlog.h). - Added 150ms delay between AI response chunks (ai.h). Phase 3: Rate Limit Improvements - Increased RATE_LIMIT_MAX from 8 to 32 for larger tracking buffer. - Added TELL_DELIVER_MAX = 5 to limit messages per nick delivery. - Added AI_COOLDOWN_SECS = 5 to prevent rapid AI query re-submission. Phase 4: Command-Specific Fixes - Added rate_limit_check() to cmd_greeting_or_chat.h for casual chat. - Limited tell_deliver() to 5 messages per nick, excess stays queued. - Added 5-second cooldown after AI query completion before next query. Files modified: zynk.h, zynk.c, irc.h, net.h, ai.h, db.h, and all 19 command header files to pass current_userhost to rate_limit_check().
197 lines
5.7 KiB
C
197 lines
5.7 KiB
C
#ifndef ZYNK_H
|
|
#define ZYNK_H
|
|
|
|
#define _GNU_SOURCE
|
|
#define _POSIX_C_SOURCE 200809L
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <dlfcn.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/stat.h>
|
|
#include <openssl/ssl.h>
|
|
|
|
#ifndef ZYNK_VERSION
|
|
#define ZYNK_VERSION "0.0.0"
|
|
#endif
|
|
|
|
#define VERSION ZYNK_VERSION
|
|
#define DEFAULT_SERVER "irc.libera.chat"
|
|
#define OPENCODE_BIN "/usr/bin/opencode"
|
|
#define CLR_RESET "\033[0m"
|
|
#define CLR_DIM "\033[2m"
|
|
#define CLR_RED "\033[31m"
|
|
#define CLR_GREEN "\033[32m"
|
|
#define CLR_YELLOW "\033[33m"
|
|
#define CLR_BLUE "\033[34m"
|
|
#define CLR_MAGENTA "\033[35m"
|
|
#define CLR_CYAN "\033[36m"
|
|
|
|
enum {
|
|
RBUF_SZ = 16384,
|
|
DEF_PORT = 6697,
|
|
POLL_MS = 100,
|
|
AI_TIMEOUT = 600,
|
|
MAX_CHANS = 32,
|
|
MAX_CHAN_OPS = 64,
|
|
RATE_LIMIT_WINDOW = 10,
|
|
RATE_LIMIT_MAX = 32,
|
|
MAX_LINES_PER_POLL = 50,
|
|
MAX_HISTORY = 12,
|
|
GITLOG_COUNT = 1,
|
|
GITLOG_MAX = 10,
|
|
CHANGELOG_COUNT = 1,
|
|
CHANGELOG_MAX = 10,
|
|
OUTGOING_DELAY_MS = 100,
|
|
TELL_DELIVER_MAX = 5,
|
|
AI_COOLDOWN_SECS = 5,
|
|
};
|
|
|
|
typedef struct {
|
|
int fd;
|
|
char host[256];
|
|
int port;
|
|
char nick[32];
|
|
char user[32];
|
|
char real[256];
|
|
char pass[256];
|
|
char auto_join[256];
|
|
int connected;
|
|
char rbuf[16384];
|
|
int rlen;
|
|
int dropping_line;
|
|
int poll_lines_left;
|
|
SSL *ssl;
|
|
int use_tls;
|
|
int code_restart;
|
|
} Session;
|
|
|
|
/* SQLite3 dynamic loading */
|
|
extern void *sqlite_lib;
|
|
extern int (*db_open)(const char*,void**);
|
|
extern int (*db_exec)(void*,const char*,int(*)(void*,int,char**,char**),void*,char**);
|
|
extern int (*db_close)(void*);
|
|
extern int (*db_prepare)(void*,const char*,int,void**,const char**);
|
|
extern int (*db_bind_text)(void*,int,const char*,int,void(*)(void*));
|
|
extern int (*db_step)(void*);
|
|
extern int (*db_finalize)(void*);
|
|
extern const unsigned char *(*db_column_text)(void*,int);
|
|
extern void (*db_free)(void*);
|
|
extern int (*db_bind_int64)(void*,int,long long);
|
|
extern long long (*db_last_rowid)(void*);
|
|
|
|
#define SQLITE_OK 0
|
|
#define SQLITE_ERROR 1
|
|
#define SQLITE_DONE 101
|
|
#define SQLITE_ROW 100
|
|
#define SQLITE_INTEGER 1
|
|
#define SQLITE_FLOAT 2
|
|
#define SQLITE_TEXT 3
|
|
#define SQLITE_BLOB 4
|
|
#define SQLITE_NULL 5
|
|
#define SQLITE_STATIC ((void(*)(void*))0)
|
|
#define SQLITE_TRANSIENT ((void(*)(void*))-1)
|
|
typedef struct { void *ptr; } sqlite3;
|
|
#define DB ((sqlite3*)db_handle)
|
|
extern void *db_handle;
|
|
|
|
/* DB key-value operations */
|
|
int db_set(const char *key, const char *value, const char *nick);
|
|
char *db_get_raw(const char *key);
|
|
char *db_get(const char *key);
|
|
char *db_get_random(void);
|
|
void db_rotate_history(const char *prefix, const char *value);
|
|
#define db_update_lastq(v) db_rotate_history("lastq", v)
|
|
#define db_update_lasta(v) db_rotate_history("lasta", v)
|
|
|
|
/* DB AI pending operations */
|
|
int ai_start(const char *question, const char *target, const char *nick, long long *out_id);
|
|
char *ai_get_question(long long id);
|
|
int ai_set_answer(long long id, const char *answer);
|
|
char *ai_get_answer(long long id);
|
|
char *ai_get_nick(long long id);
|
|
void ai_reset(long long id);
|
|
|
|
/* DB seen/tell operations */
|
|
void seen_update(const char *nick, const char *channel, const char *action);
|
|
char *seen_lookup(const char *nick);
|
|
void tell_add(const char *from, const char *to, const char *msg);
|
|
|
|
/* Channel op tracking */
|
|
typedef struct { char name[64]; char ops[MAX_CHAN_OPS][32]; int nops; } ChanEntry;
|
|
extern ChanEntry chans[MAX_CHANS];
|
|
extern int nchans;
|
|
ChanEntry *chan_get(const char *name);
|
|
ChanEntry *chan_get_or_add(const char *name);
|
|
int chan_is_op(const char *name, const char *nick);
|
|
void chan_add_op(const char *name, const char *nick);
|
|
void chan_del_op(const char *name, const char *nick);
|
|
void chan_rename_op(const char *oldnick, const char *newnick);
|
|
|
|
/* Rate limiting */
|
|
int rate_limit_check(const char *nick, const char *channel, const char *userhost);
|
|
extern char current_userhost[128];
|
|
|
|
/* Text utilities */
|
|
void strip_ai_phrases(char *s);
|
|
void strip_formatting(char *s);
|
|
void strip_opencode_header(char *s);
|
|
int has_crlf(const char *s);
|
|
int has_ctl(const char *s);
|
|
int secure_state_open(const char *path, int flags);
|
|
int valid_irc_word(const char *s);
|
|
void normalize_city(const char *in, char *display, size_t dsz, char *url, size_t usz);
|
|
void collapse_spaces(char *s);
|
|
const char *json_find(const char *haystack, const char *key);
|
|
|
|
/* Curl / fetch */
|
|
char *fetch_url(const char *url);
|
|
char *fetch_forecast(const char *city_url, const char *city_display);
|
|
|
|
/* Logging */
|
|
void log_stamp(void);
|
|
|
|
/* Signal / globals */
|
|
extern volatile sig_atomic_t running;
|
|
extern const char *g_opencode_bin;
|
|
extern volatile pid_t pending_ai_pid;
|
|
extern volatile long long pending_ai_id;
|
|
extern time_t start_time;
|
|
extern time_t last_ai_completion;
|
|
|
|
/* IRC protocol helpers */
|
|
void irc_msg(Session *s, const char *t, const char *txt);
|
|
void irc_reply(Session *s, const char *target, const char *nick, const char *txt);
|
|
void irc_join(Session *s, const char *chan);
|
|
void irc_nick(Session *s, const char *nn);
|
|
int net_send(Session *s, const char *fmt, ...);
|
|
|
|
/* Hot reload */
|
|
void reload_do(Session *s, const char *reply_target, int exec_new);
|
|
|
|
/* AI pipeline */
|
|
void ai_ask(Session *s, const char *question, const char *reply_target, const char *src_nick, const char *agent);
|
|
|
|
/* Compile helper */
|
|
int try_compile_and_restart(Session *s, const char *target);
|
|
|
|
/* DB init/cleanup */
|
|
int db_init(void);
|
|
void db_close_cleanup(void);
|
|
void sqlite_cleanup(void);
|
|
|
|
/* AI child task */
|
|
void ai_child_task(Session *s, long long id, const char *agent);
|
|
|
|
#endif
|