summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile2
-rw-r--r--README15
-rw-r--r--debian/changelog8
-rw-r--r--dns.c27
-rw-r--r--domain2name.c2
-rw-r--r--ircxmpp.c88
-rw-r--r--ircxmpp.h15
8 files changed, 123 insertions, 36 deletions
diff --git a/.gitignore b/.gitignore
index 5bd2f72..c33725b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ ircxmpp
*.so
*.o
*.out
+tmp/*
+*.orig
diff --git a/Makefile b/Makefile
index c4cc7f9..3622a3e 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README b/README
index cc15197..ac5aa4e 100644
--- a/README
+++ b/README
@@ -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
diff --git a/dns.c b/dns.c
index 8646339..8c6ec32 100644
--- a/dns.c
+++ b/dns.c
@@ -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;
diff --git a/ircxmpp.c b/ircxmpp.c
index e8d8e97..52e9355 100644
--- a/ircxmpp.c
+++ b/ircxmpp.c
@@ -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);
}
diff --git a/ircxmpp.h b/ircxmpp.h
index 38b6f16..19cf1c9 100644
--- a/ircxmpp.h
+++ b/ircxmpp.h
@@ -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);