From 877b7b64021410c1bb686cad562e7446eb65c318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Mon, 2 May 2022 00:45:48 +0200 Subject: dns server. untested. compiles. --- CHANGELOG | 6 ++ README | 36 +++++++- debian/changelog | 1 - debian/ircxmpp.service | 2 + dns.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++ domain2name.c | 135 +++++++++++++++++++++++++++ ircxmpp.c | 63 ++++++++++++- ircxmpp.conf | 13 ++- ircxmpp.h | 17 +++- 9 files changed, 501 insertions(+), 16 deletions(-) delete mode 120000 debian/changelog create mode 100644 dns.c create mode 100644 domain2name.c 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 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 /* do not use members of struct ircxmpp, use opaque ircxmpp type! */ #include @@ -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 -- cgit v1.2.3