diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | CHANGELOG | 18 | ||||
-rw-r--r-- | Makefile | 30 | ||||
-rw-r--r-- | README | 41 | ||||
l---------[-rw-r--r--] | debian/changelog | 12 | ||||
-rw-r--r-- | ircxmpp.c (renamed from main.c) | 415 | ||||
-rw-r--r-- | ircxmpp.conf | 10 | ||||
-rw-r--r-- | ircxmpp.h | 74 |
8 files changed, 382 insertions, 221 deletions
@@ -2,3 +2,6 @@ valgrind-out.txt core ircxmpp .gdb_history +*.so +*.o +*.out diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..bf42b0b --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,18 @@ +ircxmpp (0.0.3-1) stable; urgency=low + + * Fixed newline message smuggling from XMPP to IRC. + * Added support for multiple links. + + -- Anton Luka Šijanec <anton@sijanec.eu> Fri, 29 Apr 2022 17:00:00 +0200 + +ircxmpp (0.0.2-1) stable; urgency=low + + * Some fixes. + + -- Anton Luka Šijanec <anton@sijanec.eu> Wed, 27 Apr 2022 15:45:00 +0200 + +ircxmpp (0.0.1-1) stable; urgency=low + + * Initial release. + + -- Anton Luka Šijanec <anton@sijanec.eu> Wed, 27 Apr 2022 14:00:00 +0200 @@ -1,29 +1,31 @@ DESTDIR=/ CC = cc -cflags = -O0 -Wall -I. -Wformat-security -Wextra -pedantic -g $(shell pkg-config --cflags libstrophe) -SRCFILE = main.c -CFGFILE = ircxmpp.conf -CFGDEST = ircxmpp +cflags = -O0 -Wall -I. -fpic -Wformat-security -Wextra -pedantic -g3 $(shell pkg-config --cflags libstrophe) ldflags = $(shell pkg-config --libs libstrophe) -lircclient -BINFILE = ircxmpp +PROJ = ircxmpp # cflags and ldflags are used so that users specifying CFLAGS and LDFLAGS do not override my flags # += is not used, because gcc usually accepts last option, for example -O0 -O2 will use -O2 .NOTPARALLEL: default: - $(CC) $(cflags) $(CFLAGS) $(SRCFILE) $(ldflags) $(LDFLAGS) -o$(BINFILE) - + $(CC) -c -DIX_LIB $(cflags) $(CFLAGS) $(PROJ).c + $(CC) -shared -olib$(PROJ).so $(PROJ).o + $(CC) -L. $(cflags) $(CFLAGS) $(PROJ).c -l$(PROJ) $(ldflags) $(LDFLAGS) -o$(PROJ) install: - mkdir -p $(DESTDIR)/usr/bin $(DESTDIR)/etc - cp $(BINFILE) $(DESTDIR)/usr/bin/ - cp $(CFGFILE) $(DESTDIR)/etc/$(CFGDEST) + mkdir -p $(DESTDIR)/usr/bin $(DESTDIR)/etc $(DESTDIR)/usr/include $(DESTDIR)/usr/lib + cp $(PROJ) $(DESTDIR)/usr/bin/ + cp $(PROJ).conf $(DESTDIR)/etc/$(PROJ) + cp lib$(PROJ).so $(DESTDIR)/usr/lib/ + cp $(PROJ).h $(DESTDIR)/usr/include/ -distclean: - rm $(BINFILE) tmp -rf +distclean: clean clean: - rm $(BINFILE) tmp -rf + rm $(PROJ) $(PROJ).o lib$(PROJ).so + +uninstall: + rm $(DESTDIR)/usr/bin/$(PROJ) $(DESTDIR)/etc/$(PROJ) $(DESTDIR)/usr/include/$(PROJ).h $(DESTDIR)/usr/lib/lib$(PROJ).so valgrind: valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt $(COMMAND) -.PHONY: valgrind clean distclean install default +.PHONY: valgrind clean distclean install uninstall default @@ -48,19 +48,48 @@ To implement: - subject changing - automatic +v/+h/+o botnet juggling between bridge IRC connections and control IRC conn - setting user@host of bridge bots on IRC to JIDs of XMPP users by temporary changing rDNS + - reusing bridge and control connections with same nick to different channels on same network Notes: - when connecting to a channel with already joined nicks, they're not connected to XMPP until they send a message or change their nick. only after that are they bridged, and the sent message is not lost. implementing this would require parsing NAMES list, which only contains nicks and not usernames and hostnames, and would also impact performance, especially on MAM-enabled MUCs that send a lot of history on XMPP join for every connection. XMPP MUCs also tend to slow down with a large number of connections, whereas IRC channels don't. + - if possible, send core dumps of crashes to my email address anton@šijanec.eu. + - coredumps contain passwords and some chats, so make sure to change XMPP password before + - to capture core dumps of systemd services (when installed on debian), use systemd-coredump -Debian installation and systemd service: - - this program is packaged in my personal package archive on http://prog.sijanec.eu/ +Debian installation, systemd service and precompiled dynamically linked binaries: + - packages in my ppa on http://prog.sijanec.eu/ (amd64, arm64, armel, i386, src, dbgsym, lib) - after adding the archive and running apt update, install with apt install ircxmpp - edit the configuration file with environment variables in /etc/ircxmpp - run the service with service ircxmpp start, enable it at boot with systemctl enable ircxmpp + - ci/cd: http://jenkins.sijanec.eu/job/ircxmpp for amd64, arm64 and armel Security: - - Do not trust this program. - - If you find a (security) vulnerability or any issue, please email anton at šijanec.eu - - Whenever possible, run the program in a chroot jail as an unpriviledged user. Running with systemd (service ircxmpp start) does that. + - do not trust this program. + - if you find a (security) vulnerability or any issue, please email anton@šijanec.eu. + - whenever possible, run the program in a chroot jail as an unpriviledged user. Running with systemd (service ircxmpp start) does that. - -- Anton Luka Šijanec <anton@sijanec.eu> Wed, 27 Apr 2022 14:30:00 +0200 +Running multiple irc<->xmpp links at the same time: + - first link's environment variables are still in format IX_JID, IX_PASS, IX_HOST and so on + - second link's environment variables are in format IX_JID2, IX_PASS2, IX_HOST2 and so on + - this means you concatenate the config environment variable with the digit + - digits must be consecutive from 2 to infinity, vars without digit (1st link) are required + - IX_LOOPDELAY applies to all links, as there is no delay between individual links (no 2, 3) + +Compilation and manual install from source: + - git clone https://git.sijanec.eu/sijanec/ircxmpp + - cd ircxmpp + - make # tested compilers: gcc, clang and tcc. make -e CC=tcc, make -e CC=clang, ... + - make install # not required, but otherwise LD_LIBRARY_PATH=. ./ircxmpp for libircxmpp.so + +Using as a library: + - you may only use functions between #else and #endif in ircxmpp.h, handler struct is opaque + - after make install, ircxmpp.h and libircxmpp.so are placed in /usr/{include,lib} + - #include <ircxmpp.h> and link with -lircxmpp + - functions return nothing (except ircxmpp_init) and log to standard error + - do not call ircxmpp_set_*() functions after first call to ircxmpp_run_once() + - actual main function of the ircxmpp program is in ircxmpp.c between #else and #endif + +Gentoo/openrc?: + - http://github.com/OpenRC/openrc/pull/517 needs to be merged before for increased security + + -- Anton Luka Šijanec <anton@sijanec.eu> Fri, 29 Apr 2022 17:00:00 +0200 diff --git a/debian/changelog b/debian/changelog index ce4a19f..a535994 100644..120000 --- a/debian/changelog +++ b/debian/changelog @@ -1,11 +1 @@ -ircxmpp (0.0.2-1) stable; urgency=low - - * Some fixes. - - -- Anton Luka Šijanec <anton@sijanec.eu> Wed, 27 Apr 2021 15:45:00 +0200 - -ircxmpp (0.0.1-1) stable; urgency=low - - * Initial release. - - -- Anton Luka Šijanec <anton@sijanec.eu> Wed, 27 Apr 2021 14:00:00 +0200 +../CHANGELOG
\ No newline at end of file @@ -1,14 +1,26 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <strophe.h> #include <ctype.h> #include <time.h> #include <sys/select.h> #include <errno.h> #include <signal.h> +#include <stdarg.h> +#include <search.h> #include "ircxmpp.h" -void free_bridge (struct bridge ** bridge, const char * razlog) { +#ifdef IX_LIB +#define BUG(...) bug(__FILE__, __func__, __LINE__, __VA_ARGS__) +__attribute__((noreturn)) static void bug (const char * file, const char * function, int line, const char * format, ...) { + va_list ap; + va_start(ap, format); + fprintf(stderr, "BUG DETECTED in %s()@%s:%d REPORT TO anton@šijanec.eu and attach core.\n" + "additional description: ", file, function, line); + vfprintf(stderr, format, ap); + abort(); + va_end(ap); /* this is never called */ +} +static void free_bridge (struct bridge ** bridge, const char * razlog) { if (!bridge || !*bridge) return; fprintf(stderr, "freeing bridge with reason: %s\n", razlog); @@ -18,7 +30,7 @@ void free_bridge (struct bridge ** bridge, const char * razlog) { irc_destroy_session((*bridge)->irc); } if ((*bridge)->conn) - xmpp_conn_release((*bridge)->conn); /* graceful disconnect, what is that? */ + xmpp_conn_release((*bridge)->conn); // graceful disconnect, what is that? free((*bridge)->identifier); for (size_t i = 0; i < (*bridge)->messages_length; i++) free((*bridge)->messages[i]); @@ -28,26 +40,32 @@ void free_bridge (struct bridge ** bridge, const char * razlog) { struct bridge * tofree = *bridge; if ((*bridge)->prev) (*bridge)->prev->next = (*bridge)->next; - else + else { + if ((*bridge)->next) + (*bridge)->next->prev = (*bridge)->prev; *bridge = (*bridge)->next; + } free(tofree); } -void free_bridges (struct bridge ** bridges) { - while (*bridges) // enkrat bo *bridges NULL, ker ne bo nobenega več notri +static void free_bridges (struct bridge ** bridges) { + while (*bridges) { // enkrat bo *bridges NULL, ker ne bo nobenega več notri + if ((*bridges)->prev) + BUG("(*bridges)->prev is set"); free_bridge(bridges, "vsi mostovi se podirajo, ker se ircxmpp izklaplja"); + } } -struct bridge ** find_bridge (struct bridge ** bridges, const char * identifier, enum side side) { +static struct bridge ** find_bridge (struct bridge ** bridges, const char * id, enum side side) { s: if (!bridges || !*bridges) return NULL; - if ((*bridges)->side == side && !strcmp((*bridges)->identifier, identifier)) + if ((*bridges)->side == side && !strcmp((*bridges)->identifier, id)) return bridges; bridges = &((*bridges)->next); goto s; } -void jid2ircnick (char * jid) { /* edits a jid into an irc-compatible nick. libera trims nicks. */ +static void jid2ircnick (char * jid) { /* edits a jid into an irc nick. libera trims nicks. */ char * c = strchr(jid, '/'); if (c) { c++; @@ -60,7 +78,7 @@ void jid2ircnick (char * jid) { /* edits a jid into an irc-compatible nick. libe if (!(isalnum(*jid) || memchr(VALID_NICK, *jid, strlen(VALID_NICK)))) *jid = '_'; } -void jid2ircuser (char * jid) { +static void jid2ircuser (char * jid) { char * c = strchr(jid, '/'); if (c) { c++; @@ -74,12 +92,12 @@ void jid2ircuser (char * jid) { if (!(isalnum(*jid) || memchr(VALID_USER, *jid, strlen(VALID_USER)))) *jid = 'x'; } -void bridge_forward (const char * from, const char * msg, struct ircxmpp * ircxmpp, enum side side) { - struct bridge ** bridge_resp = find_bridge(&ircxmpp->bridges, from, !side); - if (strstr(from, "ircxmpp_") || (ircxmpp->irchost && strstr(from, ircxmpp->irchost))) +static void bridge_forward (const char * f, const char * m, struct ircxmpp * ircxmpp, enum side s) { + struct bridge ** bridge_resp = find_bridge(&ircxmpp->bridges, f, !s); + if (strstr(f, "ircxmpp_") || (ircxmpp->irchost && strstr(f, ircxmpp->irchost))) return; - fprintf(stderr, "sending text from %s to %s: %s\n", from, side == IRC ? "IRC" : "XMPP", - msg ? msg : "[join only]"); + fprintf(stderr, "sending text from %s to %s: %s\n", f, s == IRC ? "IRC" : "XMPP", + m ? m : "[join only]"); struct bridge * bridge; if (!bridge_resp) { bridge = calloc(1, sizeof(struct bridge)); @@ -87,10 +105,10 @@ void bridge_forward (const char * from, const char * msg, struct ircxmpp * ircxm ircxmpp->bridges->prev = bridge; bridge->next = ircxmpp->bridges; ircxmpp->bridges = bridge; - bridge->identifier = strdup(from); + bridge->identifier = strdup(f); bridge->ircxmpp = ircxmpp; - bridge->side = !side; - if (side == IRC) + bridge->side = !s; + if (s == IRC) init_irc(bridge); else { bridge->conn = xmpp_conn_new(bridge->ircxmpp->ctx); @@ -100,22 +118,30 @@ void bridge_forward (const char * from, const char * msg, struct ircxmpp * ircxm } } else bridge = *bridge_resp; - if (side == IRC) { + if (s == IRC) { irc_cmd_join(bridge->irc, ircxmpp->channel, ircxmpp->channel_password); irc_run_once(bridge); - if (msg) - irc_cmd_msg(bridge->irc, ircxmpp->channel, msg); + if (m) { + char b[1024]; + strncpy(b, m, 1023); + b[1023] = '\0'; + char * c = b; + while (*++c != '\0') + if (!(isprint(*c) || * (unsigned char *) c > 0x7F /* UTF-8 */)) + *c = ' '; /* to prevent newline message smuggling */ + irc_cmd_msg(bridge->irc, ircxmpp->channel, m); + } irc_run_once(bridge); - } else if (msg) { + } else if (m) { bridge->messages = realloc(bridge->messages, sizeof(*bridge->messages) * (bridge->messages_length+1)); - bridge->messages[bridge->messages_length++] = strdup(msg); + bridge->messages[bridge->messages_length++] = strdup(m); } -} /* msg can be NULL, in that case we only join. */ -int message_handler (xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { - if (!conn) /* just to get rid of -Wunused-parameter */ +} /* m can be NULL, in that case we only join. */ +static int message_handler (xmpp_conn_t * const c, xmpp_stanza_t * const stanza, void * const ud) { + if (!c) /* just to get rid of -Wunused-parameter */ return 1; - struct ircxmpp * ircxmpp = (struct ircxmpp *) userdata; + struct ircxmpp * ircxmpp = (struct ircxmpp *) ud; xmpp_stanza_t * body; const char * type; char * intext; @@ -135,9 +161,9 @@ int message_handler (xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, voi xmpp_free(ircxmpp->ctx, intext); return 1; } -int presence_handler (xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const ud) { +static int presence_handler (xmpp_conn_t * const c, xmpp_stanza_t * const stanza, void * const ud) { struct ircxmpp * ircxmpp = (struct ircxmpp *) ud; - if (!conn || !xmpp_stanza_get_from(stanza) || !strchr(xmpp_stanza_get_from(stanza), '/')) + if (!c || !xmpp_stanza_get_from(stanza) || !strchr(xmpp_stanza_get_from(stanza), '/')) return 1; if (xmpp_stanza_get_type(stanza) && !strcmp("unavailable", xmpp_stanza_get_type(stanza))) { fprintf(stderr, "sogovornik %s je zapustil MUC\n", xmpp_stanza_get_from(stanza)); @@ -151,8 +177,8 @@ int presence_handler (xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, vo return 1; } -void conn_handler (xmpp_conn_t * const conn, const xmpp_conn_event_t status, const int error, - xmpp_stream_error_t * const stream_error, void * const userdata) { +static void conn_handler (xmpp_conn_t * const conn, const xmpp_conn_event_t status, + const int error, xmpp_stream_error_t * const stream_error, void * const userdata) { struct ircxmpp * ircxmpp = (struct ircxmpp *) userdata; if (stream_error) /* just for -Wunused-parameter */ fprintf(stderr, "stream_error in conn_handler, error = %d\n", error); @@ -181,8 +207,8 @@ void conn_handler (xmpp_conn_t * const conn, const xmpp_conn_event_t status, con // xmpp_stop(ircxmpp->ctx); } } -void conn_handler_bridge (xmpp_conn_t * const conn, const xmpp_conn_event_t status, const int error, - xmpp_stream_error_t * const stream_error, void * const userdata) { +static void conn_handler_bridge (xmpp_conn_t * const conn, const xmpp_conn_event_t status, + const int error, xmpp_stream_error_t * const stream_error, void * const userdata) { struct bridge * bridge = (struct bridge *) userdata; if (stream_error) /* just for -Wunused-parameter */ fprintf(stderr, "stream_error in conn_handler, error = %d\n", error); @@ -202,27 +228,6 @@ void conn_handler_bridge (xmpp_conn_t * const conn, const xmpp_conn_event_t stat xmpp_stanza_release(x); xmpp_send(bridge->conn, pres); xmpp_stanza_release(pres); - /* xmpp_stanza_t * pres; - fprintf(stderr, "DEBUG: connected.\n"); - xmpp_handler_add(conn, message_handler, NULL, "message", NULL, bridge); - xmpp_handler_add(conn, presence_handler, NULL, "presence", NULL, bridge); - // Send initial <presence/> so that we appear online to contacts - // pres = xmpp_presence_new(ctx); // somehow send can only be called once? - // xmpp_send(conn, pres); - // xmpp_stanza_release(pres); - pres = xmpp_presence_new(bridge->ircxmpp->ctx); // joining a MUC - char b[512]; - bridge->identifier(); - snprintf(b, 512, "%s/ircxmpp_%x", ircxmpp->muc, rand()); - xmpp_stanza_set_to(pres, b); - xmpp_stanza_t * x = xmpp_stanza_new(ircxmpp->ctx); - xmpp_stanza_set_name(x, "x"); - xmpp_stanza_set_ns(x, "http://jabber.org/protocol/muc"); - xmpp_stanza_add_child(pres, x); - xmpp_stanza_release(x); - xmpp_send(conn, pres); - xmpp_stanza_release(pres); - */ } else { fprintf(stderr, "DEBUG: disconnected\n"); // xmpp_stop(ircxmpp->ctx); @@ -231,7 +236,8 @@ void conn_handler_bridge (xmpp_conn_t * const conn, const xmpp_conn_event_t stat } /* IRC */ -void dump_event (irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { +static void dump_event ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { if (!s) /* just for -Wunused-parameter */ return; fprintf(stderr, "Event %s, origin %s, params %d [", e, o ? o : "NULL", c); @@ -242,40 +248,42 @@ void dump_event (irc_session_t * s, const char * e, const char * o, const char * } fprintf(stderr, "]\n"); } -void event_connect (irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { +static void event_connect ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); struct bridge * bridge = (struct bridge *) irc_get_ctx(s); irc_cmd_join(s, bridge->ircxmpp->channel, bridge->ircxmpp->channel_password); } -void event_connect_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_connect_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); irc_cmd_join(s, ircxmpp->channel, ircxmpp->channel_password); } -void event_privmsg (irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { +static void event_privmsg ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); /* SAME FOR _control. do NOT use irc_get_ctx here!!! */ char nickbuf[512]; irc_target_get_nick(o, nickbuf, sizeof(nickbuf)); irc_cmd_msg(s, nickbuf, "ircxmpp (še) ne podpira zasebnih sporočil xmpp uporabnikom. ircxmpp se razvija na http://git.sijanec.eu/sijanec/ircxmpp/."); } -void event_partquit_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_partquit_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); struct bridge ** bridge = find_bridge(&ircxmpp->bridges, o /* indeed n!u@h */, IRC); free_bridge(bridge, "part or quit from irc"); } /* part and quit events */ -void event_channel_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_channel_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); /* o je avtor, p[0] je kanal p[1] je besedilo */ if (c != 2) /* no message text */ return; struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); bridge_forward(o /* indeed n!u@h */, p[1], ircxmpp, XMPP); } -void event_join_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_join_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); char buf[512]; @@ -298,8 +306,8 @@ void event_join_control (irc_session_t * s, const char * e, const char * o, cons } bridge_forward(o /* indeed n!u@h */, NULL, ircxmpp, XMPP); } -void event_nick_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_nick_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); /* o je originalen nick, p[0] je nov nick */ if (!c) return; @@ -313,15 +321,16 @@ void event_nick_control (irc_session_t * s, const char * e, const char * o, cons : "neznan uporabnik@neznan strežnik"); bridge_forward(buf, NULL, ircxmpp, XMPP); /* and now connect */ } -void event_topic_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_topic_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); /* o je avtor, p[0] je kanal, p[1] je nova tema/zadeva */ 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 */ -void event_numeric (irc_session_t * s, unsigned int e, const char * o, const char ** p, unsigned c) { +static void event_numeric ( + irc_session_t * s, unsigned int e, const char * o, const char ** p, unsigned c) { char b[512]; struct bridge * bridge = (struct bridge *) irc_get_ctx(s); sprintf(b, "%d", e); @@ -342,8 +351,8 @@ void event_numeric (irc_session_t * s, unsigned int e, const char * o, const cha break; } } -void event_numeric_control (irc_session_t * s, unsigned int e, const char * o, const char ** p, - unsigned c) { +static void event_numeric_control ( + irc_session_t * s, unsigned int e, const char * o, const char ** p, unsigned c) { char b[512]; struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); sprintf(b, "%d", e); @@ -360,7 +369,7 @@ void event_numeric_control (irc_session_t * s, unsigned int e, const char * o, c break; } } -int irc_run_once (struct bridge * bridge) { /* returns nonzero if connection failed (dropped brid) */ +static int irc_run_once (struct bridge * bridge) { /* returns nonzero if connection failed */ if (!irc_is_connected(bridge->irc)) { char b[512], u[512]; strncpy(b, bridge->identifier, sizeof(b)-1); @@ -400,7 +409,9 @@ int irc_run_once (struct bridge * bridge) { /* returns nonzero if connection fai } return 0; } -int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if failed (dropped conne) */ +static int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if failed */ + if (!ircxmpp->irc) + return 1; if (!irc_is_connected(ircxmpp->irc)) { char b[512]; sprintf(b, "ircxmpp_%X", rand()); @@ -408,11 +419,12 @@ int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if fail free(ircxmpp->ircnick); ircxmpp->ircnick = strdup(b); if (irc_connect(ircxmpp->irc, ircxmpp->hostname, ircxmpp->port, - NULL, b, "ircxmpp", "http://git.sijanec.eu/sijanec/ircxmpp")) { + NULL, b, "ircxmpp", "http git.sijanec.eu/sijanec/ircxmpp")) { fprintf(stderr, "could not connect: %s\n", irc_strerror(irc_errno(ircxmpp->irc))); - return 1; + return 2; } + return 0; } struct timeval tv; fd_set in_set, out_set; @@ -425,13 +437,13 @@ int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if fail if (select(maxfd+1, &in_set, &out_set, NULL, &tv) < 0) { if (errno == EINTR) return 0; - return 1; + return 3; } if (irc_process_select_descriptors(ircxmpp->irc, &in_set, &out_set)) - return 1; + return 4; return 0; } -void init_irc (struct bridge * bridge) { +static void init_irc (struct bridge * bridge) { irc_callbacks_t callbacks; irc_session_t * s; memset(&callbacks, 0, sizeof(callbacks)); @@ -465,7 +477,7 @@ void init_irc (struct bridge * bridge) { irc_run_once(bridge); return; } -void init_irc_control (struct ircxmpp * ircxmpp) { +static void init_irc_control (struct ircxmpp * ircxmpp) { irc_callbacks_t callbacks; irc_session_t * s; memset(&callbacks, 0, sizeof(callbacks)); @@ -499,92 +511,172 @@ void init_irc_control (struct ircxmpp * ircxmpp) { irc_run_once_control(ircxmpp); return; } -/* /IRC */ /* irc_is_connected(irc_session_t * session): 1 ali 0 */ /* TODO: if nick is used */ +/* /IRC */ /* irc_is_connected(irc_session_t * session): 1 ali 0 */ +struct ircxmpp * ircxmpp_init (void) { + xmpp_initialize(); + return calloc(1, sizeof(struct ircxmpp)); +} +void ircxmpp_set_jid (struct ircxmpp * ircxmpp, const char * jid) { + free(ircxmpp->jid); + ircxmpp->jid = strdup(jid); +} +void ircxmpp_set_password (struct ircxmpp * ircxmpp, const char * password) { + free(ircxmpp->password); + ircxmpp->password = strdup(password); +} +void ircxmpp_set_hostname (struct ircxmpp * ircxmpp, const char * hostname) { + free(ircxmpp->hostname); + ircxmpp->hostname = strdup(hostname); +} +void ircxmpp_set_port (struct ircxmpp * ircxmpp, unsigned short int port) { + ircxmpp->port = port; +} +void ircxmpp_set_channel (struct ircxmpp * ircxmpp, const char * channel) { + free(ircxmpp->channel); + ircxmpp->channel = strdup(channel); +} +void ircxmpp_set_muc (struct ircxmpp * ircxmpp, const char * muc) { + free(ircxmpp->muc); + ircxmpp->muc = strdup(muc); +} +void ircxmpp_set_channel_password (struct ircxmpp * ircxmpp, const char * channel_password) { + free(ircxmpp->channel_password); + ircxmpp->channel_password = strdup(channel_password); +} +void ircxmpp_run_once (struct ircxmpp * ircxmpp) { + if (!ircxmpp->ctx || !ircxmpp->conn || (!xmpp_conn_is_connected(ircxmpp->conn) + && !xmpp_conn_is_connecting(ircxmpp->conn))) { + fprintf(stderr, "XMPP control is DISCONNECTED! CONNECTING!\n"); + if (ircxmpp->conn) + xmpp_conn_release(ircxmpp->conn); + if (ircxmpp->ctx) + xmpp_ctx_free(ircxmpp->ctx); + ircxmpp->ctx = xmpp_ctx_new(NULL, xmpp_get_default_logger(XMPP_LEVEL_DEBUG)); + ircxmpp->conn = xmpp_conn_new(ircxmpp->ctx); + xmpp_conn_set_jid(ircxmpp->conn, ircxmpp->jid); + xmpp_conn_set_pass(ircxmpp->conn, ircxmpp->password); + xmpp_connect_client(ircxmpp->conn, NULL, 0, conn_handler, ircxmpp); + } + xmpp_run_once(ircxmpp->ctx, 1); /* no need to run for bridges, as they share the ctx */ + int ret = 0; + if ((ret = irc_run_once_control(ircxmpp))) { + fprintf(stderr, "IRC control is DISCONNECTED with code %d. CONNECTING!\n", ret); + if (ircxmpp->irc) + irc_destroy_session(ircxmpp->irc); + init_irc_control(ircxmpp); + } + struct bridge ** bridge = &ircxmpp->bridges; + while (bridge && *bridge) { + struct bridge ** next = NULL; + if ((*bridge)->next) + next = &((*bridge)->next); + if ((*bridge)->irc && irc_run_once(*bridge)) { + free_bridge(bridge, "irc connection dropped"); + goto cont; + } + if ((*bridge)->conn && !xmpp_conn_is_connected((*bridge)->conn) + && !xmpp_conn_is_connecting(((*bridge)->conn))) { + if ((*bridge)->side == IRC && (*bridge)->messages_length) { + fprintf(stderr, "RECONNECTING BRIDGE BECAUSE IT DIED and has msgs!"); + xmpp_connect_client((*bridge)->conn, NULL, 0, + conn_handler_bridge, *bridge); + } else + free_bridge(bridge, "xmpp connection dropped"); + goto cont; + } + while ((*bridge)->conn && xmpp_conn_is_connected((*bridge)->conn) + && (*bridge)->messages_length + && xmpp_conn_get_bound_jid((*bridge)->conn)) { + char id[64]; + fprintf(stderr, "sending xmpp msg from %s\n", (*bridge)->identifier); + sprintf(id, "ircxmpp-%x", rand()); + xmpp_stanza_t * stan = xmpp_message_new(ircxmpp->ctx, + "groupchat", ircxmpp->muc, id); + xmpp_message_set_body(stan, + (*bridge)->messages[(*bridge)->messages_length-1]); + xmpp_stanza_set_from(stan, xmpp_conn_get_bound_jid((*bridge)->conn)); + xmpp_send((*bridge)->conn, stan); + xmpp_stanza_release(stan); + free((*bridge)->messages[--(*bridge)->messages_length]); + } +cont: + bridge = next; + } +} +void ircxmpp_free (struct ircxmpp * ircxmpp) { + free_bridges(&ircxmpp->bridges); + xmpp_conn_release(ircxmpp->conn); + xmpp_ctx_free(ircxmpp->ctx); + xmpp_shutdown(); + irc_cmd_quit(ircxmpp->irc, "vsesplošni izklop programa"); + irc_run_once_control(ircxmpp); // verjetno je to potrebno, da pošlje quit + irc_destroy_session(ircxmpp->irc); + free(ircxmpp->ircnick); + free(ircxmpp->irchost); + free(ircxmpp->jid); + free(ircxmpp->password); + free(ircxmpp->hostname); + free(ircxmpp->channel); + free(ircxmpp->muc); + free(ircxmpp->channel_password); + free(ircxmpp); +} +#else int shouldexit = 0; void signalhandler (int s) { shouldexit += s+1; /* only for -Wunused-parameter */ } +const char * str2str (const char * str) { + return str; +} int main (void) { srand(time(NULL)); - struct ircxmpp ircxmpp; - memset(&ircxmpp, '\0', sizeof(ircxmpp)); - xmpp_log_t *log; - if (!getenv("IX_JID") || !getenv("IX_PASS") || !getenv("IX_HOST") || !getenv("IX_PORT") - || !getenv("IX_CHANNEL") || !getenv("IX_MUC")) { - fprintf(stderr, "Usage: IX_JID=jid@xmpp.server IX_PASS=pass IX_HOST=irc.server " - "IX_PORT=6666 IX_CHANNEL=#channel IX_MUC=muc@xmpp.srv ircxmpp\n"); - return 1; +#define USAGE " *** ircxmpp is developed at http://git.sijanec.eu/sijanec/ircxmpp ***\n" \ + "Usage: IX_JID=jid@xmpp.server IX_PASS=pass IX_HOST=irc.server IX_PORT=6666 " \ + "IX_CHANNEL=#channel IX_MUC=muc@xmpp.srv ircxmpp\n" \ + "multiple links can be specified by appending a consecutive number, starting with " \ + "2, to every environment variable. first link is IX_*, second is IX_*2, and so on.\n" + size_t handles_length = 0; + 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 + if (handles_length++) + sprintf(b, "IX_JID%d", handles_length); + else + strcpy(b, "IX_JID"); + if (!getenv(b)) { + if (handles_length == 1) { + fprintf(stderr, USAGE "FAIL: at least one link is required!\n"); + return 1; + } + handles_length--; + break; + } + handles = realloc(handles, sizeof(ircxmpp *)*handles_length); + handles[handles_length-1] = ircxmpp_init(); +#define PREPARE_HANDLE(value, config, function, required) \ + if (handles_length == 1) \ + sprintf(b, "IX_" config); \ + else \ + sprintf(b, "IX_" config "%d", 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); } - ircxmpp.jid = getenv("IX_JID"); - ircxmpp.password = getenv("IX_PASS"); - ircxmpp.hostname = getenv("IX_HOST"); - ircxmpp.port = atoi(getenv("IX_PORT")); - ircxmpp.channel = getenv("IX_CHANNEL"); - ircxmpp.muc = getenv("IX_MUC"); - ircxmpp.channel_password = getenv("IX_CHPASS"); - xmpp_initialize(); - log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); - init_irc_control(&ircxmpp); -c: - ircxmpp.ctx = xmpp_ctx_new(NULL, log); - ircxmpp.conn = xmpp_conn_new(ircxmpp.ctx); - xmpp_conn_set_jid(ircxmpp.conn, ircxmpp.jid); - xmpp_conn_set_pass(ircxmpp.conn, ircxmpp.password); - xmpp_connect_client(ircxmpp.conn, NULL, 0, conn_handler, &ircxmpp); signal(SIGTERM, signalhandler); signal(SIGINT, signalhandler); // signal(SIGPIPE, SIG_IGN); while (!shouldexit) { /* TEH EVENT LOOP */ - xmpp_conn_is_disconnected(ircxmpp.conn); - xmpp_run_once(ircxmpp.ctx, 1); - struct bridge ** bridge = &ircxmpp.bridges; - while (*bridge) { - struct bridge ** next = &((*bridge)->next); - if ((*bridge)->irc && irc_run_once(*bridge)) { - free_bridge(bridge, "irc connection dropped"); - goto cont; - } - if ((*bridge)->conn) - xmpp_run_once(xmpp_conn_get_context((*bridge)->conn), 1); - if ((*bridge)->conn && !xmpp_conn_is_connected((*bridge)->conn) - && !xmpp_conn_is_connecting(((*bridge)->conn))) { - if ((*bridge)->side == IRC) { - fprintf(stderr, "RECONNECTING BRIDGE BECAUSE IT DIED!"); - xmpp_connect_client((*bridge)->conn, NULL, 0, - conn_handler_bridge, *bridge); - } else - free_bridge(bridge, "xmpp connection dropped"); - goto cont; - } - while ((*bridge)->conn && xmpp_conn_is_connected((*bridge)->conn) - && (*bridge)->messages_length - && xmpp_conn_get_bound_jid((*bridge)->conn)) { - char id[64]; - fprintf(stderr, "sending xmpp msg from %s\n", (*bridge)->identifier); - sprintf(id, "ircxmpp-%x", rand()); - xmpp_stanza_t * stan = xmpp_message_new((*bridge)->ircxmpp->ctx, - "groupchat", (*bridge)->ircxmpp->muc, id); - xmpp_message_set_body(stan, - (*bridge)->messages[(*bridge)->messages_length-1]); - xmpp_stanza_set_from(stan, xmpp_conn_get_bound_jid((*bridge)->conn)); - xmpp_send((*bridge)->conn, stan); - xmpp_stanza_release(stan); - free((*bridge)->messages[--(*bridge)->messages_length]); - } -cont: - bridge = next; - } - if (!xmpp_conn_is_connected(ircxmpp.conn) && !xmpp_conn_is_connecting(ircxmpp.conn)){ - fprintf(stderr, "XMPP control DISCONNECTED! RECONNECTING!\n"); - xmpp_conn_release(ircxmpp.conn); - xmpp_ctx_free(ircxmpp.ctx); - goto c; - } - if (irc_run_once_control(&ircxmpp)) { - fprintf(stderr, "IRC control DISCONNECTED! RECONNECTING!\n"); - irc_destroy_session(ircxmpp.irc); - init_irc_control(&ircxmpp); - } + for (size_t i = 0; i < handles_length; i++) + ircxmpp_run_once(handles[i]); struct timespec ts = { .tv_sec = 0, .tv_nsec = getenv("IX_LOOPDELAY") ? atoi(getenv("IX_LOOPDELAY"))/1000 : 1e8 @@ -592,14 +684,9 @@ cont: nanosleep(&ts, NULL); } fprintf(stderr, "signal received, cleaning up!\n"); - free_bridges(&ircxmpp.bridges); - xmpp_conn_release(ircxmpp.conn); - xmpp_ctx_free(ircxmpp.ctx); - xmpp_shutdown(); - irc_cmd_quit(ircxmpp.irc, "vsesplošni izklop programa"); - irc_run_once_control(&ircxmpp); // verjetno je to potrebno, da pošlje quit - irc_destroy_session(ircxmpp.irc); - free(ircxmpp.ircnick); - free(ircxmpp.irchost); + for (size_t i = 0; i < handles_length; i++) + ircxmpp_free(handles[i]); + free(handles); return 0; } +#endif diff --git a/ircxmpp.conf b/ircxmpp.conf index fd635c7..ae11eef 100644 --- a/ircxmpp.conf +++ b/ircxmpp.conf @@ -19,3 +19,13 @@ # IX_CHPASS=somepassword ## delay after each event loop cycle in microseconds, defaults to 100ms. # IX_LOOPDELAY=100000 +####################### ANOTHER LINK SETUP ######################## +## As many links as you'd like can be setup. Append a number, starting with 2, to every setting. +# IX_JID2=v@lu.e +# IX_PASS2=value +# IX_HOST2=valu.e +# IX_PORT2=6666 +# IX_CHANNEL2=#value +# IX_MUC2=v@l.u.e +# IX_CHPASS2=value +# IX_LOOPDELAY2 does not exist, as the event loop is shared between all links. @@ -1,4 +1,6 @@ -#include <libircclient.h> +#ifdef IX_LIB /* do not use functions until #else in programs that use libircxmpp. */ +#include <libircclient.h> /* do not use members of struct ircxmpp, use opaque ircxmpp type! */ +#include <strophe.h> enum irc_numeric { /* numerics from rfc 1459 */ ERR_NOSUCHNICK = 401, ERR_NOSUCHSERVER, @@ -165,32 +167,52 @@ struct ircxmpp { char * muc; char * channel_password; }; -void free_bridge (struct bridge **, const char *); -void free_bridges (struct bridge **); -struct bridge ** find_bridge (struct bridge **, const char *, enum side); -void jid2ircnick (char *); -void jid2ircuser (char *); -void bridge_forward (const char *, const char *, struct ircxmpp *, enum side); -int message_handler (xmpp_conn_t * const, xmpp_stanza_t * const, void * const); -int presence_handler (xmpp_conn_t * const, xmpp_stanza_t * const, void * const); -void conn_handler (xmpp_conn_t * const, const xmpp_conn_event_t, const int, +static void free_bridge (struct bridge **, const char *); +static void free_bridges (struct bridge **); +static struct bridge ** find_bridge (struct bridge **, const char *, enum side); +static void jid2ircnick (char *); +static void jid2ircuser (char *); +static void bridge_forward (const char *, const char *, struct ircxmpp *, enum side); +static int message_handler (xmpp_conn_t * const, xmpp_stanza_t * const, void * const); +static int presence_handler (xmpp_conn_t * const, xmpp_stanza_t * const, void * const); +static void conn_handler (xmpp_conn_t * const, const xmpp_conn_event_t, const int, xmpp_stream_error_t * const, void * const); -void conn_handler_bridge (xmpp_conn_t * const, const xmpp_conn_event_t, const int, +static void conn_handler_bridge (xmpp_conn_t * const, const xmpp_conn_event_t, const int, xmpp_stream_error_t * const, void * const); // IRC -void dump_event (irc_session_t *, const char *, const char *, const char **, unsigned); -void event_connect (irc_session_t *, const char *, const char *, const char **, unsigned); -void event_connect_control (irc_session_t *, const char *, const char *, const char **, unsigned); -void event_privmsg (irc_session_t *, const char *, const char *, const char **, unsigned); -void event_partquit_control (irc_session_t *, const char *, const char *, const char **, unsigned); -void event_join_control (irc_session_t *, const char *, const char *, const char **, unsigned); -void event_channel_control (irc_session_t *, const char *, const char *, const char **, unsigned); -void event_nick_control(irc_session_t *, const char *, const char *, const char **, unsigned); -void event_topic_control (irc_session_t *, const char *, const char *, const char **, unsigned); -void event_numeric (irc_session_t *, unsigned int, const char *, const char **, unsigned); -void event_numeric_control (irc_session_t *, unsigned int, const char *, const char **, unsigned); -int irc_run_once (struct bridge *); -void init_irc (struct bridge *); -void init_irc_control (struct ircxmpp *); +static void dump_event (irc_session_t *, const char *, const char *, const char **, unsigned); +static void event_connect (irc_session_t *, const char *, const char *, const char **, unsigned); +static void event_connect_control ( + irc_session_t *, const char *, const char *, const char **, unsigned); +static void event_privmsg (irc_session_t *, const char *, const char *, const char **, unsigned); +static void event_partquit_control ( + irc_session_t *, const char *, const char *, const char **, unsigned); +static void event_join_control ( + irc_session_t *, const char *, const char *, const char **, unsigned); +static void event_channel_control ( + irc_session_t *, const char *, const char *, const char **, unsigned); +static void event_nick_control(irc_session_t *, const char *, const char *, const char **, unsigned); +static void event_topic_control ( + irc_session_t *, const char *, const char *, const char **, unsigned); +static void event_numeric ( + irc_session_t *, unsigned int, const char *, const char **, unsigned); +static void event_numeric_control ( + irc_session_t *, unsigned int, const char *, const char **, unsigned); +static int irc_run_once (struct bridge *); +static void init_irc (struct bridge *); +static void init_irc_control (struct ircxmpp *); // /IRC -int main (void); +#endif // IX_LIB +// ZUNANJE +typedef struct ircxmpp ircxmpp; /* opaque handle */ +ircxmpp * ircxmpp_init (void); +void ircxmpp_set_jid (ircxmpp *, const char *); +void ircxmpp_set_password (ircxmpp *, const char *); +void ircxmpp_set_hostname (ircxmpp *, const char *); +void ircxmpp_set_port (ircxmpp *, unsigned short int); +void ircxmpp_set_channel (ircxmpp *, const char *); +void ircxmpp_set_muc (ircxmpp *, const char *); +void ircxmpp_set_channel_password (ircxmpp *, const char *); +void ircxmpp_run_once (struct ircxmpp *); +void ircxmpp_free (struct ircxmpp *); +// /ZUNANJE |