separated dnc code from dns.c to dns-cli.c

This commit is contained in:
2020-02-21 04:21:16 +00:00
parent 6787ee7841
commit c620afe4c9
4 changed files with 600 additions and 561 deletions

View File

@ -24,41 +24,9 @@
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <mio-dns.h>
#include <mio-sck.h>
#include "mio-dns.h"
#include "mio-prv.h"
#include <netinet/in.h>
struct mio_svc_dns_t
{
MIO_SVC_HEADERS;
/*MIO_DNS_SVC_HEADERS;*/
};
struct mio_svc_dnc_t
{
MIO_SVC_HEADERS;
/*MIO_DNS_SVC_HEADERS;*/
mio_dev_sck_t* udp_sck;
mio_dev_sck_t* tcp_sck;
mio_skad_t serv_addr;
mio_ntime_t send_tmout;
mio_ntime_t reply_tmout; /* default reply timeout */
mio_oow_t reply_tmout_max_tries;
mio_oow_t seq;
mio_dns_msg_t* pending_req;
};
struct dnc_sck_xtn_t
{
mio_svc_dnc_t* dnc;
};
typedef struct dnc_sck_xtn_t dnc_sck_xtn_t;
/* ----------------------------------------------------------------------- */
#define DN_AT_END(ptr) (ptr[0] == '\0' || (ptr[0] == '.' && ptr[1] == '\0'))
@ -126,521 +94,6 @@ static mio_oow_t dn_length (mio_uint8_t* ptr, mio_oow_t len)
return curptr - ptr;
}
/* ----------------------------------------------------------------------- */
struct dnc_dns_msg_xtn_t
{
mio_dev_t* dev;
mio_tmridx_t rtmridx;
mio_dns_msg_t* prev;
mio_dns_msg_t* next;
mio_skad_t servaddr;
mio_svc_dnc_on_reply_t on_reply;
mio_ntime_t wtmout;
mio_ntime_t rtmout;
int rmaxtries; /* maximum number of tries to receive a reply */
int rtries; /* number of tries made so far */
};
typedef struct dnc_dns_msg_xtn_t dnc_dns_msg_xtn_t;
#if defined(MIO_HAVE_INLINE)
static MIO_INLINE dnc_dns_msg_xtn_t* dnc_dns_msg_getxtn(mio_dns_msg_t* msg) { return (dnc_dns_msg_xtn_t*)((mio_uint8_t*)mio_dns_msg_to_pkt(msg) + msg->pktalilen); }
#else
# define dnc_dns_msg_getxtn(msg) ((dnc_dns_msg_xtn_t*)((mio_uint8_t*)mio_dns_msg_to_pkt(msg) + msg->pktalilen))
#endif
static mio_dns_msg_t* build_dns_msg (mio_svc_dnc_t* dnc, mio_dns_bhdr_t* bdns, mio_dns_bqr_t* qr, mio_oow_t qr_count, mio_dns_brr_t* rr, mio_oow_t rr_count, mio_dns_bedns_t* edns, mio_oow_t xtnsize)
{
mio_dns_msg_t* msg;
dnc_dns_msg_xtn_t* msgxtn;
msg = mio_dns_make_msg(dnc->mio, bdns, qr, qr_count, rr, rr_count, edns, MIO_SIZEOF(*msgxtn) + xtnsize);
if (msg)
{
if (bdns->id < 0)
{
mio_dns_pkt_t* pkt = mio_dns_msg_to_pkt(msg);
pkt->id = mio_hton16(dnc->seq);
dnc->seq++;
}
msgxtn = dnc_dns_msg_getxtn(msg);
msgxtn->dev = (mio_dev_t*)dnc->udp_sck;
msgxtn->rtmridx = MIO_TMRIDX_INVALID;
}
return msg;
}
static MIO_INLINE void chain_pending_dns_reqmsg (mio_svc_dnc_t* dnc, mio_dns_msg_t* msg)
{
if (dnc->pending_req)
{
dnc_dns_msg_getxtn(dnc->pending_req)->prev = msg;
dnc_dns_msg_getxtn(msg)->next = dnc->pending_req;
}
dnc->pending_req = msg;
}
static MIO_INLINE void unchain_pending_dns_reqmsg (mio_svc_dnc_t* dnc, mio_dns_msg_t* msg)
{
dnc_dns_msg_xtn_t* msgxtn = dnc_dns_msg_getxtn(msg);
if (msgxtn->next) dnc_dns_msg_getxtn(msgxtn->next)->prev = msgxtn->prev;
if (msgxtn->prev) dnc_dns_msg_getxtn(msgxtn->prev)->next = msgxtn->next;
else dnc->pending_req = msgxtn->next;
}
static void release_dns_msg (mio_svc_dnc_t* dnc, mio_dns_msg_t* msg)
{
mio_t* mio = dnc->mio;
dnc_dns_msg_xtn_t* msgxtn = dnc_dns_msg_getxtn(msg);
MIO_DEBUG1 (mio, "releasing dns msg %d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id));
if (msg == dnc->pending_req || msgxtn->next || msgxtn->prev)
{
/* it's chained in the pending request. unchain it */
unchain_pending_dns_reqmsg (dnc, msg);
}
/* TODO: add it to the free msg list instead of just freeing it. */
if (msgxtn->rtmridx != MIO_TMRIDX_INVALID)
{
mio_deltmrjob (mio, msgxtn->rtmridx);
MIO_ASSERT (mio, msgxtn->rtmridx == MIO_TMRIDX_INVALID);
}
mio_dns_free_msg (dnc->mio, msg);
}
static int on_udp_read (mio_dev_sck_t* dev, const void* data, mio_iolen_t dlen, const mio_skad_t* srcaddr)
{
mio_t* mio = dev->mio;
mio_svc_dnc_t* dnc = ((dnc_sck_xtn_t*)mio_dev_sck_getxtn(dev))->dnc;
mio_dns_pkt_t* pkt;
mio_dns_msg_t* reqmsg;
mio_uint16_t id;
if (MIO_UNLIKELY(dlen < MIO_SIZEOF(*pkt)))
{
MIO_DEBUG0 (mio, "dns packet too small from ....\n"); /* TODO: add source packet */
return 0; /* drop */
}
pkt = (mio_dns_pkt_t*)data;
if (!pkt->qr)
{
MIO_DEBUG0 (mio, "dropping dns request received ...\n"); /* TODO: add source info */
return 0; /* drop request */
}
id = mio_ntoh16(pkt->id);
/* if id doesn't match one of the pending requests sent, drop it */
/* TODO: improve performance of dns response matching*/
reqmsg = dnc->pending_req;
while (reqmsg)
{
mio_dns_pkt_t* reqpkt = mio_dns_msg_to_pkt(reqmsg);
dnc_dns_msg_xtn_t* reqmsgxtn = dnc_dns_msg_getxtn(reqmsg);
if (dev == (mio_dev_sck_t*)reqmsgxtn->dev && pkt->id == reqpkt->id && mio_equal_skads(&reqmsgxtn->servaddr, srcaddr, 0))
{
if (pkt->tc) /* truncated */
{
/* TODO: add an option for this behavior */
/* TODO: switch to tc ... send the same request over tcp... */
}
MIO_DEBUG1 (mio, "received dns response...id %d\n", id);
if (MIO_LIKELY(reqmsgxtn->on_reply))
reqmsgxtn->on_reply (dnc, reqmsg, MIO_ENOERR, data, dlen);
release_dns_msg (dnc, reqmsg);
return 0;
}
reqmsg = reqmsgxtn->next;
}
/* the response id didn't match the ID of pending requests - need to wait longer? */
MIO_DEBUG1 (mio, "unknown dns response... %d\n", pkt->id); /* TODO: add source info */
return 0;
}
static void on_udp_reply_timeout (mio_t* mio, const mio_ntime_t* now, mio_tmrjob_t* job)
{
mio_dns_msg_t* reqmsg = (mio_dns_msg_t*)job->ctx;
dnc_dns_msg_xtn_t* msgxtn = dnc_dns_msg_getxtn(reqmsg);
mio_dev_sck_t* dev = (mio_dev_sck_t*)msgxtn->dev;
mio_svc_dnc_t* dnc = ((dnc_sck_xtn_t*)mio_dev_sck_getxtn(dev))->dnc;
MIO_ASSERT (mio, msgxtn->rtmridx == MIO_TMRIDX_INVALID);
MIO_DEBUG0 (mio, "*** TIMEOUT ==> unable to receive dns response in time...\n");
if (msgxtn->rtries < msgxtn->rmaxtries)
{
mio_ntime_t* tmout;
tmout = MIO_IS_POS_NTIME(&msgxtn->wtmout)? &msgxtn->wtmout: MIO_NULL;
if (mio_dev_sck_timedwrite(dnc->udp_sck, mio_dns_msg_to_pkt(reqmsg), reqmsg->pktlen, tmout, reqmsg, &msgxtn->servaddr) <= -1)
{
if (MIO_LIKELY(msgxtn->on_reply))
msgxtn->on_reply (dnc, reqmsg, MIO_ETMOUT, MIO_NULL, 0);
release_dns_msg (dnc, reqmsg);
return;
}
printf (">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> RESENT REQUEST >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> \n");
}
else
{
if (MIO_LIKELY(msgxtn->on_reply))
msgxtn->on_reply (dnc, reqmsg, MIO_ETMOUT, MIO_NULL, 0);
release_dns_msg (dnc, reqmsg);
}
}
static int on_udp_write (mio_dev_sck_t* dev, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr)
{
mio_t* mio = dev->mio;
mio_dns_msg_t* msg = (mio_dns_msg_t*)wrctx;
dnc_dns_msg_xtn_t* msgxtn = dnc_dns_msg_getxtn(msg);
mio_svc_dnc_t* dnc = ((dnc_sck_xtn_t*)mio_dev_sck_getxtn(dev))->dnc;
MIO_DEBUG1 (mio, "sent dns message %d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id));
MIO_ASSERT (mio, dev == (mio_dev_sck_t*)msgxtn->dev);
if (wrlen <= -1)
{
/* write has timed out or an error has occurred */
if (MIO_LIKELY(msgxtn->on_reply))
msgxtn->on_reply (dnc, msg, mio_geterrnum(mio), MIO_NULL, 0);
release_dns_msg (dnc, msg);
}
else if (mio_dns_msg_to_pkt(msg)->qr == 0 && msgxtn->rmaxtries > 0)
{
/* question. schedule to wait for response */
mio_tmrjob_t tmrjob;
MIO_MEMSET (&tmrjob, 0, MIO_SIZEOF(tmrjob));
tmrjob.ctx = msg;
mio_gettime (mio, &tmrjob.when);
MIO_ADD_NTIME (&tmrjob.when, &tmrjob.when, &msgxtn->rtmout);
tmrjob.handler = on_udp_reply_timeout;
tmrjob.idxptr = &msgxtn->rtmridx;
msgxtn->rtmridx = mio_instmrjob(mio, &tmrjob);
if (msgxtn->rtmridx == MIO_TMRIDX_INVALID)
{
/* call the callback to indicate this operation failure in the middle of transaction */
if (MIO_LIKELY(msgxtn->on_reply))
msgxtn->on_reply (dnc, msg, mio_geterrnum(mio), MIO_NULL, 0);
release_dns_msg (dnc, msg);
MIO_DEBUG0 (mio, "unable to schedule timeout...\n");
}
else
{
if (msgxtn->rtries == 0)
{
/* this is the first wait */
/* TODO: improve performance. hashing by id? */
/* chain it to the peing request list */
chain_pending_dns_reqmsg (dnc, msg);
}
msgxtn->rtries++;
}
}
else
{
/* sent an answer - we don't need this any more */
/* we don't call the on_reply callback stored in msg->ctx as this is not a reply context */
release_dns_msg (dnc, msg);
}
return 0;
}
static void on_udp_connect (mio_dev_sck_t* dev)
{
}
static void on_udp_disconnect (mio_dev_sck_t* dev)
{
}
mio_svc_dnc_t* mio_svc_dnc_start (mio_t* mio, const mio_skad_t* serv_addr, const mio_skad_t* bind_addr, const mio_ntime_t* send_tmout, const mio_ntime_t* reply_tmout, mio_oow_t reply_tmout_max_tries)
{
mio_svc_dnc_t* dnc = MIO_NULL;
mio_dev_sck_make_t mkinfo;
dnc_sck_xtn_t* xtn;
dnc = (mio_svc_dnc_t*)mio_callocmem(mio, MIO_SIZEOF(*dnc));
if (!dnc) goto oops;
dnc->mio = mio;
dnc->stop = mio_svc_dnc_stop;
dnc->serv_addr = *serv_addr;
dnc->send_tmout = *send_tmout;
dnc->reply_tmout = *reply_tmout;
dnc->reply_tmout_max_tries = reply_tmout_max_tries;
MIO_MEMSET (&mkinfo, 0, MIO_SIZEOF(mkinfo));
switch (mio_skad_family(serv_addr))
{
case MIO_AF_INET:
mkinfo.type = MIO_DEV_SCK_UDP4;
break;
case MIO_AF_INET6:
mkinfo.type = MIO_DEV_SCK_UDP6;
break;
default:
mio_seterrnum (mio, MIO_EINVAL);
goto oops;
}
mkinfo.on_write = on_udp_write;
mkinfo.on_read = on_udp_read;
mkinfo.on_connect = on_udp_connect;
mkinfo.on_disconnect = on_udp_disconnect;
dnc->udp_sck = mio_dev_sck_make(mio, MIO_SIZEOF(*xtn), &mkinfo);
if (!dnc->udp_sck) goto oops;
xtn = (dnc_sck_xtn_t*)mio_dev_sck_getxtn(dnc->udp_sck);
xtn->dnc = dnc;
if (bind_addr) /* TODO: get mio_dev_sck_bind_t? instead of bind_addr? */
{
mio_dev_sck_bind_t bi;
MIO_MEMSET (&bi, 0, MIO_SIZEOF(bi));
bi.localaddr = *bind_addr;
if (mio_dev_sck_bind(dnc->udp_sck, &bi) <= -1) goto oops;
}
MIO_SVC_REGISTER (mio, (mio_svc_t*)dnc);
return dnc;
oops:
if (dnc)
{
if (dnc->udp_sck) mio_dev_sck_kill (dnc->udp_sck);
mio_freemem (mio, dnc);
}
return MIO_NULL;
}
void mio_svc_dnc_stop (mio_svc_dnc_t* dnc)
{
mio_t* mio = dnc->mio;
if (dnc->udp_sck) mio_dev_sck_kill (dnc->udp_sck);
MIO_SVC_UNREGISTER (mio, dnc);
while (dnc->pending_req) release_dns_msg (dnc, dnc->pending_req);
mio_freemem (mio, dnc);
}
mio_dns_msg_t* mio_svc_dnc_sendmsg (mio_svc_dnc_t* dnc, mio_dns_bhdr_t* bdns, mio_dns_bqr_t* qr, mio_oow_t qr_count, mio_dns_brr_t* rr, mio_oow_t rr_count, mio_dns_bedns_t* edns, mio_svc_dnc_on_reply_t on_reply, mio_oow_t xtnsize)
{
/* send a request or a response */
mio_dns_msg_t* msg;
dnc_dns_msg_xtn_t* msgxtn;
mio_ntime_t* tmout;
msg = build_dns_msg(dnc, bdns, qr, qr_count, rr, rr_count, edns, MIO_SIZEOF(*msgxtn) + xtnsize);
if (!msg) return MIO_NULL;
msgxtn = dnc_dns_msg_getxtn(msg);
msgxtn->on_reply = on_reply;
msgxtn->wtmout = dnc->send_tmout;
msgxtn->rtmout = dnc->reply_tmout;
msgxtn->rmaxtries = dnc->reply_tmout_max_tries;
msgxtn->rtries = 0;
msgxtn->servaddr = dnc->serv_addr;
/* TODO: optionally, override dnc->serv_addr and use the target address passed as a parameter */
tmout = MIO_IS_POS_NTIME(&msgxtn->wtmout)? &msgxtn->wtmout: MIO_NULL;
if (mio_dev_sck_timedwrite(dnc->udp_sck, mio_dns_msg_to_pkt(msg), msg->pktlen, tmout, msg, &msgxtn->servaddr) <= -1)
{
release_dns_msg (dnc, msg);
return MIO_NULL;
}
return msg;
}
mio_dns_msg_t* mio_svc_dnc_sendreq (mio_svc_dnc_t* dnc, mio_dns_bhdr_t* bdns, mio_dns_bqr_t* qr, mio_dns_bedns_t* edns, mio_svc_dnc_on_reply_t on_reply, mio_oow_t xtnsize)
{
/* send a request without resource records */
if (bdns->rcode != MIO_DNS_RCODE_NOERROR)
{
mio_seterrnum (dnc->mio, MIO_EINVAL);
return MIO_NULL;
}
return mio_svc_dnc_sendmsg(dnc, bdns, qr, 1, MIO_NULL, 0, edns, on_reply, xtnsize);
}
/* ----------------------------------------------------------------------- */
struct dnc_dns_msg_resolve_xtn_t
{
mio_dns_rrt_t qtype;
int flags;
mio_svc_dnc_on_resolve_t on_resolve;
};
typedef struct dnc_dns_msg_resolve_xtn_t dnc_dns_msg_resolve_xtn_t;
#if defined(MIO_HAVE_INLINE)
static MIO_INLINE dnc_dns_msg_resolve_xtn_t* dnc_dns_msg_resolve_getxtn(mio_dns_msg_t* msg) { return ((dnc_dns_msg_resolve_xtn_t*)((mio_uint8_t*)dnc_dns_msg_getxtn(msg) + MIO_SIZEOF(dnc_dns_msg_xtn_t))); }
#else
# define dnc_dns_msg_resolve_getxtn(msg) ((dnc_dns_msg_resolve_xtn_t*)((mio_uint8_t*)dnc_dns_msg_getxtn(msg) + MIO_SIZEOF(dnc_dns_msg_xtn_t)))
#endif
static void on_dnc_resolve (mio_svc_dnc_t* dnc, mio_dns_msg_t* reqmsg, mio_errnum_t status, const void* data, mio_oow_t dlen)
{
mio_t* mio = mio_svc_dnc_getmio(dnc);
mio_dns_pkt_t* pkt;
mio_dns_pkt_info_t* pi = MIO_NULL;
dnc_dns_msg_resolve_xtn_t* reqmsgxtn = dnc_dns_msg_resolve_getxtn(reqmsg);
if (!(reqmsgxtn->flags & MIO_SVC_DNC_RESOLVE_FLAG_BRIEF))
{
/* the full reply packet is requested. no transformation is required */
if (reqmsgxtn->on_resolve) reqmsgxtn->on_resolve(dnc, reqmsg, status, data, dlen);
return;
}
if (data)
{
mio_uint32_t i;
MIO_ASSERT (mio, status == MIO_ENOERR);
pi = mio_dns_make_packet_info(mio, data, dlen);
if (!pi)
{
status = mio_geterrnum(mio);
goto no_valid_reply;
}
if (pi->hdr.rcode != MIO_DNS_RCODE_NOERROR)
{
status = MIO_EINVAL;
goto no_valid_reply;
}
if (pi->ancount < 0) goto no_valid_reply;
/* in the brief mode, we inspect the answer section only */
if (reqmsgxtn->qtype == MIO_DNS_RRT_Q_ANY)
{
/* return A or AAAA for ANY in the brief mode */
for (i = 0; i < pi->ancount; i++)
{
if (pi->rr.an[i].rrtype == MIO_DNS_RRT_A || pi->rr.an[i].rrtype == MIO_DNS_RRT_AAAA)
{
match_found:
if (reqmsgxtn->on_resolve) reqmsgxtn->on_resolve (dnc, reqmsg, status, &pi->rr.an[i], MIO_SIZEOF(pi->rr.an[i]));
goto done;
}
}
}
for (i = 0; i < pi->ancount; i++)
{
/* it is a bit time taking to retreive the query type from the packet
* bundled in reqmsg as it requires parsing of the packet. let me use
* the query type i stored in the extension space. */
switch (reqmsgxtn->qtype)
{
case MIO_DNS_RRT_Q_ANY:
case MIO_DNS_RRT_Q_AFXR: /* AFXR doesn't make sense in the brief mode. just treat it like ANY */
/* no A or AAAA found. so give the first entry in the answer */
goto match_found;
case MIO_DNS_RRT_Q_MAILA:
/* if you want to get the full RRs, don't use the brief mode. */
if (pi->rr.an[i].rrtype == MIO_DNS_RRT_MD || pi->rr.an[i].rrtype == MIO_DNS_RRT_MF) goto match_found;
break;
case MIO_DNS_RRT_Q_MAILB:
/* if you want to get the full RRs, don't use the brief mode. */
if (pi->rr.an[i].rrtype == MIO_DNS_RRT_MB || pi->rr.an[i].rrtype == MIO_DNS_RRT_MG ||
pi->rr.an[i].rrtype == MIO_DNS_RRT_MR || pi->rr.an[i].rrtype == MIO_DNS_RRT_MINFO) goto match_found;
break;
default:
if (pi->rr.an[i].rrtype == reqmsgxtn->qtype) goto match_found;
break;
}
}
goto no_valid_reply;
}
else
{
no_valid_reply:
if (reqmsgxtn->on_resolve) reqmsgxtn->on_resolve (dnc, reqmsg, status, MIO_NULL, 0);
}
done:
if (pi) mio_dns_free_packet_info(mio_svc_dnc_getmio(dnc), pi);
}
mio_dns_msg_t* mio_svc_dnc_resolve (mio_svc_dnc_t* dnc, const mio_bch_t* qname, mio_dns_rrt_t qtype, int flags, mio_svc_dnc_on_resolve_t on_resolve, mio_oow_t xtnsize)
{
static mio_dns_bhdr_t qhdr =
{
-1, /* id */
0, /* qr */
MIO_DNS_OPCODE_QUERY, /* opcode */
0, /* aa */
0, /* tc */
1, /* rd */
0, /* ra */
0, /* ad */
0, /* cd */
MIO_DNS_RCODE_NOERROR /* rcode */
};
static mio_dns_bedns_t qedns =
{
4096, /* uplen */
0, /* edns version */
0, /* dnssec ok */
0, /* number of edns options */
MIO_NULL
};
mio_dns_bqr_t qr;
mio_dns_msg_t* reqmsg;
dnc_dns_msg_resolve_xtn_t* reqmsgxtn;
qr.qname = (mio_bch_t*)qname;
qr.qtype = qtype;
qr.qclass = MIO_DNS_RRC_IN;
reqmsg = mio_svc_dnc_sendmsg(dnc, &qhdr, &qr, 1, MIO_NULL, 0, &qedns, on_dnc_resolve, MIO_SIZEOF(*reqmsgxtn) + xtnsize);
if (reqmsg)
{
reqmsgxtn = dnc_dns_msg_resolve_getxtn(reqmsg);
reqmsgxtn->on_resolve = on_resolve;
reqmsgxtn->qtype = qtype;
reqmsgxtn->flags = flags;
}
return reqmsg;
}
/* ----------------------------------------------------------------------- */
static int parse_domain_name (mio_t* mio, mio_dns_pkt_info_t* pi)