/* vpn_dns.C -- handle the dns tunnel part of the protocol. Copyright (C) 2003-2008 Marc Lehmann This file is part of GVPE. GVPE is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . Additional permission under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with the OpenSSL project's OpenSSL library (or a modified version of that library), containing parts covered by the terms of the OpenSSL or SSLeay licenses, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL used as well as that of the covered work. */ // TODO: EDNS0 option to increase dns mtu? // TODO: re-write dns packet parsing/creation using a safe mem-buffer // to ensure no buffer overflows or similar problems. #include "config.h" #if ENABLE_DNS // dns processing is EXTREMELY ugly. For obvious(?) reasons. // it's a hack, use only in emergency situations please. #include #include #include #include #include #include #include #include #include #include #include #include /* bug in libgmp: gmp.h relies on cstdio being included */ #include #include "netcompat.h" #include "vpn.h" #define MAX_POLL_INTERVAL 5. // how often to poll minimally when the server has no data #define ACTIVITY_INTERVAL 5. #define INITIAL_TIMEOUT 0.1 // retry timeouts #define INITIAL_SYN_TIMEOUT 2. // retry timeout for initial syn #define MAX_SEND_INTERVAL 2. // optimistic? #define MAX_WINDOW 1000 // max. for MAX_OUTSTANDING, and backlog #define MAX_BACKLOG (64*1024) // size of gvpe protocol backlog (bytes), must be > MAXSIZE #define MAX_DOMAIN_SIZE 240 // 255 is legal limit, but bind doesn't compress well // 240 leaves about 4 bytes of server reply data // every request byte less give room for two reply bytes #define SEQNO_MASK 0x3fff #define SEQNO_EQ(a,b) ( 0 == ( ((a) ^ (b)) & SEQNO_MASK) ) #define MAX_LBL_SIZE 63 #define MAX_PKT_SIZE 512 #define RR_TYPE_A 1 #define RR_TYPE_NULL 10 #define RR_TYPE_TXT 16 #define RR_TYPE_AAAA 28 #define RR_TYPE_ANY 255 #define RR_CLASS_IN 1 #define CMD_IP_1 207 #define CMD_IP_2 46 #define CMD_IP_3 236 #define CMD_IP_RST 29 #define CMD_IP_SYN 113 #define CMD_IP_REJ 32 // works for cmaps up to 255 (not 256!) struct charmap { enum { INVALID = (u8)255 }; char encode [256]; // index => char u8 decode [256]; // char => index unsigned int size; charmap (const char *cmap); }; charmap::charmap (const char *cmap) { char *enc = encode; u8 *dec = decode; memset (enc, (char) 0, 256); memset (dec, (char)INVALID, 256); for (size = 0; cmap [size]; size++) { char c = cmap [size]; enc [size] = c; dec [(u8)c] = size; // allow lowercase/uppercase aliases if possible if (c >= 'A' && c <= 'Z' && dec [c + ('a' - 'A')] == INVALID) dec [c + ('a' - 'A')] = size; if (c >= 'a' && c <= 'z' && dec [c - ('a' - 'A')] == INVALID) dec [c - ('a' - 'A')] = size; } assert (size < 256); } #define MAX_DEC_LEN 500 #define MAX_ENC_LEN (MAX_DEC_LEN * 2) #define MAX_LIMBS ((MAX_DEC_LEN * 8 + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS) // ugly. minimum base is 16(!) struct basecoder { charmap cmap; unsigned int enc_len [MAX_DEC_LEN]; unsigned int dec_len [MAX_ENC_LEN]; unsigned int encode_len (unsigned int len); unsigned int decode_len (unsigned int len); unsigned int encode (char *dst, u8 *src, unsigned int len); unsigned int decode (u8 *dst, char *src, unsigned int len); basecoder (const char *cmap); }; basecoder::basecoder (const char *cmap) : cmap (cmap) { for (unsigned int len = 0; len < MAX_DEC_LEN; ++len) { u8 src [MAX_DEC_LEN]; u8 dst [MAX_ENC_LEN]; memset (src, 255, len); mp_limb_t m [MAX_LIMBS]; mp_size_t n; n = mpn_set_str (m, src, len, 256); n = mpn_get_str (dst, this->cmap.size, m, n); for (int i = 0; !dst [i]; ++i) n--; enc_len [len] = n; dec_len [n] = len; } } unsigned int basecoder::encode_len (unsigned int len) { return enc_len [len]; } unsigned int basecoder::decode_len (unsigned int len) { while (len && !dec_len [len]) --len; return dec_len [len]; } unsigned int basecoder::encode (char *dst, u8 *src, unsigned int len) { if (!len || len > MAX_DEC_LEN) return 0; int elen = encode_len (len); mp_limb_t m [MAX_LIMBS]; mp_size_t n; u8 dst_ [MAX_ENC_LEN]; n = mpn_set_str (m, src, len, 256); n = mpn_get_str (dst_, cmap.size, m, n); int plen = elen; // for padding while (n < plen) { *dst++ = cmap.encode [0]; plen--; } for (unsigned int i = n - plen; i < n; ++i) *dst++ = cmap.encode [dst_ [i]]; return elen; } unsigned int basecoder::decode (u8 *dst, char *src, unsigned int len) { if (!len || len > MAX_ENC_LEN) return 0; u8 src_ [MAX_ENC_LEN]; unsigned int elen = 0; while (len--) { u8 val = cmap.decode [(u8)*src++]; if (val != charmap::INVALID) src_ [elen++] = val; } int dlen = decode_len (elen); mp_limb_t m [MAX_LIMBS]; mp_size_t n; u8 dst_ [MAX_DEC_LEN]; n = mpn_set_str (m, src_, elen, cmap.size); n = mpn_get_str (dst_, 256, m, n); if (n < dlen) { memset (dst, 0, dlen - n); memcpy (dst + dlen - n, dst_, n); } else memcpy (dst, dst_ + n - dlen, dlen); return dlen; } #if 0 struct test { test (); } test; test::test () { basecoder cdc ("0123456789abcdefghijklmnopqrstuvwxyz"); u8 in[] = "0123456789abcdefghijklmnopqrstuvwxyz"; static char enc[200]; static u8 dec[200]; for (int i = 1; i < 20; i++) { int elen = cdc.encode (enc, in, i); int dlen = cdc.decode (dec, enc, elen); printf ("%d>%d>%d (%s>%s)\n", i, elen, dlen, enc, dec); } abort (); } #endif //static basecoder cdc64 ("_dDpPhHzZrR06QqMmjJkKBb34TtSsvVlL81xXaAeEFf92WwGgYyoO57UucCNniI-"); //static basecoder cdc63 ("_dDpPhHzZrR06QqMmjJkKBb34TtSsvVlL81xXaAeEFf92WwGgYyoO57UucCNniI"); static basecoder cdc62 ("dDpPhHzZrR06QqMmjJkKBb34TtSsvVlL81xXaAeEFf92WwGgYyoO57UucCNniI"); //static basecoder cdc36 ("dphzr06qmjkb34tsvl81xaef92wgyo57ucni"); // unused as of yet static basecoder cdc26 ("dPhZrQmJkBtSvLxAeFwGyO"); ///////////////////////////////////////////////////////////////////////////// #define HDRSIZE 6 inline void encode_header (char *data, int clientid, int seqno, int retry = 0) { seqno &= SEQNO_MASK; u8 hdr[3] = { clientid, (seqno >> 8) | (retry << 6), seqno, }; assert (clientid < 256); cdc26.encode (data, hdr, 3); } inline void decode_header (char *data, int &clientid, int &seqno) { u8 hdr[3]; cdc26.decode (hdr, data, HDRSIZE); clientid = hdr[0]; seqno = ((hdr[1] << 8) | hdr[2]) & SEQNO_MASK; } ///////////////////////////////////////////////////////////////////////////// struct byte_stream { u8 *data; int maxsize; int fill; byte_stream (int maxsize); ~byte_stream (); bool empty () { return !fill; } int size () { return fill; } bool put (u8 *data, unsigned int datalen); bool put (vpn_packet *pkt); vpn_packet *get (); u8 *begin () { return data; } void remove (int count); }; byte_stream::byte_stream (int maxsize) : maxsize (maxsize), fill (0) { data = new u8 [maxsize]; } byte_stream::~byte_stream () { delete data; } void byte_stream::remove (int count) { if (count > fill) assert (count <= fill); memmove (data, data + count, fill -= count); } bool byte_stream::put (u8 *data, unsigned int datalen) { if (maxsize - fill < datalen) return false; memcpy (this->data + fill, data, datalen); fill += datalen; return true; } bool byte_stream::put (vpn_packet *pkt) { if (maxsize - fill < pkt->len + 2) return false; data [fill++] = pkt->len >> 8; data [fill++] = pkt->len; memcpy (data + fill, pkt->at (0), pkt->len); fill += pkt->len; return true; } vpn_packet *byte_stream::get () { unsigned int len; for (;;) { len = (data [0] << 8) | data [1]; if (len <= MAXSIZE || fill < 2) break; // TODO: handle this better than skipping, e.g. by reset slog (L_DEBUG, _("DNS: corrupted packet stream skipping a byte...")); remove (1); } if (fill < len + 2) return 0; vpn_packet *pkt = new vpn_packet; pkt->len = len; memcpy (pkt->at (0), data + 2, len); remove (len + 2); return pkt; } ///////////////////////////////////////////////////////////////////////////// #define FLAG_QUERY ( 0 << 15) #define FLAG_RESPONSE ( 1 << 15) #define FLAG_OP_MASK (15 << 11) #define FLAG_OP_QUERY ( 0 << 11) #define FLAG_AA ( 1 << 10) #define FLAG_TC ( 1 << 9) #define FLAG_RD ( 1 << 8) #define FLAG_RA ( 1 << 7) #define FLAG_AUTH ( 1 << 5) #define FLAG_RCODE_MASK (15 << 0) #define FLAG_RCODE_OK ( 0 << 0) #define FLAG_RCODE_FORMERR ( 1 << 0) #define FLAG_RCODE_SERVFAIL ( 2 << 0) #define FLAG_RCODE_NXDOMAIN ( 3 << 0) #define FLAG_RCODE_REFUSED ( 5 << 0) #define DEFAULT_CLIENT_FLAGS (FLAG_QUERY | FLAG_OP_QUERY | FLAG_RD) #define DEFAULT_SERVER_FLAGS (FLAG_RESPONSE | FLAG_OP_QUERY | FLAG_AA | FLAG_RD | FLAG_RA) struct dns_cfg { static int next_uid; u8 id1, id2, id3, id4; u8 version; u8 flags; u8 rrtype; u8 def_ttl; u16 client; u16 uid; // to make request unique u16 max_size; u8 seq_cdc; u8 req_cdc; u8 rep_cdc; u8 delay; // time in 0.01s units that the server may delay replying packets u8 r3, r4; u8 r5, r6, r7, r8; void reset (int clientid); bool valid (); }; int dns_cfg::next_uid; void dns_cfg::reset (int clientid) { id1 = 'G'; id2 = 'V'; id3 = 'P'; id4 = 'E'; version = 1; rrtype = RR_TYPE_TXT; flags = 0; def_ttl = 0; seq_cdc = 26; req_cdc = 62; rep_cdc = 0; max_size = htons (MAX_PKT_SIZE); client = htons (clientid); uid = next_uid++; delay = 0; r3 = r4 = 0; r4 = r5 = r6 = r7 = 0; } bool dns_cfg::valid () { // although the protocol itself allows for some configurability, // only the following encoding/decoding settings are implemented. return id1 == 'G' && id2 == 'V' && id3 == 'P' && id4 == 'E' && seq_cdc == 26 && req_cdc == 62 && rep_cdc == 0 && version == 1; } struct dns_packet : net_packet { u16 id; u16 flags; // QR:1 Opcode:4 AA:1 TC:1 RD:1 RA:1 Z:3 RCODE:4 u16 qdcount, ancount, nscount, arcount; u8 data [MAXSIZE - 6 * 2]; int decode_label (char *data, int size, int &offs); }; int dns_packet::decode_label (char *data, int size, int &offs) { char *orig = data; memset (data, 0, size); while (offs < size - 1) { u8 len = (*this)[offs++]; if (!len) break; else if (len < 64) { if (size < len + 1 || offs + len >= MAXSIZE - 1) break; memcpy (data, &((*this)[offs]), len); data += len; size -= len; offs += len; *data++ = '.'; size--; } else { int offs2 = ((len & 63) << 8) + (*this)[offs++]; data += decode_label (data, size, offs2); break; } } return data - orig; } ///////////////////////////////////////////////////////////////////////////// static u16 next_id () { static u16 dns_id = 0; // TODO: should be per-vpn if (!dns_id) dns_id = time (0); // the simplest lsfr with periodicity 65535 i could find dns_id = (dns_id << 1) | (((dns_id >> 1) ^ (dns_id >> 2) ^ (dns_id >> 4) ^ (dns_id >> 15)) & 1); return dns_id; } struct dns_rcv; struct dns_snd; struct dns_connection { connection *c; struct vpn *vpn; dns_cfg cfg; bool established; tstamp last_received; tstamp last_sent; double min_latency; double poll_interval, send_interval; vector rcvpq; byte_stream rcvdq; int rcvseq; int repseq; byte_stream snddq; int sndseq; inline void time_cb (ev::timer &w, int revents); ev::timer tw; void receive_rep (dns_rcv *r); dns_connection (connection *c); ~dns_connection (); }; struct dns_snd { dns_packet *pkt; tstamp timeout, sent; int retry; struct dns_connection *dns; int seqno; bool stdhdr; void gen_stream_req (int seqno, byte_stream &stream); void gen_syn_req (); dns_snd (dns_connection *dns); ~dns_snd (); }; dns_snd::dns_snd (dns_connection *dns) : dns (dns) { timeout = 0; retry = 0; seqno = 0; sent = ev_now (); stdhdr = false; pkt = new dns_packet; pkt->id = next_id (); } dns_snd::~dns_snd () { delete pkt; } static void append_domain (dns_packet &pkt, int &offs, const char *domain) { // add tunnel domain for (;;) { const char *end = strchr (domain, '.'); if (!end) end = domain + strlen (domain); int len = end - domain; pkt [offs++] = len; memcpy (pkt.at (offs), domain, len); offs += len; if (!*end) break; domain = end + 1; } } void dns_snd::gen_stream_req (int seqno, byte_stream &stream) { stdhdr = true; this->seqno = seqno; timeout = ev_now () + INITIAL_TIMEOUT; pkt->flags = htons (DEFAULT_CLIENT_FLAGS); pkt->qdcount = htons (1); int offs = 6*2; int dlen = MAX_DOMAIN_SIZE - (strlen (dns->c->conf->domain) + 2); // MAX_DOMAIN_SIZE is technically 255, but bind doesn't compress responses well, // so we need to have space for 2*MAX_DOMAIN_SIZE + header + extra char enc[256], *encp = enc; encode_header (enc, THISNODE->id, seqno); int datalen = cdc62.decode_len (dlen - (dlen + MAX_LBL_SIZE - 1) / MAX_LBL_SIZE - HDRSIZE); if (datalen > stream.size ()) datalen = stream.size (); int enclen = cdc62.encode (enc + HDRSIZE, stream.begin (), datalen) + HDRSIZE; stream.remove (datalen); while (enclen) { int lbllen = enclen < MAX_LBL_SIZE ? enclen : MAX_LBL_SIZE; (*pkt)[offs++] = lbllen; memcpy (pkt->at (offs), encp, lbllen); offs += lbllen; encp += lbllen; enclen -= lbllen; } append_domain (*pkt, offs, dns->c->conf->domain); (*pkt)[offs++] = 0; (*pkt)[offs++] = RR_TYPE_ANY >> 8; (*pkt)[offs++] = RR_TYPE_ANY; (*pkt)[offs++] = RR_CLASS_IN >> 8; (*pkt)[offs++] = RR_CLASS_IN; pkt->len = offs; } void dns_snd::gen_syn_req () { timeout = ev_now () + INITIAL_SYN_TIMEOUT; pkt->flags = htons (DEFAULT_CLIENT_FLAGS); pkt->qdcount = htons (1); int offs = 6 * 2; int elen = cdc26.encode ((char *)pkt->at (offs + 1), (u8 *)&dns->cfg, sizeof (dns_cfg)); assert (elen <= MAX_LBL_SIZE); (*pkt)[offs] = elen; offs += elen + 1; append_domain (*pkt, offs, dns->c->conf->domain); (*pkt)[offs++] = 0; (*pkt)[offs++] = RR_TYPE_A >> 8; (*pkt)[offs++] = RR_TYPE_A; (*pkt)[offs++] = RR_CLASS_IN >> 8; (*pkt)[offs++] = RR_CLASS_IN; pkt->len = offs; } struct dns_rcv { int seqno; dns_packet *pkt; // reply packet u8 data [MAXSIZE]; // actually part of the reply packet... int datalen; dns_rcv (int seqno, u8 *data, int datalen); ~dns_rcv (); }; dns_rcv::dns_rcv (int seqno, u8 *data, int datalen) : seqno (seqno), pkt (new dns_packet), datalen (datalen) { memcpy (this->data, data, datalen); } dns_rcv::~dns_rcv () { delete pkt; } ///////////////////////////////////////////////////////////////////////////// dns_connection::dns_connection (connection *c) : c (c) , rcvdq (MAX_BACKLOG * 2) , snddq (MAX_BACKLOG) { tw.set (this); vpn = c->vpn; established = false; rcvseq = repseq = sndseq = 0; last_sent = last_received = 0; poll_interval = 0.5; // starting here send_interval = 0.5; // starting rate min_latency = INITIAL_TIMEOUT; } dns_connection::~dns_connection () { for (vector::iterator i = rcvpq.begin (); i != rcvpq.end (); ++i) delete *i; } void dns_connection::receive_rep (dns_rcv *r) { if (r->datalen) { last_received = ev_now (); tw (); poll_interval = send_interval; } else { poll_interval *= 1.5; if (poll_interval > MAX_POLL_INTERVAL) poll_interval = MAX_POLL_INTERVAL; } rcvpq.push_back (r); redo: // find next packet for (vector::iterator i = rcvpq.end (); i-- != rcvpq.begin (); ) if (SEQNO_EQ (rcvseq, (*i)->seqno)) { //printf ("seqno eq %x %x\n", rcvseq, (*i)->seqno);//D // enter the packet into our input stream r = *i; // remove the oldest packet, look forward, as it's oldest first for (vector::iterator j = rcvpq.begin (); j != rcvpq.end (); ++j) if (SEQNO_EQ ((*j)->seqno, rcvseq - MAX_WINDOW)) { //printf ("seqno RR %x %x\n", (*j)->seqno, rcvseq - MAX_WINDOW);//D delete *j; rcvpq.erase (j); break; } rcvseq = (rcvseq + 1) & SEQNO_MASK; if (!rcvdq.put (r->data, r->datalen)) { // MUST never overflow, can be caused by data corruption, TODO slog (L_CRIT, "DNS: !rcvdq.put (r->data, r->datalen)"); c->dnsv4_reset_connection (); return; } while (vpn_packet *pkt = rcvdq.get ()) { sockinfo si; si.host = htonl (c->conf->id); si.port = 0; si.prot = PROT_DNSv4; vpn->recv_vpn_packet (pkt, si); delete pkt; } // check for further packets goto redo; } } void vpn::dnsv4_server (dns_packet &pkt) { u16 flags = ntohs (pkt.flags); int offs = 6 * 2; // skip header pkt.flags = htons (DEFAULT_SERVER_FLAGS | FLAG_RCODE_FORMERR); if (0 == (flags & (FLAG_RESPONSE | FLAG_OP_MASK)) && pkt.qdcount == htons (1)) { char qname [MAXSIZE]; int qlen = pkt.decode_label ((char *)qname, MAXSIZE - offs, offs); u16 qtype = pkt [offs++] << 8; qtype |= pkt [offs++]; u16 qclass = pkt [offs++] << 8; qclass |= pkt [offs++]; pkt.qdcount = htons (1); pkt.ancount = 0; pkt.nscount = 0; // should be self, as other nameservers reply like this pkt.arcount = 0; // a record for self, as other nameservers reply like this pkt.flags = htons (DEFAULT_SERVER_FLAGS | FLAG_RCODE_SERVFAIL); int dlen = strlen (THISNODE->domain); if (qclass == RR_CLASS_IN && qlen > dlen + 1 && !memcmp (qname + qlen - (dlen + 1), THISNODE->domain, dlen)) { // now generate reply pkt.ancount = htons (1); // one answer RR pkt.flags = htons (DEFAULT_SERVER_FLAGS | FLAG_RCODE_OK); if ((qtype == RR_TYPE_ANY || qtype == RR_TYPE_TXT || qtype == RR_TYPE_NULL) && qlen > dlen + 1 + HDRSIZE) { // correct class, domain: parse int client, seqno; decode_header (qname, client, seqno); u8 data[MAXSIZE]; int datalen = cdc62.decode (data, qname + HDRSIZE, qlen - (dlen + 1 + HDRSIZE)); if (0 < client && client <= conns.size ()) { connection *c = conns [client - 1]; dns_connection *dns = c->dns; dns_rcv *rcv; if (dns) { for (vector::iterator i = dns->rcvpq.end (); i-- != dns->rcvpq.begin (); ) if (SEQNO_EQ ((*i)->seqno, seqno)) { // already seen that request: simply reply with the cached reply dns_rcv *r = *i; slog (L_DEBUG, "DNS: duplicate packet received ID %d, SEQ %d", htons (r->pkt->id), seqno); // refresh header & id, as the retry count could have changed memcpy (r->pkt->at (6 * 2 + 1), pkt.at (6 * 2 + 1), HDRSIZE); r->pkt->id = pkt.id; memcpy (pkt.at (0), r->pkt->at (0), offs = r->pkt->len); goto duplicate_request; } // new packet, queue rcv = new dns_rcv (seqno, data, datalen); dns->receive_rep (rcv); } { pkt [offs++] = 0xc0; pkt [offs++] = 6 * 2; // refer to name in query section int rtype = dns ? dns->cfg.rrtype : RR_TYPE_A; pkt [offs++] = rtype >> 8; pkt [offs++] = rtype; // type pkt [offs++] = RR_CLASS_IN >> 8; pkt [offs++] = RR_CLASS_IN; // class pkt [offs++] = 0; pkt [offs++] = 0; pkt [offs++] = 0; pkt [offs++] = dns ? dns->cfg.def_ttl : 0; // TTL int rdlen_offs = offs += 2; if (dns) { int dlen = ntohs (dns->cfg.max_size) - offs; // bind doesn't compress well, so reduce further by one label length dlen -= qlen; // only put data into in-order sequence packets, if // we receive out-of-order packets we generate empty // replies //printf ("%d - %d & %x (=%d) < %d\n", seqno, dns->repseq, SEQNO_MASK, (seqno - dns->repseq) & SEQNO_MASK, MAX_WINDOW);//D if (((seqno - dns->repseq) & SEQNO_MASK) <= MAX_WINDOW) { dns->repseq = seqno; while (dlen > 1 && !dns->snddq.empty ()) { int txtlen = dlen <= 255 ? dlen - 1 : 255; if (txtlen > dns->snddq.size ()) txtlen = dns->snddq.size (); pkt[offs++] = txtlen; memcpy (pkt.at (offs), dns->snddq.begin (), txtlen); offs += txtlen; dns->snddq.remove (txtlen); dlen -= txtlen + 1; } } // avoid completely empty TXT rdata if (offs == rdlen_offs) pkt[offs++] = 0; slog (L_NOISE, "DNS: snddq %d", dns->snddq.size ()); } else { // send RST pkt [offs++] = CMD_IP_1; pkt [offs++] = CMD_IP_2; pkt [offs++] = CMD_IP_3; pkt [offs++] = CMD_IP_RST; } int rdlen = offs - rdlen_offs; pkt [rdlen_offs - 2] = rdlen >> 8; pkt [rdlen_offs - 1] = rdlen; if (dns) { // now update dns_rcv copy rcv->pkt->len = offs; memcpy (rcv->pkt->at (0), pkt.at (0), offs); } } duplicate_request: ; } else pkt.flags = htons (DEFAULT_SERVER_FLAGS | FLAG_RCODE_FORMERR); } else if (qtype == RR_TYPE_A && qlen > dlen + 1 + cdc26.encode_len (sizeof (dns_cfg))) { dns_cfg cfg; cdc26.decode ((u8 *)&cfg, qname, cdc26.encode_len (sizeof (dns_cfg))); int client = ntohs (cfg.client); pkt [offs++] = 0xc0; pkt [offs++] = 6 * 2; // refer to name in query section pkt [offs++] = RR_TYPE_A >> 8; pkt [offs++] = RR_TYPE_A; // type pkt [offs++] = RR_CLASS_IN >> 8; pkt [offs++] = RR_CLASS_IN; // class pkt [offs++] = 0; pkt [offs++] = 0; pkt [offs++] = 0; pkt [offs++] = cfg.def_ttl; // TTL pkt [offs++] = 0; pkt [offs++] = 4; // rdlength slog (L_INFO, _("DNS: client %d connects"), client); pkt [offs++] = CMD_IP_1; pkt [offs++] = CMD_IP_2; pkt [offs++] = CMD_IP_3; pkt [offs++] = CMD_IP_REJ; if (0 < client && client <= conns.size ()) { connection *c = conns [client - 1]; if (cfg.valid ()) { pkt [offs - 1] = CMD_IP_SYN; delete c->dns; c->dns = new dns_connection (c); c->dns->cfg = cfg; } } } } pkt.len = offs; } } void vpn::dnsv4_client (dns_packet &pkt) { u16 flags = ntohs (pkt.flags); int offs = 6 * 2; // skip header pkt.qdcount = ntohs (pkt.qdcount); pkt.ancount = ntohs (pkt.ancount); // go through our request list and find the corresponding request for (vector::iterator i = dns_sndpq.begin (); i != dns_sndpq.end (); ++i) if ((*i)->pkt->id == pkt.id) { dns_connection *dns = (*i)->dns; connection *c = dns->c; int seqno = (*i)->seqno; u8 data[MAXSIZE], *datap = data; //printf ("rcv pkt %x\n", seqno);//D if ((*i)->retry) { dns->send_interval *= 1.01; if (dns->send_interval > MAX_SEND_INTERVAL) dns->send_interval = MAX_SEND_INTERVAL; } else { #if 0 dns->send_interval *= 0.999; #endif // the latency surely puts an upper bound on // the minimum send interval double latency = ev_now () - (*i)->sent; if (latency < dns->min_latency) dns->min_latency = latency; if (dns->send_interval > dns->min_latency * conf.dns_overlap_factor) dns->send_interval = dns->min_latency * conf.dns_overlap_factor; if (dns->send_interval < conf.dns_send_interval) dns->send_interval = conf.dns_send_interval; } delete *i; dns_sndpq.erase (i); if (flags & FLAG_RESPONSE && !(flags & FLAG_OP_MASK)) { char qname[MAXSIZE]; while (pkt.qdcount-- && offs < MAXSIZE - 4) { int qlen = pkt.decode_label ((char *)qname, MAXSIZE - offs, offs); offs += 4; // skip qtype, qclass } while (pkt.ancount-- && offs < MAXSIZE - 10 && datap) { int qlen = pkt.decode_label ((char *)qname, MAXSIZE - offs, offs); u16 qtype = pkt [offs++] << 8; qtype |= pkt [offs++]; u16 qclass = pkt [offs++] << 8; qclass |= pkt [offs++]; u32 ttl = pkt [offs++] << 24; ttl |= pkt [offs++] << 16; ttl |= pkt [offs++] << 8; ttl |= pkt [offs++]; u16 rdlen = pkt [offs++] << 8; rdlen |= pkt [offs++]; if (qtype == RR_TYPE_NULL || qtype == RR_TYPE_TXT) { if (rdlen <= MAXSIZE - offs) { // decode bytes, finally while (rdlen) { int txtlen = pkt [offs++]; assert (txtlen + offs < MAXSIZE - 1); memcpy (datap, pkt.at (offs), txtlen); datap += txtlen; offs += txtlen; rdlen -= txtlen + 1; } } } else if (qtype == RR_TYPE_A) { u8 ip [4]; ip [0] = pkt [offs++]; ip [1] = pkt [offs++]; ip [2] = pkt [offs++]; ip [3] = pkt [offs++]; if (ip [0] == CMD_IP_1 && ip [1] == CMD_IP_2 && ip [2] == CMD_IP_3) { slog (L_TRACE, _("DNS: got tunnel meta command %02x"), ip [3]); if (ip [3] == CMD_IP_RST) { slog (L_DEBUG, _("DNS: got tunnel RST request")); c->dnsv4_reset_connection (); } else if (ip [3] == CMD_IP_SYN) { slog (L_DEBUG, _("DNS: got tunnel SYN reply, server likes us.")); dns->established = true; } else if (ip [3] == CMD_IP_REJ) slog (L_ERR, _("DNS: got tunnel REJ reply, server does not like us.")); else slog (L_INFO, _("DNS: got unknown meta command %02x"), ip [3]); } else slog (L_INFO, _("DNS: got spurious a record %d.%d.%d.%d"), ip [0], ip [1], ip [2], ip [3]); return; } int client, rseqno; decode_header (qname, client, rseqno); if (client != THISNODE->id) { slog (L_INFO, _("DNS: got dns tunnel response with wrong clientid, ignoring")); datap = 0; } else if (rseqno != seqno) { slog (L_DEBUG, _("DNS: got dns tunnel response with wrong seqno, badly caching nameserver?")); datap = 0; } } } // todo: pkt now used if (datap) dns->receive_rep (new dns_rcv (seqno, data, datap - data)); break; } } void vpn::dnsv4_ev (ev::io &w, int revents) { if (revents & EV_READ) { dns_packet *pkt = new dns_packet; struct sockaddr_in sa; socklen_t sa_len = sizeof (sa); pkt->len = recvfrom (w.fd, pkt->at (0), MAXSIZE, 0, (sockaddr *)&sa, &sa_len); if (pkt->len > 0) { if (ntohs (pkt->flags) & FLAG_RESPONSE) dnsv4_client (*pkt); else { dnsv4_server (*pkt); sendto (w.fd, pkt->at (0), pkt->len, 0, (sockaddr *)&sa, sa_len); } delete pkt; } } } bool vpn::send_dnsv4_packet (vpn_packet *pkt, const sockinfo &si, int tos) { int client = ntohl (si.host); assert (0 < client && client <= conns.size ()); connection *c = conns [client - 1]; if (!c->dns) c->dns = new dns_connection (c); if (c->dns->snddq.put (pkt)) c->dns->tw (); // always return true even if the buffer overflows return true; } void connection::dnsv4_reset_connection () { //delete dns; dns = 0; //TODO } #define NEXT(w) do { if (next > (w)) next = w; } while (0) void dns_connection::time_cb (ev::timer &w, int revents) { // servers have to be polled if (THISNODE->dns_port) return; // check for timeouts and (re)transmit tstamp next = ev::now () + poll_interval; dns_snd *send = 0; for (vector::iterator i = vpn->dns_sndpq.begin (); i != vpn->dns_sndpq.end (); ++i) { dns_snd *r = *i; if (r->timeout <= ev_now ()) { if (!send) { send = r; r->retry++; r->timeout = ev_now () + (r->retry * min_latency * conf.dns_timeout_factor); //printf ("RETRY %x (%d, %f)\n", r->seqno, r->retry, r->timeout - ev_now ());//D // the following code changes the query section a bit, forcing // the forwarder to generate a new request if (r->stdhdr) encode_header ((char *)r->pkt->at (6 * 2 + 1), THISNODE->id, r->seqno, r->retry); } } else NEXT (r->timeout); } if (!send) { // generate a new packet, if wise if (!established) { if (vpn->dns_sndpq.empty ()) { send = new dns_snd (this); cfg.reset (THISNODE->id); send->gen_syn_req (); } } else if (vpn->dns_sndpq.size () < conf.dns_max_outstanding && !SEQNO_EQ (rcvseq, sndseq - (MAX_WINDOW - 1))) { if (last_sent + send_interval <= ev_now ()) { //printf ("sending data request etc.\n"); //D if (!snddq.empty () || last_received + 1. > ev_now ()) { poll_interval = send_interval; NEXT (ev_now () + send_interval); } send = new dns_snd (this); send->gen_stream_req (sndseq, snddq); send->timeout = ev_now () + min_latency * conf.dns_timeout_factor; //printf ("SEND %x (%f)\n", send->seqno, send->timeout - ev_now (), min_latency, conf.dns_timeout_factor);//D sndseq = (sndseq + 1) & SEQNO_MASK; } else NEXT (last_sent + send_interval); } if (send) vpn->dns_sndpq.push_back (send); } if (send) { last_sent = ev_now (); sendto (vpn->dnsv4_fd, send->pkt->at (0), send->pkt->len, 0, vpn->dns_forwarder.sav4 (), vpn->dns_forwarder.salenv4 ()); } slog (L_NOISE, "DNS: pi %f si %f N %f (%d:%d %d)", poll_interval, send_interval, next - ev_now (), vpn->dns_sndpq.size (), snddq.size (), rcvpq.size ()); // TODO: no idea when this happens, but when next < ev_now (), we have a problem // doesn't seem to happen anymore if (next < ev_now () + 0.001) next = ev_now () + 0.1; w.start (next - ev_now ()); } #endif