summaryrefslogtreecommitdiff
path: root/src/server/shibari-server-tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/shibari-server-tcp.c')
-rw-r--r--src/server/shibari-server-tcp.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/server/shibari-server-tcp.c b/src/server/shibari-server-tcp.c
new file mode 100644
index 0000000..e646a44
--- /dev/null
+++ b/src/server/shibari-server-tcp.c
@@ -0,0 +1,235 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/types.h>
+#include <skalibs/strerr.h>
+#include <skalibs/buffer.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/sig.h>
+#include <skalibs/tai.h>
+#include <skalibs/ip46.h>
+#include <skalibs/cdb.h>
+#include <skalibs/unix-timed.h>
+
+#include <s6-dns/s6dns-domain.h>
+#include <s6-dns/s6dns-message.h>
+
+#include <shibari/common.h>
+#include <shibari/server.h>
+
+#define PROGNAME "shibari-server-tcp"
+#define USAGE PROGNAME " [ -v verbosity ] [ -f cdbfile ] [ -r timeout ] [ -w timeout ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+#define QMAX 2048
+#define RMAX 65535
+
+static uint32_t verbosity = 1 ;
+
+static inline void get_socket_info (ip46 *localip, uint16_t *localport, ip46 *remoteip, uint16_t *remoteport)
+{
+ char const *x = getenv("PROTO") ;
+ if (!x) strerr_dienotset(100, "PROTO") ;
+ {
+ size_t protolen = strlen(x) ;
+ char var[protolen + 11] ;
+ memcpy(var, x, protolen) ;
+ memcpy(var + protolen, "LOCALIP", 8) ;
+ x = getenv(var) ;
+ if (!x) strerr_dienotset(100, var) ;
+ if (!ip46_scan(x, localip)) strerr_dieinvalid(100, var) ;
+ memcpy(var + protolen + 5, "PORT", 5) ;
+ x = getenv(var) ;
+ if (!x) strerr_dienotset(100, var) ;
+ if (!uint160_scan(x, localport)) strerr_dieinvalid(100, var) ;
+ memcpy(var + protolen, "REMOTEIP", 9) ;
+ x = getenv(var) ;
+ if (!x) strerr_dienotset(100, var) ;
+ if (!ip46_scan(x, remoteip)) strerr_dieinvalid(100, var) ;
+ memcpy(var + protolen + 6, "PORT", 5) ;
+ x = getenv(var) ;
+ if (!x) strerr_dienotset(100, var) ;
+ if (!uint160_scan(x, remoteport)) strerr_dieinvalid(100, var) ;
+ }
+}
+
+static void add (shibari_packet *pkt, shibari_tdb_entry const *entry, int prefixlen, uint16_t id, s6dns_domain_t const *zone, tain const *deadline)
+{
+ if (!shibari_packet_add_rr(pkt, entry, prefixlen, 0, 2))
+ {
+ shibari_packet_end(pkt) ;
+ if (!buffer_timed_put_g(buffer_1, pkt->buf - 2, pkt->pos + 2, deadline))
+ strerr_diefu1sys(111, "write to stdout") ;
+ shibari_packet_begin(pkt, id, zone, SHIBARI_T_AXFR) ;
+ if (!shibari_packet_add_rr(pkt, entry, prefixlen, 0, 2))
+ strerr_dief1x(101, "can't happen: record too long to fit in single packet") ;
+ }
+}
+
+static inline int axfr (char const *axfrok, char const *loc, cdb const *tdb, s6dns_message_header_t const *qhdr, s6dns_domain_t const *zone, shibari_packet *pkt, tain const *deadline, tain const *wstamp)
+{
+ shibari_tdb_entry soa ;
+ shibari_tdb_entry cur ;
+ uint32_t pos = CDB_TRAVERSE_INIT() ;
+ if (!axfrok) return 5 ;
+ if (axfrok[0] != '*')
+ {
+ s6dns_domain_t decoded = *zone ;
+ unsigned int zonelen ;
+ size_t len = strlen(axfrok) + 1 ;
+ char buf[256] ;
+ if (!s6dns_domain_decode(&decoded)) return 1 ;
+ zonelen = s6dns_domain_tostring(buf, 256, &decoded) ;
+ while (len >= zonelen)
+ {
+ if (!strncmp(buf, axfrok, zonelen) && (!axfrok[zonelen] || strchr("/,; \t\n", axfrok[zonelen]))) break ;
+ axfrok += zonelen + 1 ;
+ len -= zonelen + 1 ;
+ }
+ if (len < zonelen) return 5 ;
+ }
+
+ {
+ cdb_find_state state = CDB_FIND_STATE_ZERO ;
+ int r = shibari_tdb_read_entry(tdb, &state, &soa, zone->s, zone->len, SHIBARI_T_SOA, 0, loc, wstamp, 0) ;
+ if (r == -1) return 2 ;
+ if (!r) return 9 ;
+ }
+
+ shibari_packet_begin(pkt, qhdr->id, zone, SHIBARI_T_AXFR) ;
+ pkt->hdr.aa = 1 ;
+ add(pkt, &soa, 0, qhdr->id, zone, deadline) ;
+
+ for (;;)
+ {
+ cdb_data data ;
+ int prefixlen ;
+ int r = cdb_traverse_next(tdb, &cur.key, &data, &pos) ;
+ if (r == -1) return 2 ;
+ if (!r) break ;
+ prefixlen = shibari_util_get_prefixlen(cur.key.s, cur.key.len, zone->s, zone->len) ;
+ if (prefixlen == -1) continue ;
+ r = shibari_tdb_entry_parse(&cur, data.s, data.len, SHIBARI_T_ANY, 2, loc, wstamp) ;
+ if (r == -1) return 2 ;
+ if (!r) continue ;
+ if (cur.type == SHIBARI_T_SOA) continue ;
+ add(pkt, &cur, prefixlen, qhdr->id, zone, deadline) ;
+ }
+
+ add(pkt, &soa, 0, qhdr->id, zone, deadline) ;
+ shibari_packet_end(pkt) ;
+ return 0 ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ cdb tdb = CDB_ZERO ;
+ char const *axfrok = getenv("AXFR") ;
+ char const *loc = getenv("LOC") ;
+ tain rtto = TAIN_INFINITE_RELATIVE, wtto = TAIN_INFINITE_RELATIVE ;
+ ip46 localip, remoteip ;
+ uint16_t localport, remoteport ;
+ char progbuf[sizeof(PROGNAME) + 5 + PID_FMT] = PROGNAME ": pid " ;
+ char buf[RMAX + 2] ;
+ shibari_packet pkt = SHIBARI_PACKET_INIT(buf, RMAX + 2, 1) ;
+ PROG = "shibari-server-tcp" ;
+
+ {
+ size_t pos = sizeof(PROGNAME) + 5 ;
+ pos += pid_fmt(progbuf + pos, getpid()) ;
+ progbuf[pos++] = 0 ;
+ }
+
+ {
+ char const *tdbfile = "data.cdb" ;
+ uint32_t r = 0, w = 0 ;
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "v:f:r:w:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'v' : if (!uint320_scan(l.arg, &verbosity)) dieusage() ; break ;
+ case 'f' : tdbfile = l.arg ; break ;
+ case 'r' : if (!uint320_scan(l.arg, &r)) dieusage() ; break ;
+ case 'w' : if (!uint320_scan(l.arg, &w)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (r) tain_from_millisecs(&rtto, r) ;
+ if (w) tain_from_millisecs(&wtto, w) ;
+ get_socket_info(&localip, &localport, &remoteip, &remoteport) ;
+ PROG = progbuf ;
+ if (!cdb_init(&tdb, tdbfile)) strerr_diefu2sys(111, "open DNS database file ", tdbfile) ;
+ }
+
+ if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ tain_now_set_stopwatch_g() ;
+ shibari_log_start(verbosity, &remoteip, remoteport) ;
+
+ for (;;)
+ {
+ tain wstamp ;
+ size_t w ;
+ tain deadline ;
+ s6dns_message_header_t hdr ;
+ s6dns_message_counts_t counts ;
+ s6dns_domain_t name ;
+ unsigned int rcode ;
+ uint16_t qtype ;
+ uint16_t len ;
+ tain_add_g(&deadline, &rtto) ;
+ w = buffer_timed_get_g(buffer_0, buf, 2, &deadline) ;
+ if (w == 1) strerr_dief1x(1, "invalid request") ;
+ if (!w)
+ {
+ if (errno != EPIPE && errno != ETIMEDOUT)
+ strerr_diefu1sys(111, "read from stdin") ;
+ else break ;
+ }
+ uint16_unpack_big(buf, &len) ;
+ if (len > QMAX) strerr_dief1x(1, "request too large") ;
+ if (buffer_timed_get_g(buffer_0, buf, len, &deadline) < len)
+ strerr_diefu1sys(111, "read from stdin") ;
+
+ if (!s6dns_message_parse_init(&hdr, &counts, buf, len, &rcode))
+ strerr_diefu1sys(111, "parse message") ;
+ if (hdr.opcode) { rcode = 4 ; goto answer ; }
+ if (!s6dns_message_parse_question(&counts, &name, &qtype, buf, len, &rcode) || !s6dns_domain_encode(&name))
+ {
+ rcode = errno == ENOTSUP ? 4 : 1 ;
+ goto answer ;
+ }
+ shibari_log_query(verbosity, &name, qtype) ;
+ tain_add_g(&deadline, &wtto) ;
+ tain_wallclock_read(&wstamp) ;
+ rcode = qtype == SHIBARI_T_AXFR ?
+ axfr(axfrok, loc, &tdb, &hdr, &name, &pkt, &deadline, &wstamp) :
+ shibari_packet_tdb_answer_query(&pkt, &tdb, &hdr, &name, qtype, loc, &wstamp) ;
+
+ answer:
+ if (rcode && rcode != 3)
+ {
+ shibari_packet_begin(&pkt, hdr.id, &name, qtype) ;
+ pkt.hdr.rcode = rcode ;
+ shibari_packet_end(&pkt) ;
+ }
+ shibari_log_answer(verbosity, &pkt.hdr, pkt.pos) ;
+ if (!buffer_timed_put_g(buffer_1, buf, pkt.pos + 2, &deadline)
+ || !buffer_timed_flush_g(buffer_1, &deadline))
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+
+ shibari_log_exit(verbosity, 0) ;
+ return 0 ;
+}