diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README | 15 | ||||
-rw-r--r-- | debian/changelog | 8 | ||||
-rw-r--r-- | dns.c | 27 | ||||
-rw-r--r-- | domain2name.c | 2 | ||||
-rw-r--r-- | ircxmpp.c | 88 | ||||
-rw-r--r-- | ircxmpp.h | 15 |
8 files changed, 123 insertions, 36 deletions
@@ -5,3 +5,5 @@ ircxmpp *.so *.o *.out +tmp/* +*.orig @@ -1,6 +1,6 @@ DESTDIR=/ CC = cc -cflags = -O0 -Wall -I. -fpic -Wformat-security -Wextra -pedantic -g3 $(shell pkg-config --cflags libstrophe) +cflags = -O0 -Wall -I. -fpic -Wformat-security -Wextra -pedantic -g3 $(shell pkg-config --cflags libstrophe) -I/usr/include/libircclient ldflags = $(shell pkg-config --libs libstrophe) -lircclient PROJ = ircxmpp # cflags and ldflags are used so that users specifying CFLAGS and LDFLAGS do not override my flags @@ -123,4 +123,17 @@ IPv6: - TODO: add support in DNS server for ip6 (binding to AF_INET6 sockets, ip6 memmem, ...) - TODO: use irc_connect6 somehow in libircclient, libstrophe should already work with IPv6 - -- Anton Luka Šijanec <anton@sijanec.eu> Fri, 29 Apr 2022 17:00:00 +0200 +Checklist before releasing: + - test compilers: tcc, gcc, clang. + - scan-build make + - gcc -fanalyzer + - make valgrind + +Bugs: + - CFLAGS=-fanalyzer make -e reports two memory leaks in bridge_forward + - tcc build does not work from my amd64 gentoo (referenced dlls not found) + - dns ptr was not yet tested yet, so it probably doesn't work. don't use it. + - scan-build returns three non-bugs + - valgrind: still reachable leaks of allocations by libxml2 in libstrophe + + -- Anton Luka Šijanec <anton@sijanec.eu> Tue, 28 Jun 2022 19:30:00 +0200 diff --git a/debian/changelog b/debian/changelog index f366f51..8bf8881 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +ircxmpp (0.0.11-1) stable; urgency=low + + * tdelete(3) now isn't called from twalk(3) / trecurse() (that's illegal) + * program now quits if a required envvar is not set, instead of just printing + * program now correctly sends message with replaced CR/LF with \t to IRC + + -- Anton Luka Šijanec <anton@sijanec.eu> Tue, 28 Jun 2022 19:30:00 +0200 + ircxmpp (0.0.10-1) stable; urgency=low * do not use 0.0.9 @@ -110,6 +110,10 @@ struct dns * dns_init (void) { return NULL; dns->fd = -1; dns->domain = strdup(" call dns_set_domain to set the domain"); + if (!dns->domain) { + free(dns); + return NULL; + } dns->domain[0] = strlen(dns->domain)-1; dns->log_handler = dns_default_log_handler; dns->sockaddr.sin_family = AF_INET; @@ -117,12 +121,23 @@ struct dns * dns_init (void) { dns->sockaddr.sin_addr.s_addr = INADDR_ANY; return dns; } -static void dns_set_domain (struct dns * dns, const char * domain) { // static functions are - int required = domain2name_len(domain, strlen(domain)); // visible inside the same - if (required <= 0) // translation unit - they - return; // are visible across - free(dns->domain); // included files - domain2name((dns->domain = malloc(required)), domain, strlen(domain)); +static void dns_set_domain (struct dns * dns, const char * domain) { + char buf[64]; + int required = domain2name_len(domain, strlen(domain)); + if (required <= 0) { + sprintf(buf, "domain2name_len failed with %d", required); + dns->log_handler(dns->log_userdata, DNS_ERROR, "dns", buf); + return; + } + char * new = malloc(required); + if (!new) { + sprintf(buf, "malloc of size %d failed", required); + dns->log_handler(dns->log_userdata, DNS_ERROR, "dns", buf); + return; + } + free(dns->domain); + dns->domain = new; + domain2name(dns->domain, domain, strlen(domain)); } static void dns_set_log_handler (struct dns * dns, dns_log_handler log_handler) { if (!log_handler) diff --git a/domain2name.c b/domain2name.c index 47bfdad..9faaf3e 100644 --- a/domain2name.c +++ b/domain2name.c @@ -39,7 +39,7 @@ int domain2name (char * n /* at least _len bytes */, const char * s, int l) { /* } *w++ = '\0'; /* luckily this makes domain2name kind of safe for handling as a string (: */ return w-n; /* we return number of bytes written */ -} /* no compression, it's 2022, net bandwidth is unlimited. n2d OFC does decompress ptrs acc2 std. */ +} __attribute__((nonnull)) int name2domain_len (const char * u, int s /* size of u */, const char * n /* name */) { #define N2DO(x) (ntohs(x) & ~(1 << 15 | 1 << 14)) /* pointer offset */ int r = 0; @@ -63,13 +63,13 @@ static void free_bridge (struct bridge * bridge, const char * razlog) { LOG(bridge->ircxmpp, IRCXMPP_DEBUG, "freeing bridge with reason: %s", razlog); if (bridge->irc) { irc_cmd_quit(bridge->irc, razlog); - if (strcmp(razlog, "free_bridge_from_tdestroy")) + if (strcmp(razlog, "free_bridge_from_tdestroy")) // če nismo iz tdestroy irc_run_once(bridge); // verjetno je to potrebno, da pošlje quit irc_destroy_session(bridge->irc); } if (bridge->conn) xmpp_conn_release(bridge->conn); // graceful disconnect, what is that? - if (strcmp(razlog, "free_bridge_from_tdestroy")) + if (strcmp(razlog, "free_bridge_from_tdestroy")) // če nismo iz tdestroy tdelete(bridge, &bridge->ircxmpp->bridges, bridge_compare); free(bridge->identifier); for (size_t i = 0; i < bridge->messages_length; i++) @@ -133,6 +133,10 @@ static void bridge_forward (const char * f, const char * m, struct ircxmpp * irc m ? m : "[join only]"); if (!bridge) { bridge = calloc(1, sizeof(struct bridge)); + if (!bridge) { + return; + LOG(ircxmpp, IRCXMPP_ERROR, "calloc bridge failed"); + } bridge->identifier = strdup(f); bridge->ircxmpp = ircxmpp; bridge->side = !s; @@ -181,8 +185,12 @@ static void bridge_forward (const char * f, const char * m, struct ircxmpp * irc } irc_run_once(bridge); } else if (m) { - bridge->messages = realloc(bridge->messages, - sizeof(*bridge->messages) * (bridge->messages_length+1)); + typeof(bridge->messages) new = reallocarray(bridge->messages, bridge->messages_length+1, sizeof(*bridge->messages)); + if (!new) { + LOG(ircxmpp, IRCXMPP_ERROR, "reallocarray(bridge->messages, bridge->messages_length+1 = %z, sizeof(*bridge->messages) = %z) failed", bridge->messages_length+1, sizeof*(bridge->messages)); + return; + } + bridge->messages = new; bridge->messages[bridge->messages_length++] = strdup(m); } } /* m can be NULL, in that case we only join. */ @@ -272,8 +280,7 @@ static void conn_handler_bridge (xmpp_conn_t * const conn __attribute__((unused) xmpp_send(bridge->conn, pres); xmpp_stanza_release(pres); } else - LOG(bridge->ircxmpp, IRCXMPP_WARN, "control disconnected from XMPP."); - + LOG(bridge->ircxmpp, IRCXMPP_WARN, "bridge disconnected from XMPP."); } /* IRC */ @@ -395,12 +402,12 @@ static void event_nick_control ( } static void event_topic_control ( irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { - dump_event_control(s, e, o, p, c); /* o je avtor, p[0] je kanal, p[1] je nova tema/zadeva */ + dump_event_control(s, e, o, p, c); // o avtor, p[0] kanal, p[1] je nova tema struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); char buf[1024]; snprintf(buf, 1024, "/me je nastavil IRC temo na: %s", p[1]); bridge_forward(o, buf, ircxmpp, XMPP); -} /* TODO */ +} static void event_numeric ( irc_session_t * s, unsigned int e, const char * o, const char ** p, unsigned c) { char b[512]; @@ -693,10 +700,13 @@ void ircxmpp_set_domain (struct ircxmpp * ircxmpp, const char * domain) { ircxmpp->domain = strdup(domain); // this intentionally crashes } static void obdelaj_bridge (const void * nodep, VISIT which __attribute__((unused)), - int depth __attribute__((unused))) { + int depth __attribute__((unused))) { // to be called only from twalk struct bridge * bridge = *(struct bridge **) nodep; if (bridge->irc && irc_run_once(bridge)) { - free_bridge(bridge, "irc connection dropped"); + MR(bridge->ircxmpp->to_free_bridges); + MR(bridge->ircxmpp->to_free_reasons); + bridge->ircxmpp->to_free_bridges[bridge->ircxmpp->to_free_bridges_length++] = bridge; + bridge->ircxmpp->to_free_reasons[bridge->ircxmpp->to_free_reasons_length++] = "irc connection dropped"; return; } if (bridge->conn && !xmpp_conn_is_connected(bridge->conn) @@ -704,8 +714,12 @@ static void obdelaj_bridge (const void * nodep, VISIT which __attribute__((unuse if (bridge->side == IRC && bridge->messages_length) { LOG(bridge->ircxmpp, IRCXMPP_WARN, "RECONNECTING dead BRIDGE with msgs!"); xmpp_connect_client(bridge->conn, NULL, 0, conn_handler_bridge, bridge); - } else - free_bridge(bridge, "xmpp connection dropped"); + } else { + MR(bridge->ircxmpp->to_free_bridges); + MR(bridge->ircxmpp->to_free_reasons); + bridge->ircxmpp->to_free_bridges[bridge->ircxmpp->to_free_bridges_length++] = bridge; + bridge->ircxmpp->to_free_reasons[bridge->ircxmpp->to_free_reasons_length++] = "xmpp connection dropped"; + } return; } const char * jid = NULL; @@ -737,9 +751,9 @@ void ircxmpp_run_once (struct ircxmpp * ircxmpp) { if (!ircxmpp->ctx || !ircxmpp->conn || (!xmpp_conn_is_connected(ircxmpp->conn) && !xmpp_conn_is_connecting(ircxmpp->conn))) { LOG(ircxmpp, IRCXMPP_WARN, "XMPP control is DISCONNECTED! CONNECTING!"); - if (!ircxmpp->bridges) { // bridges contain ctx inside of their conns. - if (ircxmpp->conn) // maybe conn can be freed but i don't know - xmpp_conn_release(ircxmpp->conn); // bottom line: ctx really can't be + if (!ircxmpp->bridges) { // bridges contain ctx inside of their conns + if (ircxmpp->conn) + xmpp_conn_release(ircxmpp->conn); if (ircxmpp->ctx) xmpp_ctx_free(ircxmpp->ctx); ircxmpp->ctx = xmpp_ctx_new(NULL, &ircxmpp->xmpp_logger); @@ -757,9 +771,25 @@ void ircxmpp_run_once (struct ircxmpp * ircxmpp) { irc_destroy_session(ircxmpp->irc); init_irc_control(ircxmpp); } + ircxmpp->to_free_bridges_length = ircxmpp->to_free_reasons_length = 0; + ircxmpp->to_free_bridges_sizeof = ircxmpp->to_free_reasons_sizeof = ALLOC_CHUNK; + size_t req = sizeof(*ircxmpp->to_free_bridges)*ircxmpp->to_free_bridges_sizeof; + ircxmpp->to_free_bridges = malloc(req); + ircxmpp->to_free_reasons = malloc(req); + if (!ircxmpp->to_free_reasons || !ircxmpp->to_free_bridges) { + LOG(ircxmpp, IRCXMPP_ERROR, "malloc(%z) to_free failed!", req); + goto r; + } twalk(ircxmpp->bridges, obdelaj_bridge); + for (size_t i = 0; i < ircxmpp->to_free_bridges_length; i++) + free_bridge(ircxmpp->to_free_bridges[i], ircxmpp->to_free_reasons[i]); +r: + free(ircxmpp->to_free_bridges); + free(ircxmpp->to_free_reasons); + ircxmpp->to_free_bridges_length = ircxmpp->to_free_bridges_sizeof = ircxmpp->to_free_reasons_length = ircxmpp->to_free_reasons_sizeof = 0; } void ircxmpp_free (struct ircxmpp * ircxmpp) { + LOG(ircxmpp, IRCXMPP_DEBUG, "engaging the freeing routine"); free_bridges(&ircxmpp->bridges); xmpp_conn_release(ircxmpp->conn); xmpp_ctx_free(ircxmpp->ctx); @@ -797,8 +827,8 @@ int main (void) { size_t handles_length = 0; char * domain = NULL; // to know if we want to run dns server or not ircxmpp ** handles = NULL; - while (1) { // note that if input config is invalid we leak memory before exiting - char b[64]; // i don't free any allocated shit and just return, probably it's ok + while (1) { + char b[64]; if (handles_length++) sprintf(b, "IX_JID%zu", handles_length); else @@ -806,6 +836,7 @@ int main (void) { if (!getenv(b)) { if (handles_length == 1) { fprintf(stderr, USAGE "FAIL: at least one link is required!\n"); + free(handles); return 1; } handles_length--; @@ -820,16 +851,19 @@ int main (void) { sprintf(b, "IX_" config "%zu", handles_length); \ if (getenv(b)) \ ircxmpp_set_##value(handles[handles_length-1], function(getenv(b))); \ - else if (required) \ - fprintf(stderr, USAGE "FAIL: environment variable %s was expected to be set.\n", b) - PREPARE_HANDLE(jid, "JID", str2str, 1); - PREPARE_HANDLE(password, "PASS", str2str, 1); - PREPARE_HANDLE(hostname, "HOST", str2str, 1); - PREPARE_HANDLE(port, "PORT", atoi, 1); - PREPARE_HANDLE(channel, "CHANNEL", str2str, 1); - PREPARE_HANDLE(muc, "MUC", str2str, 1); - PREPARE_HANDLE(channel_password, "CHPASS", str2str, 0); - PREPARE_HANDLE(domain, "DOMAIN", str2str, 0); + else if (required) { \ + fprintf(stderr, USAGE "FAIL: environment variable %s was expected to be set.\n", b); \ + free(handles); \ + return 2; \ + } + PREPARE_HANDLE(jid, "JID", str2str, 1) + PREPARE_HANDLE(password, "PASS", str2str, 1) + PREPARE_HANDLE(hostname, "HOST", str2str, 1) + PREPARE_HANDLE(port, "PORT", atoi, 1) + PREPARE_HANDLE(channel, "CHANNEL", str2str, 1) + PREPARE_HANDLE(muc, "MUC", str2str, 1) + PREPARE_HANDLE(channel_password, "CHPASS", str2str, 0) + PREPARE_HANDLE(domain, "DOMAIN", str2str, 0) if (getenv(b)) domain = getenv(b); } @@ -1,3 +1,4 @@ +#define IX_S0(x) (x ? x : "") enum ircxmpp_loglevel { IRCXMPP_DEBUG, IRCXMPP_INFO, @@ -10,6 +11,18 @@ typedef void (* ircxmpp_domain_setter) (void *, const char *); #ifdef IX_LIB /* do not use functions until #endif in programs that use libircxmpp. */ #include <libircclient.h> /* do not use members of struct ircxmpp, use opaque ircxmpp type! */ #include <strophe.h> +#include <math.h> +#define ISA(type, name) type * name; size_t name##_sizeof; size_t name##_length +#define ALLOC_CHUNK 1 // \\ note: this ISA is array of type not array of type * +#define REALLOC_K 1.5 +#define BIGGER_ARRAY(name) do { \ + name = realloc(name, sizeof(name[0])*ceil(name##_sizeof*REALLOC_K)); \ + for (int BA = name##_sizeof; BA < ceil(name##_sizeof*REALLOC_K); BA++) \ + name[BA] = NULL; \ + name##_sizeof = ceil(name##_sizeof*REALLOC_K); \ + } while (0) +#define MR(n) if (n##_sizeof <= n##_length) /* make room */ \ + BIGGER_ARRAY(n) enum irc_numeric { /* numerics from rfc 1459 */ ERR_NOSUCHNICK = 401, ERR_NOSUCHSERVER, @@ -179,6 +192,8 @@ struct ircxmpp { ircxmpp_domain_setter domain_setter; void * domain_setter_userdata; char * domain; + ISA(struct bridge *, to_free_bridges); + ISA(char *, to_free_reasons); // static reason strings, do not free them }; static void send_xmpp_logs_to_me ( void * const, const xmpp_log_level_t, const char * const, const char * const); |