summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--CHANGELOG6
-rw-r--r--README36
l---------debian/changelog1
-rw-r--r--debian/ircxmpp.service2
-rw-r--r--dns.c244
-rw-r--r--domain2name.c135
-rw-r--r--ircxmpp.c63
-rw-r--r--ircxmpp.conf13
-rw-r--r--ircxmpp.h17
9 files changed, 501 insertions, 16 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 5c399c0..d1e884b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,9 @@
+ircxmpp (0.0.6-1) stable; urgency=low
+
+ * implemented a built-in DNS server for rDNS PTR spoofing of IRC bridges
+
+ -- Anton Luka Šijanec <anton@sijanec.eu> Mon, 2 May 2022 00:45:00 +0200
+
ircxmpp (0.0.5-1) stable; urgency=low
* Decreased default event loop delay to 10 miliseconds.
diff --git a/README b/README
index c67fc25..cc15197 100644
--- a/README
+++ b/README
@@ -18,7 +18,7 @@ Configure the IRC channel or server if possible:
- use a bot to set XMPP users to have +o, +v or +h to prevent flood kicks
- disable invite-only mode on channel. you may use IRC channel passwords.
-Required environment variables for configuration:
+Required environment variables for configuration of a bridge:
- IX_JID JID of ircxmpp user on XMPP server to connect with
- IX_PASS password for XMPP authentication of ircxmpp user
- IX_HOST hostname of the IRC server, prefix the value with # to connect with TLS
@@ -26,9 +26,14 @@ Required environment variables for configuration:
- IX_CHANNEL channel on IRC server to bridge
- IX_MUC multi-user chat on XMPP server to bridge
-Optional environment variables for configuration:
+Optional environment variables for configuration of a bridge:
- IX_CHPASS set to IRC channel password if channel on IRC is password protected
+ - IX_DOMAIN start the built in DNS server and spoof IRC hostnames (more info below)
+
+Optional environment variables for global configuration: (those do not have numbered counterparts)
- IX_LOOPDELAY delay after each event loop cycle in microseconds, defaults to 10 ms.
+ - IX_DNS_PORT port on which the DNS server should run, if it's enabled, by default 53
+ - IX_DNS_IP IP on which to listen for DNS queries. by default this is INADDR_ANY-0.0.0.0
Operation principle:
- ircxmpp initiates two control connections, one to XMPP server, one to IRC server, and joins
@@ -47,7 +52,6 @@ To implement:
- ctcp messages (ACTION - /me) and perhaps file upload (that'd be hard)
- 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:
@@ -93,4 +97,30 @@ Using as a library:
Gentoo/openrc?:
- http://github.com/OpenRC/openrc/pull/517 needs to be merged before for increased security
+Built-in DNS server for spoofing IRC hostnames:
+ - IRC hostnames can be spoofed so that they look like the XMPP JID domain of the XMPP user
+ - you need to run the program with CAP_NET_BIND_SERVICE, on debian systemd this is by default
+ - you need a domain name, on which you set a wildcard record to A record to server's IP
+ + for example *.ircxmpp.example. IN A 192.168.0.2 (this is for IRC PTR verification)
+ - in the in-addr.arpa zone for you IP address, add a NS record pointing to your server
+ + for example 2.0.168.193.in-addr.arpa. IN NS server.ircxmpp.example.
+ - start ircxmpp with the configuration variable IX_DOMAIN=ircxmpp.example.
+ - read the logs! the built in DNS server might spam bind: permission denied errors
+ - when a bridge will connect to IRC, the IRC server will query the ircxmpp NS for PTR
+ - when a request is received by the ircxmpp NS, last XMPP user's JID hostname will be sent
+ + let's say xmpp.server.de.ircxmpp.example; ircxmpp.example appended for verification
+ - IRC server will then verify that this domain really points to your IP address-that wildcard
+
+Built-in DNS server for spoofing IRC hostnames when using the program as a library:
+ - call ircxmpp_set_domain with your domain suffix to which JID host and dot will be prepended
+ - you can set your callback with ircxmpp_set_set_domain_setter
+ - callback is called when domain change is wanted even if ircxmpp_set_dns is false
+ - with this you can differently publish the domain name for spoofing, perhaps with nsupdate
+ - otherwise if ircxmpp_set_dns is true, internal DNS server works as described above
+
+IPv6:
+ - Haha, remember this is C you're talking about! That'd require writing code!
+ - 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
diff --git a/debian/changelog b/debian/changelog
deleted file mode 120000
index a535994..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1 +0,0 @@
-../CHANGELOG \ No newline at end of file
diff --git a/debian/ircxmpp.service b/debian/ircxmpp.service
index 18eb171..6c05701 100644
--- a/debian/ircxmpp.service
+++ b/debian/ircxmpp.service
@@ -10,6 +10,8 @@ Type=simple
DynamicUser=yes
ExecStart=/usr/bin/ircxmpp
Restart=no
+CapabilityBoundingSet=CAP_NET_BIND_SERVICE
+AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
diff --git a/dns.c b/dns.c
new file mode 100644
index 0000000..97ea778
--- /dev/null
+++ b/dns.c
@@ -0,0 +1,244 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <unistd.h>
+#include "domain2name.c" // this is from http://git.sijanec.eu/sijanec/dnsfind
+enum type {
+ A = 1,
+ Ns,
+ Md,
+ Mf,
+ Cname,
+ Soa,
+ Mb,
+ M,
+ Mr,
+ Null,
+ Wks,
+ Ptr,
+ Hinfo,
+ Minfo,
+ Mx,
+ Txt
+};
+enum class {
+ In = 1,
+ Cs,
+ Ch,
+ He,
+ Any = 255
+};
+#define RESPONSE (1 << 15)
+#define QUESTION (0 << 15)
+#define QUERY (0 << 11)
+#define IQUERY (1 << 11)
+#define STATUS (1 << 12)
+#define AA (1 << 10)
+#define TC (1 << 9)
+#define RD (1 << 8)
+#define RA (1 << 7)
+#define SUCCESS (0 << 0)
+#define FORMAT_ERROR (1 << 0)
+#define SERVFAIL (1 << 1)
+#define NXDOMAIN (FORMAT_ERROR | SERVFAIL)
+#define NI (1 << 2)
+#define FORBIDDEN (NXDOMAIN | FORMAT_ERROR)
+struct header {
+ uint16_t xid __attribute__((packed));
+ uint16_t flags __attribute__((packed));
+ uint16_t qdcount __attribute__((packed));
+ uint16_t ancount __attribute__((packed));
+ uint16_t nscount __attribute__((packed));
+ uint16_t arcount __attribute__((packed));
+ char data[]; /* ignored for char[] */
+} __attribute__((packed));
+struct rr { /* name is omitted, first byte of struct is first byte of type */
+ uint16_t type __attribute__((packed));
+ uint16_t class __attribute__((packed));
+ uint32_t ttl __attribute__((packed));
+ uint16_t len __attribute__((packed));
+ char data[]; /* ignored for char[] */
+} __attribute__((packed));
+struct question {
+ uint16_t type __attribute__((packed));
+ uint16_t class __attribute__((packed));
+} __attribute__((packed));
+enum dns_loglevel {
+ DNS_DEBUG,
+ DNS_INFO,
+ DNS_WARN,
+ DNS_ERROR
+};
+typedef void (* dns_log_handler) (void * const, const enum dns_loglevel, const char * const, const char * const);
+struct dns {
+ int fd;
+ char * domain; // this is as it appears in the packet
+ dns_log_handler log_handler;
+ void * log_userdata;
+ struct sockaddr_in sockaddr;
+};
+typedef struct dns dns;
+static void dns_default_log_handler (void * const u __attribute__((unused)),
+ const enum dns_loglevel l, const char * a, const char * m) {
+ char * n = "unspec";
+ switch (l) {
+ case DNS_DEBUG:
+ n = "DEBUG";
+ break;
+ case DNS_INFO:
+ n = "INFO";
+ break;
+ case DNS_WARN:
+ n = "WARN";
+ break;
+ case DNS_ERROR:
+ n = "ERROR";
+ break;
+ }
+ fprintf(stderr, "%s %s %s\n", n, a, m);
+}
+struct dns * dns_init (void) {
+ struct dns * dns = calloc(1, sizeof(struct dns));
+ dns->fd = -1;
+ dns->domain = strdup(" call dns_set_domain to set the domain");
+ dns->domain[0] = strlen(dns->domain)-1;
+ dns->log_handler = dns_default_log_handler;
+ dns->sockaddr.sin_family = AF_INET;
+ dns->sockaddr.sin_port = htons(53);
+ 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_log_handler (struct dns * dns, dns_log_handler log_handler) {
+ if (!log_handler)
+ log_handler = dns_default_log_handler;
+ dns->log_handler = log_handler;
+}
+static void dns_set_log_userdata (struct dns * dns, void * log_userdata) {
+ dns->log_userdata = log_userdata;
+}
+static void dns_set_port (struct dns * dns, int port) {
+ dns->sockaddr.sin_port = htons(port);
+}
+static void dns_set_ip (struct dns * dns, const char * ip) {
+ if (!ip) {
+ dns->sockaddr.sin_addr.s_addr = INADDR_ANY;
+ return;
+ }
+ inet_aton(ip, &dns->sockaddr.sin_addr);
+}
+static void dns_run_once (struct dns * dns) {
+#define BUFLEN 4096
+ char buf[BUFLEN];
+ if (dns->fd == -1) {
+ if ((dns->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+ sprintf(buf, "socket failed with %s", strerror(errno));
+ dns->log_handler(dns->log_userdata, DNS_ERROR, "dns", buf);
+ return;
+ }
+ int ž = 1;
+ if (setsockopt(dns->fd, SOL_SOCKET, SO_BROADCAST, &ž, sizeof(ž)) == -1) {
+ sprintf(buf, "setsockopt failed with %s", strerror(errno));
+ dns->log_handler(dns->log_userdata, DNS_ERROR, "dns", buf);
+ close(dns->fd);
+ dns->fd = -1;
+ return;
+ }
+ if (bind(dns->fd, (struct sockaddr *) &dns->sockaddr, sizeof(struct sockaddr))) {
+ sprintf(buf, "bind failed with %s", strerror(errno));
+ dns->log_handler(dns->log_userdata, DNS_ERROR, "dns", buf);
+ close(dns->fd);
+ dns->fd = -1;
+ return;
+ }
+ }
+ struct sockaddr_in sender;
+ socklen_t sendl = sizeof sender;
+ int size = recvfrom(dns->fd, buf, 65535, MSG_DONTWAIT, (struct sockaddr *) &sender, &sendl);
+ if (size == -1) {
+ if (errno != EWOULDBLOCK) {
+ sprintf(buf, "recvfrom failed with %s", strerror(errno));
+ dns->log_handler(dns->log_userdata, DNS_ERROR, "dns", buf);
+ close(dns->fd);
+ dns->fd = -1;
+ return;
+ }
+ return;
+ }
+ struct header * header = (struct header *) buf; // time for some overwriting
+ header->flags = htons(RESPONSE | QUERY | AA | SUCCESS);
+ header->ancount = htons(1); // we keep question number intact, as we send all qs back
+ header->nscount = 0;
+ header->arcount = 0;
+ buf[BUFLEN-1] = '\0'; // strstr and strlen on untrusted data!
+ char * ouranswerisat = memmem(buf, size, "in-addr", strlen("in-addr"));
+ if (!ouranswerisat) {
+ dns->log_handler(dns->log_userdata, DNS_INFO, "dns",
+ "received request without 'in-addr' string ... weird.");
+ return;
+ }
+ ouranswerisat += 17;
+ struct rr * rr = (struct rr *) (ouranswerisat + strlen(header->data) + 1);
+ if (ouranswerisat + strlen(header->data) + 1 + sizeof(struct rr)
+ + strlen(dns->domain) + 1 >= buf + BUFLEN) {
+ dns->log_handler(dns->log_userdata, DNS_WARN, "dns", "sent packet would be to big");
+ return;
+ }
+ strcpy(ouranswerisat, header->data);
+ rr->type = htons(Ptr);
+ rr->class = htons(In);
+ rr->ttl = 0;
+ rr->len = htons(strlen(dns->domain)+1);
+ strcpy(rr->data, dns->domain);
+ int len = rr->data - buf + strlen(dns->domain) + 1;
+ if (sendto(dns->fd, buf, len, 0, (struct sockaddr *) &sender, sizeof(sender)) == -1) {
+ sprintf(buf, "sendto failed with %s", strerror(errno));
+ dns->log_handler(dns->log_userdata, DNS_ERROR, "dns", buf);
+ close(dns->fd);
+ dns->fd = -1;
+ return;
+ }
+ char dst[INET_ADDRSTRLEN];
+ const char * resp = inet_ntop(AF_INET, &sender.sin_addr, dst, INET_ADDRSTRLEN);
+ sprintf(buf, "successfully sent DNS reply to %s", resp ? resp : "[inet_ntop failed]");
+ dns->log_handler(dns->log_userdata, DNS_ERROR, "dns", buf);
+}
+static void dns_free (struct dns * dns) {
+ if (dns->fd != -1)
+ close (dns->fd);
+ free(dns->domain);
+ free(dns);
+}
+#if __INCLUDE_LEVEL__ == 0
+int shouldexit = 0;
+void handler (int signal __attribute__((unused))) {
+ shouldexit++;
+}
+int main (int argc, char ** argv) {
+ if (argc != 1+1) {
+ fprintf(stderr, "usage: %s respond.to.ptr.with.this.domain.\n", argv[0]);
+ return 1;
+ }
+ signal(SIGINT, handler);
+ signal(SIGTERM, handler);
+ dns * dns = dns_init();
+ dns_set_domain(dns, argv[1]);
+ while (!shouldexit) {
+ dns_run_once(dns);
+ }
+ dns_free(dns);
+}
+#endif
diff --git a/domain2name.c b/domain2name.c
new file mode 100644
index 0000000..47bfdad
--- /dev/null
+++ b/domain2name.c
@@ -0,0 +1,135 @@
+int domain2name_len (const char * s, int l) { /* TODO make domain2name FAIL at empty label (..) */
+ int r = 1; /* ending terminator */ /* make functions FAIL at label.length > 63 */
+ int o = 0; /* label offset */ /* currently domain2name never fails */
+ for (int i = 0; i < l; i++) { /* NOTE when using BOTH _len, check that they are */
+ if (s[i] == '.') { /* NOT negative. d2n_len may fail in d future */
+ if (!o) /* double period or starting period, label is empty */
+ break; /* we could return -1 here if r == 1 */
+ o = 0;
+ continue;
+ }
+ if (!o) /* label has started */
+ r++;
+ if (o < 63) { /* we cap label length at 64 bytes. we could return -2 here. */
+ r++;
+ o++;
+ }
+ }
+ return r;
+}
+int domain2name (char * n /* at least _len bytes */, const char * s, int l) { /* l is length of s */
+ char * c = n; /* where to write the label size when done with label */
+ char * w = n;
+ int o = 0; /* label offset */
+ for (int i = 0; i <= l /* yes, we go one more ... */; i++) {
+ if (i == l /* ... here */ || s[i] == '.') { /* end of label or end of last label */
+ if (!o) /* double period or starting period, label is empty */
+ break;
+ if (o <= 63) /* max length of label (six bits) */
+ *c = o;
+ o = 0;
+ continue;
+ }
+ if (!o++) /* label has started */
+ c = w++; /* to be filled with length */
+ if (o <= 63)
+ *w++ = s[i];
+ if (o == 64) /* if this label is too long, instead of writing it, we silently cap */
+ *c = 63; /* it at 63 bytes */
+ }
+ *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. */
+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;
+ char f[s/8+1];
+ memset(f, '\0', s/8+1);
+ if (n < u+s && *n == '\0') {
+ return 2;
+ }
+ while (n < u+s) {
+ if (*n & 1 << 7) {
+ if (!(*n & 1 << 6))
+ return -1; /* 10xx xxxx not implemented - reserved for future use */
+ if (n+1 >= u+s)
+ return -2; /* malformed packet */
+ if (f[(n-u)/8] & 1 << (n-u)%8)
+ return -3; /* infinite pointer loop detected */
+ f[(n-u)/8] |= 1 << (n-u)%8;
+ n = u + N2DO(*(uint16_t *) n);
+ continue;
+ }
+ if (*n & 1 << 6)
+ return -4; /* 01xx xxxx not implemented - reserved for future use */
+ if (!*n)
+ return r+1;
+ r += *n+1;
+ n += *n+1;
+ }
+ return -5; /* malformed packet */
+} /* returns number of bytes needed for buffer, passed as the first argument of name2domain(). */
+const char * name2domain (char * d /* >= _len B */, const char * u, int s, const char * n) {
+ char * w = d; /* if d is NULL nothing is written and last byte of name is returned */
+ const char * r = NULL;
+ char f[s/8+1];
+ memset(f, '\0', s/8+1);
+ if (n < u+s && *n == '\0') {
+ *w++ = '.';
+ *w++ = '\0';
+ return n;
+ }
+ while (n < u+s) {
+ if (*n & 1 << 7) {
+ if (!(*n & 1 << 6))
+ return NULL; /* 10xx xxxx N/I - reserved for future use as per RFC */
+ if (n+1 >= u+s)
+ return NULL;
+ r = n+1;
+ if (f[(n-u)/8] & 1 << (n-u)%8)
+ return NULL; /* infinite pointer loop detected */
+ f[(n-u)/8] |= 1 << (n-u)%8;
+ n = u + N2DO(*(uint16_t *) n);
+ continue;
+ }
+ if (*n & 1 << 6)
+ return NULL; /* 01xx xxxx N/I - reserved for future use as per RFC */
+ if (!*n) { /* end of name */
+ if (w)
+ *w++ = '\0';
+ return r ? r : n;
+ }
+ const char * x = n+*n;
+ n++;
+ if (!(x < u+s))
+ return NULL; /* malformed packet */
+ while (n <= x)
+ if (w)
+ *w++ = *n++;
+ else
+ n++;
+ if (w)
+ *w++ = '.';
+ }
+ return NULL; /* malformed packet */
+} /* Returns ptr to last byte of name - '\0' or dnsptr. Ret. NULL on fail (_len also returned < 0) */
+int normalizedomain_len (const char * s, int l) {
+ int ž = domain2name_len(s, l);
+ if (ž < 0)
+ return -6;
+ char b[ž];
+ if (domain2name(b, s, l) != ž)
+ return -7;
+ return name2domain_len(b, ž, b);
+}
+int normalizedomain (char * d /* at least _len bytes */, const char * s, int l) {
+ int ž = domain2name_len(s, l);
+ if (ž < 0)
+ return -6;
+ char b[ž];
+ if (domain2name(b, s, l) != ž)
+ return -7;
+ if (!name2domain(d, b, ž, b))
+ return -8;
+ return 0;
+}
diff --git a/ircxmpp.c b/ircxmpp.c
index a7b6a44..76e3a9f 100644
--- a/ircxmpp.c
+++ b/ircxmpp.c
@@ -126,7 +126,8 @@ static void jid2ircuser (char * jid) {
}
static void bridge_forward (const char * f, const char * m, struct ircxmpp * ircxmpp, enum side s) {
struct bridge * bridge = find_bridge(&ircxmpp->bridges, f, !s);
- if (strstr(f, "ircxmpp_") || (ircxmpp->irchost && strstr(f, ircxmpp->irchost)))
+ if (strstr(f, "ircxmpp_") || (ircxmpp->irchost && strstr(f, ircxmpp->irchost))
+ || (ircxmpp->domain && strstr(f, ircxmpp->domain)))
return;
LOG(ircxmpp, IRCXMPP_DEBUG, "sending text from %s to %s: %s", f, s == IRC ? "IRC" : "XMPP",
m ? m : "[join only]");
@@ -136,9 +137,21 @@ static void bridge_forward (const char * f, const char * m, struct ircxmpp * irc
bridge->ircxmpp = ircxmpp;
bridge->side = !s;
tsearch(bridge, &ircxmpp->bridges, bridge_compare);
- if (s == IRC)
+ if (s == IRC) {
+ char buf[512+512+strlen(ircxmpp->domain)]; // for good measure, i think
+ char * cp = strchr(f, '@'); // 512+1+strlen is enough
+ if (cp) { // cmiiw
+ strncpy(buf, cp+1, 511);
+ buf[511] = '\0';
+ } else
+ strcpy(buf, "unable.to.extract.domain.from.JID");
+ *strchrnul(buf, '/') = '\0';
+ if (buf[strlen(buf)-1] != '.') // jid domain can probably end with a dot.
+ strcat(buf, "."); // two consecutive dots would invalidate
+ strcat(buf, ircxmpp->domain); // the domain
+ ircxmpp->domain_setter(ircxmpp->domain_setter_userdata, buf);
init_irc(bridge);
- else {
+ } else {
bridge->conn = xmpp_conn_new(bridge->ircxmpp->ctx);
xmpp_conn_set_jid(bridge->conn, bridge->ircxmpp->jid);
xmpp_conn_set_pass(bridge->conn, bridge->ircxmpp->password);
@@ -469,6 +482,10 @@ static int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero
LOG(ircxmpp, IRCXMPP_INFO, "CONNECTING control %s!ircxmpp@host", b);
free(ircxmpp->ircnick);
ircxmpp->ircnick = strdup(b);
+ char domain[512+strlen(ircxmpp->domain)];
+ strcpy(domain, "čžš .. invalid domain so that we get our IP address as irchost ..");
+ strcat(domain, ircxmpp->domain);
+ ircxmpp->domain_setter(ircxmpp->domain_setter_userdata, domain);
if (irc_connect(ircxmpp->irc, ircxmpp->hostname, ircxmpp->port,
NULL, b, "ircxmpp", "http git.sijanec.eu/sijanec/ircxmpp")) {
LOG(ircxmpp, IRCXMPP_ERROR, "control could not connect: %s",
@@ -562,6 +579,7 @@ static void init_irc_control (struct ircxmpp * ircxmpp) {
irc_run_once_control(ircxmpp);
return;
}
+/* /IRC */ /* irc_is_connected(irc_session_t * session): 1 ali 0 */
static void default_log_handler (void * const u __attribute__((unused)),
const enum ircxmpp_loglevel l, const char * const a, const char * const m) {
char * t = "unspec";
@@ -581,7 +599,10 @@ static void default_log_handler (void * const u __attribute__((unused)),
}
fprintf(stderr, "[ircxmpp %s] %s: %s\n", t, a, m);
}
-/* /IRC */ /* irc_is_connected(irc_session_t * session): 1 ali 0 */
+static void default_domain_setter (void * u __attribute__((unused)),
+ const char * d __attribute__((unused))) {
+ return; // does nothing
+}
static void send_xmpp_logs_to_me (void * const u, const xmpp_log_level_t l, const char * const a,
const char * const m) {
enum ircxmpp_loglevel loglevel = IRCXMPP_ERROR;
@@ -645,6 +666,20 @@ void ircxmpp_set_channel_password (struct ircxmpp * ircxmpp, const char * channe
free(ircxmpp->channel_password);
ircxmpp->channel_password = strdup(channel_password);
}
+void ircxmpp_set_domain_setter (struct ircxmpp * ircxmpp, ircxmpp_domain_setter setter) {
+ if (!setter)
+ setter = default_domain_setter;
+ ircxmpp->domain_setter = setter;
+}
+void ircxmpp_set_domain_setter_userdata (struct ircxmpp * ircxmpp, void * userdata) {
+ if (!userdata)
+ userdata = NULL;
+ ircxmpp->domain_setter_userdata = userdata;
+}
+void ircxmpp_set_domain (struct ircxmpp * ircxmpp, const char * domain) {
+ free(ircxmpp->domain);
+ ircxmpp->domain = strdup(domain); // this intentionally crashes
+}
static void obdelaj_bridge (const void * nodep, VISIT which __attribute__((unused)),
int depth __attribute__((unused))) {
struct bridge * bridge = *(struct bridge **) nodep;
@@ -715,9 +750,11 @@ void ircxmpp_free (struct ircxmpp * ircxmpp) {
free(ircxmpp->channel);
free(ircxmpp->muc);
free(ircxmpp->channel_password);
+ free(ircxmpp->domain);
free(ircxmpp);
}
#else
+#include "dns.c"
int shouldexit = 0;
void signalhandler (int s __attribute__((unused))) {
shouldexit++;
@@ -733,6 +770,7 @@ int main (void) {
"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;
+ char * domain; // 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
@@ -766,7 +804,21 @@ int main (void) {
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);
}
+ struct dns * dns = dns_init();
+ char buf[512+strlen(domain)];
+ strcpy(buf, "ircxmpp.no.domain.set.yet.");
+ strcat(buf, domain);
+ dns_set_domain(dns, buf);
+ if (getenv("IX_DNS_PORT"))
+ dns_set_port(dns, atoi(getenv("IX_DNS_PORT")));
+ if (getenv("IX_DNS_IP"))
+ dns_set_ip(dns, getenv("IX_DNS_IP"));
+ dns_set_log_handler(dns, dns_default_log_handler);
+ dns_set_log_userdata(dns, NULL); // so we don't read uninitialized values
signal(SIGTERM, signalhandler);
signal(SIGINT, signalhandler);
// signal(SIGPIPE, SIG_IGN);
@@ -777,12 +829,15 @@ int main (void) {
.tv_sec = 0,
.tv_nsec = getenv("IX_LOOPDELAY") ? atoi(getenv("IX_LOOPDELAY"))/1000 : 1e7
};
+ if (domain)
+ dns_run_once(dns);
nanosleep(&ts, NULL);
}
fprintf(stderr, "signal received, cleaning up!\n");
for (size_t i = 0; i < handles_length; i++)
ircxmpp_free(handles[i]);
free(handles);
+ dns_free(dns);
return 0;
}
#endif
diff --git a/ircxmpp.conf b/ircxmpp.conf
index d389ba1..9851fab 100644
--- a/ircxmpp.conf
+++ b/ircxmpp.conf
@@ -1,6 +1,13 @@
## configuration file for ircxmpp.
## see http://git.sijanec.eu/sijanec/ircxmpp
## or /usr/share/doc/ircxmpp/README.Debian
+ ####################### GLOBAL VARIABLES #######################
+## global variables do not have their 2, 3, 4 counterparts and are same for all bridges.
+## delay after each event loop cycle in microseconds, defaults to 10ms.
+# IX_LOOPDELAY=10000
+## port on which the DNS server should run. use this if you already have a DNS server that'd proxy.
+# IX_DNS_PORT=53
+ ###################### PER-BRIDGE VARIABLES ######################
####################### REQUIRED VARIABLES #######################
## JID of ircxmpp user on XMPP server to connect with
# IX_JID=change@me
@@ -17,8 +24,8 @@
####################### OPTIONAL VARIABLES #######################
## set to IRC channel password if channel on IRC is password protected
# IX_CHPASS=somepassword
-## delay after each event loop cycle in microseconds, defaults to 10ms.
-# IX_LOOPDELAY=10000
+## domain name suffix (your domain name) for IRC hostname spoofing. setting this ENABLES DNS server
+# IX_DOMAIN=ircxmpp.example
####################### 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
@@ -28,4 +35,4 @@
# 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.
+# IX_DOMAIN2=ircxmpp2.example
diff --git a/ircxmpp.h b/ircxmpp.h
index 29b4419..38b6f16 100644
--- a/ircxmpp.h
+++ b/ircxmpp.h
@@ -4,8 +4,9 @@ enum ircxmpp_loglevel {
IRCXMPP_WARN,
IRCXMPP_ERROR
};
-typedef void (*ircxmpp_logger)
+typedef void (* ircxmpp_logger)
(void * const, const enum ircxmpp_loglevel, const char * const, const char * const);
+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>
@@ -175,6 +176,9 @@ struct ircxmpp {
void * log_userdata;
ircxmpp_logger log_handler;
xmpp_log_t xmpp_logger;
+ ircxmpp_domain_setter domain_setter;
+ void * domain_setter_userdata;
+ char * domain;
};
static void send_xmpp_logs_to_me (
void * const, const xmpp_log_level_t, const char * const, const char * const);
@@ -218,10 +222,11 @@ static void event_numeric_control (
static int irc_run_once (struct bridge *);
static void init_irc (struct bridge *);
static void init_irc_control (struct ircxmpp *);
+// /IRC
static void obdelaj_bridge (const void *, VISIT, int);
static void default_log_handler (
void * const, const enum ircxmpp_loglevel, const char * const, const char * const);
-// /IRC
+static void default_domain_setter (void *, const char *);
#endif // IX_LIB
// ZUNANJE
typedef struct ircxmpp ircxmpp; /* opaque handle */
@@ -235,7 +240,9 @@ 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 *);
-int ircxmpp_version = 0;
+void ircxmpp_set_domain_setter (ircxmpp *, ircxmpp_domain_setter);
+void ircxmpp_set_domain_setter_userdata (ircxmpp *, void *);
+void ircxmpp_set_domain (ircxmpp *, const char *);
+void ircxmpp_run_once (ircxmpp *);
+void ircxmpp_free (ircxmpp *);
// /ZUNANJE