411 lines
10 KiB
C
411 lines
10 KiB
C
|
/*
|
||
|
* $Id$
|
||
|
*
|
||
|
Copyright (c) 2015-2016 Chung, Hyung-Hwan. All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions
|
||
|
are met:
|
||
|
1. Redistributions of source code must retain the above copyright
|
||
|
notice, this list of conditions and the following disclaimer.
|
||
|
2. Redistributions in binary form must reproduce the above copyright
|
||
|
notice, this list of conditions and the following disclaimer in the
|
||
|
documentation and/or other materials provided with the distribution.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES
|
||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include <mio-dns.h>
|
||
|
#include <mio-sck.h>
|
||
|
#include "mio-prv.h"
|
||
|
|
||
|
/* ----------------------------------------------------------------------- */
|
||
|
|
||
|
#define DN_AT_END(ptr) (ptr[0] == '\0' || (ptr[0] == '.' && ptr[1] == '\0'))
|
||
|
|
||
|
static mio_oow_t to_dn (const mio_bch_t* str, mio_uint8_t* buf, mio_oow_t bufsz)
|
||
|
{
|
||
|
mio_uint8_t* bp = buf, * be = buf + bufsz;
|
||
|
|
||
|
/*MIO_ASSERT (MIO_SIZEOF(mio_uint8_t) == MIO_SIZEOF(mio_bch_t));*/
|
||
|
|
||
|
if (!DN_AT_END(str))
|
||
|
{
|
||
|
mio_uint8_t* lp;
|
||
|
mio_oow_t len;
|
||
|
const mio_bch_t* seg;
|
||
|
const mio_bch_t* cur = str - 1;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if (bp < be) lp = bp++;
|
||
|
else lp = MIO_NULL;
|
||
|
|
||
|
seg = ++cur;
|
||
|
while (*cur != '\0' && *cur != '.')
|
||
|
{
|
||
|
if (bp < be) *bp++ = *cur;
|
||
|
cur++;
|
||
|
}
|
||
|
len = cur - seg;
|
||
|
if (len <= 0 || len > 63) return 0;
|
||
|
|
||
|
if (lp) *lp = (mio_uint8_t)len;
|
||
|
}
|
||
|
while (!DN_AT_END(cur));
|
||
|
}
|
||
|
|
||
|
if (bp < be) *bp++ = 0;
|
||
|
|
||
|
/* the length includes the terminating zero. */
|
||
|
return bp - buf;
|
||
|
}
|
||
|
|
||
|
static mio_oow_t dn_length (mio_uint8_t* ptr, mio_oow_t len)
|
||
|
{
|
||
|
mio_uint8_t* curptr;
|
||
|
mio_oow_t curlen, seglen;
|
||
|
|
||
|
curptr = ptr;
|
||
|
curlen = len;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if (curlen <= 0) return 0;
|
||
|
|
||
|
seglen = *curptr++;
|
||
|
curlen = curlen - 1;
|
||
|
if (seglen == 0) break;
|
||
|
else if (seglen > curlen || seglen > 63) return 0;
|
||
|
|
||
|
curptr += seglen;
|
||
|
curlen -= seglen;
|
||
|
}
|
||
|
while (1);
|
||
|
|
||
|
return curptr - ptr;
|
||
|
}
|
||
|
|
||
|
/* ----------------------------------------------------------------------- */
|
||
|
|
||
|
struct mio_dnss_t
|
||
|
{
|
||
|
mio_t* mio;
|
||
|
};
|
||
|
|
||
|
struct mio_dnsc_t
|
||
|
{
|
||
|
mio_t* mio;
|
||
|
mio_dev_sck_t* sck;
|
||
|
mio_sckaddr_t serveraddr;
|
||
|
mio_oow_t seq;
|
||
|
};
|
||
|
|
||
|
struct mio_dns_req_t
|
||
|
{
|
||
|
mio_dns_msg_t* msg;
|
||
|
/* source address */
|
||
|
|
||
|
/*mio_dns_req_t* prev;
|
||
|
mio_dns_req_t* next;*/
|
||
|
};
|
||
|
typedef struct mio_dns_req_t mio_dns_req_t;
|
||
|
|
||
|
|
||
|
static int dns_on_read (mio_dev_sck_t* dev, const void* data, mio_iolen_t dlen, const mio_sckaddr_t* srcaddr)
|
||
|
{
|
||
|
mio_t* mio = dev->mio;
|
||
|
mio_dns_msg_t* msg;
|
||
|
mio_uint16_t id;
|
||
|
|
||
|
if (dlen < MIO_SIZEOF(*msg))
|
||
|
{
|
||
|
MIO_DEBUG0 (mio, "dns packet too small from ....\n"); /* TODO: add source packet */
|
||
|
return 0; /* drop */
|
||
|
}
|
||
|
msg = (mio_dns_msg_t*)data;
|
||
|
if (!msg->qr) return 0; /* drop request */
|
||
|
|
||
|
id = mio_ntoh16(msg->id);
|
||
|
/* if id doesn't matche one of the pending requests sent, drop */
|
||
|
|
||
|
MIO_DEBUG1 (mio, "received dns response...id %d\n", id);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int dns_on_write (mio_dev_sck_t* dev, mio_iolen_t wrlen, void* wrctx, const mio_sckaddr_t* dstaddr)
|
||
|
{
|
||
|
mio_t* mio = dev->mio;
|
||
|
|
||
|
MIO_DEBUG0 (mio, "sent dns request...\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void dns_on_connect (mio_dev_sck_t* dev)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void dns_on_disconnect (mio_dev_sck_t* dev)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
mio_dnsc_t* mio_dnsc_start (mio_t* mio)
|
||
|
{
|
||
|
mio_dnsc_t* dnsc = MIO_NULL;
|
||
|
mio_dev_sck_make_t minfo;
|
||
|
|
||
|
dnsc = (mio_dnsc_t*)mio_callocmem(mio, MIO_SIZEOF(*dnsc));
|
||
|
if (!dnsc) goto oops;
|
||
|
|
||
|
dnsc->mio = mio;
|
||
|
|
||
|
MIO_MEMSET (&minfo, 0, MIO_SIZEOF(minfo));
|
||
|
minfo.type = MIO_DEV_SCK_UDP4; /* or UDP6 depending on the binding address */
|
||
|
minfo.on_write = dns_on_write;
|
||
|
minfo.on_read = dns_on_read;
|
||
|
minfo.on_connect = dns_on_connect;
|
||
|
minfo.on_disconnect = dns_on_disconnect;
|
||
|
dnsc->sck = mio_dev_sck_make(mio, 0, &minfo);
|
||
|
if (!dnsc->sck) goto oops;
|
||
|
|
||
|
/* bind if requested */
|
||
|
/*if (mio_dev_sck_bind(dev, ....) <= -1) goto oops;*/
|
||
|
{
|
||
|
mio_uint32_t ia = 0x01010101; /* 1.1.1.1 */
|
||
|
mio_sckaddr_initforip4 (&dnsc->serveraddr, 53, (mio_ip4addr_t*)&ia);
|
||
|
}
|
||
|
|
||
|
return dnsc;
|
||
|
|
||
|
oops:
|
||
|
if (dnsc)
|
||
|
{
|
||
|
if (dnsc->sck) mio_dev_sck_kill (dnsc->sck);
|
||
|
}
|
||
|
return MIO_NULL;
|
||
|
}
|
||
|
|
||
|
void mio_dnsc_stop (mio_dnsc_t* dnsc)
|
||
|
{
|
||
|
mio_t* mio = dnsc->mio;
|
||
|
mio_freemem (mio, dnsc);
|
||
|
}
|
||
|
|
||
|
static void release_req_msg (mio_dnsc_t* dnsc, mio_dns_msg_t* msg)
|
||
|
{
|
||
|
mio_t* mio = dnsc->mio;
|
||
|
void* msgbuf;
|
||
|
|
||
|
/* TODO: add it to the free msg list instead of just freeing it. */
|
||
|
msgbuf = ((mio_uint8_t*)msg - MIO_SIZEOF(mio_oow_t));
|
||
|
mio_freemem (mio, msgbuf);
|
||
|
}
|
||
|
|
||
|
static mio_dns_msg_t* build_req_msg (mio_dnsc_t* dnsc, mio_dns_bqrr_t* qrr, mio_oow_t qrr_count, mio_dns_brrr_t* rrr, mio_oow_t rrr_count, mio_oow_t* xmsglen)
|
||
|
{
|
||
|
mio_t* mio = dnsc->mio;
|
||
|
mio_oow_t dnlen, msgbufsz, i;
|
||
|
void* msgbuf;
|
||
|
mio_dns_msg_t* msg;
|
||
|
mio_uint8_t* dn;
|
||
|
mio_dns_qrrtr_t* qrrtr;
|
||
|
mio_dns_rrrtr_t* rrrtr;
|
||
|
int rrr_sect;
|
||
|
|
||
|
msgbufsz = MIO_SIZEOF(msgbufsz) + MIO_SIZEOF(*msg);
|
||
|
|
||
|
for (i = 0; i < qrr_count; i++)
|
||
|
{
|
||
|
/* <length>segmnet<length>segment<zero>.
|
||
|
* if the input has the ending period(e.g. mio.lib.), the dn length is namelen + 1.
|
||
|
* if the input doesn't have the ending period(e.g. mio.lib) . the dn length is namelen + 2. */
|
||
|
msgbufsz += mio_count_bcstr(qrr[i].qname) + 2 + MIO_SIZEOF(*qrrtr);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < rrr_count; i++)
|
||
|
{
|
||
|
msgbufsz += mio_count_bcstr(rrr[i].qname) + 2 + MIO_SIZEOF(*rrrtr) + rrr[i].dlen;
|
||
|
}
|
||
|
|
||
|
msgbufsz = MIO_ALIGN_POW2(msgbufsz, 64);
|
||
|
|
||
|
/* TODO: msg buffer reuse */
|
||
|
msgbuf = mio_callocmem(mio, msgbufsz);
|
||
|
if (!msgbuf) return MIO_NULL;
|
||
|
|
||
|
*(mio_oow_t*)msgbuf = msgbufsz; /* record the buffer size in the first word */
|
||
|
msg = (mio_dns_msg_t*)(msgbuf + MIO_SIZEOF(mio_oow_t)); /* actual message begins the after the size word */
|
||
|
|
||
|
dn = (mio_uint8_t*)(msg + 1);
|
||
|
for (i = 0; i < qrr_count; i++)
|
||
|
{
|
||
|
/* dnlen includes the ending <zero> */
|
||
|
dnlen = to_dn(qrr[i].qname, dn, mio_count_bcstr(qrr[i].qname) + 2);
|
||
|
if (dnlen <= 0)
|
||
|
{
|
||
|
release_req_msg (dnsc, msg);
|
||
|
mio_seterrbfmt (mio, MIO_EINVAL, "invalid domain name - %hs", qrr[i].qname);
|
||
|
return MIO_NULL;
|
||
|
}
|
||
|
|
||
|
qrrtr = (mio_dns_qrrtr_t*)(dn + dnlen);
|
||
|
qrrtr->qtype = mio_hton16(qrr[i].qtype);
|
||
|
qrrtr->qclass = mio_hton16(qrr[i].qclass);
|
||
|
|
||
|
dn = (mio_uint8_t*)(qrrtr + 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
for (rrr_sect = MIO_DNS_RRR_PART_ANSWER; rrr_sect <= MIO_DNS_RRR_PART_ADDITIONAL;)
|
||
|
{
|
||
|
mio_oow_t match_count = 0;
|
||
|
for (i = 0; i < rrr_count; i++)
|
||
|
{
|
||
|
if (rrr[i].part == rrr_sect)
|
||
|
{
|
||
|
dnlen = to_dn(rrr[i].qname, dn, mio_count_bcstr(rrr[i].qname) + 2);
|
||
|
if (dnlen <= 0)
|
||
|
{
|
||
|
release_req_msg (dnsc, msg);
|
||
|
mio_seterrbfmt (mio, MIO_EINVAL, "invalid domain name - %hs", rrr[i].qname);
|
||
|
return MIO_NULL;
|
||
|
}
|
||
|
|
||
|
rrrtr = (mio_dns_rrrtr_t*)(dn + dnlen);
|
||
|
rrrtr->qtype = mio_hton16(rrr[i].qtype);
|
||
|
rrrtr->qclass = mio_hton16(rrr[i].qclass);
|
||
|
rrrtr->ttl = mio_hton32(rrr[i].ttl);
|
||
|
rrrtr->dlen = mio_hton32(rrr[i].dlen);
|
||
|
if (rrr[i].dlen > 0) MIO_MEMCPY (rrrtr + 1, rrr[i].dptr, rrr[i].dlen);
|
||
|
|
||
|
dn = (mio_uint8_t*)(rrrtr + 1) + rrr[i].dlen;
|
||
|
|
||
|
match_count++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rrr_sect = rrr_sect + 1;
|
||
|
((mio_dns_msg_alt_t*)msg)->rrcount[rrr_sect] = mio_hton16(match_count);
|
||
|
}
|
||
|
|
||
|
if (rrr_count <= 0)
|
||
|
{
|
||
|
msg->qr = 0;
|
||
|
msg->opcode = MIO_DNS_OPCODE_QUERY;
|
||
|
msg->qdcount = mio_hton16(qrr_count);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
msg->qr = 1;
|
||
|
msg->opcode = MIO_DNS_OPCODE_QUERY;
|
||
|
msg->qdcount = mio_hton16(qrr_count);
|
||
|
}
|
||
|
|
||
|
*xmsglen = dn - (mio_uint8_t*)msg;
|
||
|
return msg; /* return the pointer to the beginning of the actual message */
|
||
|
}
|
||
|
|
||
|
|
||
|
static MIO_INLINE int send_req_with_single_rr (mio_dnsc_t* dnsc, mio_dns_bqrr_t* qrr)
|
||
|
{
|
||
|
mio_dns_msg_t* msg;
|
||
|
mio_oow_t msglen;
|
||
|
|
||
|
msg = build_req_msg(dnsc, qrr, 1, MIO_NULL, 0, &msglen);
|
||
|
if (!msg) return -1;
|
||
|
|
||
|
msg->rd = 1;
|
||
|
msg->id = mio_hton16(dnsc->seq);
|
||
|
dnsc->seq++;
|
||
|
|
||
|
/* TODO: if response, set msg->ra, set msg->id with id in request */
|
||
|
|
||
|
mio_dev_sck_write(dnsc->sck, msg, msglen, MIO_NULL, &dnsc->serveraddr); /* TODO: optionally, override dnsc->serveraddr */
|
||
|
|
||
|
release_req_msg (dnsc, msg);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int mio_dnsc_sendreq (mio_dnsc_t* dnsc, mio_dns_bqrr_t* qrr, mio_oow_t count)
|
||
|
{
|
||
|
#if 1
|
||
|
mio_oow_t i;
|
||
|
|
||
|
for (i = 0; i < count; i++)
|
||
|
{
|
||
|
if (send_req_with_single_rr(dnsc, &qrr[i]) <= -1) return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
#else
|
||
|
mio_dns_msg_t* msg;
|
||
|
mio_oow_t msglen;
|
||
|
|
||
|
msg = build_req_msg(dnsc, qrr, count, &msglen);
|
||
|
if (!msg) return -1;
|
||
|
|
||
|
msg->rd = 1;
|
||
|
msg->id = mio_hton16(dnsc->seq);
|
||
|
dnsc->seq++;
|
||
|
/* TODO: if response, set msg->ra, set msg->id with id in request */
|
||
|
|
||
|
mio_dev_sck_write(dnsc->sck, msg, msglen, MIO_NULL, &dnsc->serveraddr); /* TODO: optionally, override dnsc->serveraddr */
|
||
|
|
||
|
release_req_msg (dnsc, msg);
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int mio_dnsc_sendrep (mio_dnsc_t* dnsc, mio_dns_bqrr_t* qrr, mio_oow_t qrr_count, mio_dns_brrr_t* rrr, mio_oow_t rrr_count)
|
||
|
{
|
||
|
mio_dns_msg_t* msg;
|
||
|
mio_oow_t msglen;
|
||
|
|
||
|
msg = build_req_msg(dnsc, qrr, qrr_count, rrr, rrr_count, &msglen);
|
||
|
if (!msg) return -1;
|
||
|
|
||
|
msg->rd = 1;
|
||
|
msg->ra = 1;
|
||
|
/* TODO: msg->id = copy from request */
|
||
|
|
||
|
mio_dev_sck_write(dnsc->sck, msg, msglen, MIO_NULL, &dnsc->serveraddr); /* TODO: optionally, override dnsc->serveraddr */
|
||
|
|
||
|
release_req_msg (dnsc, msg);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
static void dnss_on_request (mio_dnss_t* dnss, mio_dns_req_t* req)
|
||
|
{
|
||
|
mio_dnss_reply (dnss, rep); /* reply send timeout??? */
|
||
|
}
|
||
|
|
||
|
/* ----------------------------------------------------------------------- */
|
||
|
|
||
|
|
||
|
int main ()
|
||
|
{
|
||
|
mio = mio_open ();
|
||
|
|
||
|
dnss1 = mio_dnss_start(mio, "1.1.1.1:53,2.2.2.2:53", dnss_on_request);
|
||
|
mio_dnss_end (dnss1);
|
||
|
|
||
|
dnsc1 = mio_dnsc_start (mio, "8.8.8.8:53,1.1.1.1:53"); /* option - send to all, send one by one */
|
||
|
mio_dnsc_resolve (dnsc1, "A:www.google.com", dnsc_on_response);
|
||
|
mio_dnsc_end (dnsc1);
|
||
|
|
||
|
mio_close (mio);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif
|