simple DNS lookup utility

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

simple DNS lookup utility

Rodrigo Mosconi-3
Hi,

For a course work, I wrote a simple DNS lookup utility using only the
native libc.  It`s not yet a complete replacement for dig/host/nslookup,
but I can work to improve it later.
I would like to receive any comments about it.


Follow the diff.

thx,

Mosconi

Index: usr.bin/resolv/Makefile
===================================================================
RCS file: usr.bin/resolv/Makefile
diff -N usr.bin/resolv/Makefile
--- /dev/null    1 Jan 1970 00:00:00 -0000
+++ usr.bin/resolv/Makefile    26 Jul 2016 03:45:43 -0000
@@ -0,0 +1,8 @@
+#    $OpenBSD$
+
+PROG=    resolv
+
+CFLAGS+= -Wall -O0 -g
+CFLAGS+= -I. -I${.CURDIR} -I${.CURDIR}/../../lib/libc/asr
+
+.include <bsd.prog.mk>
Index: usr.bin/resolv/resolv.1
===================================================================
RCS file: usr.bin/resolv/resolv.1
diff -N usr.bin/resolv/resolv.1
--- /dev/null    1 Jan 1970 00:00:00 -0000
+++ usr.bin/resolv/resolv.1    26 Jul 2016 03:45:43 -0000
@@ -0,0 +1,131 @@
+.\"
+.\" Copyright (c) 2016 Rodrigo Mosconi <[hidden email]?
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" The following requests are required for all man pages.
+.\"
+.\" Remove `\&' from the line below.
+.Dd $\&Mdocdate$
+.Dt RESOLV 1
+.Os
+.Sh NAME
+.Nm resolv
+.Nd simple DNS lookup utility
+.Sh SYNOPSIS
+.Nm resolv
+.Op Fl c Ar class
+.Op Fl h
+.Op Fl s Ar server
+.Op Fl t Ar type
+.Ar name
+.Sh DESCRIPTION
+.Nm
+is a utility to make DNS queries to convert name into IP/IPv6 address.
+When it is called with no arguments or options,
+.Nm
+will print a synopsis of its command line.
+.Pp
+The options are:
+.Pp
+.Bl -tag -width 10n
+.It Fl c Ar class
+query with class
+.Ar class
+rather than the dafault
+.Ar IN .
+.It Fl h
+print a help.
+.It Fl s Ar server
+query against
+.Ar server
+rather than the default from
+.Pa /etc/resolv.conf .
+.It Fl s Ar type
+query with type
+.Ar type
+rather than the default
+.Ar ANY .
+.El
+.\" The following requests should be uncommented and used where
appropriate.
+.\" .Sh CONTEXT
+.\" For section 9 functions only.
+.\" .Sh RETURN VALUES
+.\" For sections 2, 3, and 9 function return values only.
+.Sh ENVIRONMENT
+.Bl -tag -width NAMESERVER
+.It Ev NAMESERVER
+If the environment variable
+.Ev NAMESERVER
+is set, and the
+.Fl s
+option is not specified,
+.Nm
+will query against that server.
+.El
+.\" For sections 1, 6, 7, and 8 only.
+.Sh FILES
+.Bl -tag -width "/etc/resolv.conf" -compact
+.It Pa /etc/resolv.conf
+.El
+.Sh EXIT STATUS
+.Ex -std resolv
+.\" For sections 1, 6, and 8 only.
+.Sh EXAMPLES
+A typical
+.Nm
+usage is like:
+.Pp
+.Dl $ resolv www.openbsd.org
+.Pp
+To query using the server 10.1.2.3 is like:
+.Pp
+.Dl $ resolv -s 10.1.2.3 www.openbsd.org
+.Pp
+To obtain the MX:
+.Pp
+.Dl $ resolv -t mx www.openbsd.org
+.P
+.\" .Sh DIAGNOSTICS
+.\" For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only.
+.\" .Sh ERRORS
+.\" For sections 2, 3, 4, and 9 errno settings only.
+.Sh SEE ALSO
+.\" .Xr foobar 1
+.Xr resolv.conf 5
+.Xr nsd 8
+.Xr rebound 8
+.Xr unbound 8
+.Sh STANDARDS
+.Rs
+.%A Mockapetris, P.
+.%D November 1987
+.%R RFC 1034
+.%T "Domain names - concepts and facilities"
+.Re
+.Pp
+.Rs
+.%A Mockapetris, P.
+.%D November 1987
+.%R RFC 1035
+.%T "Domain names - implementation and specification"
+.Re
+.\" .Sh HISTORY
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Rodrigo Mosconi Aq Mt [hidden email]
+.
+.\" .Sh CAVEATS
+.\" .Sh BUGS
Index: usr.bin/resolv/resolv.c
===================================================================
RCS file: usr.bin/resolv/resolv.c
diff -N usr.bin/resolv/resolv.c
--- /dev/null    1 Jan 1970 00:00:00 -0000
+++ usr.bin/resolv/resolv.c    26 Jul 2016 03:45:44 -0000
@@ -0,0 +1,926 @@
+/*
+ * Copyright (c) 2016 Rodrigo Mosconi <[hidden email]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <asr.h>
+
+#include <stdlib.h>
+#include <errno.h>
+#include <err.h>
+#include <search.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "asr_private.h"
+
+/*
+ * missing from  nameserv.h
+ */
+
+#define T_NSEC3PARAM 51
+
+
+
+#define OPCODE_SHIFT    11
+#define Z_SHIFT          4
+
+static unsigned int s_get_rdclass(char *);
+static unsigned int s_get_rdtype(char *);
+static int          s_cmpkw(const void *,const void *);
+static void         usage(void);
+static const char*  s_get_rdclassbyint(unsigned int);
+static const char*  s_get_rdtypebyint(unsigned int);
+
+struct unpack {
+    const char      *buf;
+    size_t           len;
+    size_t           offset;
+    const char      *err;
+};
+
+struct dns_header {
+    uint16_t        id;
+    uint16_t        flags;
+    uint16_t        qdcount;
+    uint16_t        ancount;
+    uint16_t        nscount;
+    uint16_t        arcount;
+};
+
+struct dns_query {
+    char            q_dname[MAXDNAME];
+    uint16_t        q_type;
+    uint16_t        q_class;
+};
+
+struct dns_rr {
+    char            rr_dname[MAXDNAME];
+    uint16_t        rr_type;
+    uint16_t        rr_class;
+    uint32_t        rr_ttl;
+    union {
+    struct {
+        char    cname[MAXDNAME];
+    } cname;
+    struct {
+        uint16_t        preference;
+        char            exchange[MAXDNAME];
+    } mx;
+    struct {
+        char    nsname[MAXDNAME];
+    } ns;
+    struct {
+        char    ptrname[MAXDNAME];
+    } ptr;
+    struct {
+        char            mname[MAXDNAME];
+        char            rname[MAXDNAME];
+        uint32_t        serial;
+        uint32_t        refresh;
+        uint32_t        retry;
+        uint32_t        expire;
+        uint32_t        minimum;
+    } soa;
+    struct {
+        struct in_addr  addr;
+    } in_a;
+    struct {
+        struct in6_addr addr6;
+    } in_aaaa;
+    struct {
+        uint8_t   algorithm;
+        uint8_t   type;
+        char      fingerprint[BUFSIZ+1];
+    } sshfp;
+    struct {
+        uint16_t        type_covered;
+        uint8_t         algorithm;
+        uint8_t         labels;
+        uint32_t        original_ttl;
+        uint32_t        signature_expiration;
+        uint32_t        signature_inception;
+        uint16_t        key_tag;
+        char            signer_name[MAXDNAME];
+        char            fingerprint[BUFSIZ+1];
+    } rrsig;
+        struct {
+        char            txt[MAXCDNAME];
+        } txt;
+    struct {
+        uint16_t         rdlen;
+        const void      *rdata;
+    } other;
+    } rr;
+};
+
+
+
+static char *print_dname(const char *, char *, size_t);
+static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t
*,
+  char *, size_t);
+static int unpack_data(struct unpack *, void *, size_t);
+static int unpack_u8(struct unpack *, uint8_t *);
+static int unpack_u16(struct unpack *, uint16_t *);
+static int unpack_u32(struct unpack *, uint32_t *);
+static int unpack_hex(struct unpack *, char* , uint16_t);
+static int unpack_inaddr(struct unpack *, struct in_addr *);
+static int unpack_in6addr(struct unpack *, struct in6_addr *);
+static int unpack_dname(struct unpack *, char *, size_t);
+static void unpack_init(struct unpack *, const char *, size_t);
+static int unpack_header(struct unpack *, struct dns_header *);
+static int unpack_query(struct unpack *, struct dns_query *);
+static int unpack_rr(struct unpack *, struct dns_rr *);
+const char * print_rr(const struct dns_rr *, char *, size_t );
+
+
+struct kw {
+    unsigned int num;
+    char *str;
+};
+
+
+static const struct kw rdclass_kw[] = {
+    { C_IN,    "IN"    },        /* the arpa internet */
+    { C_CHAOS, "CHAOS" },     /* for chaos net (MIT) */
+    { C_HS,    "HS"    },        /* for Hesiod name server (MIT) (XXX) */
+    { C_ANY,   "ANY"   },        /* wildcard match */
+};
+
+#define nr_of_rdclasses (sizeof(rdclass_kw)/sizeof(rdclass_kw[0]))
+
+static struct kw rdtype_kw[] = {
+    {  T_A,         "A"        },  /* host address */
+    {  T_NS,            "NS"       },  /* authoritative server */
+    {  T_MD,            "MD"       },  /* mail destination */
+    {  T_MF,            "MF"       },  /* mail forwarder */
+    {  T_CNAME,         "CNAME"    },  /* canonical name */
+    {  T_SOA,           "SOA"      },  /* start of authority zone */
+    {  T_MB,            "MB"       },  /* mailbox domain name */
+    {  T_MG,            "MG"       },  /* mail group member */
+    {  T_MR,            "MR"       },  /* mail rename name */
+    {  T_NULL,          "NULL"     },  /* null resource record */
+    {  T_WKS,           "WKS"      },  /* well known service */
+    {  T_PTR,           "PTR"      },  /* domain name pointer */
+    {  T_HINFO,         "HINFO"    },  /* host information */
+    {  T_MINFO,         "MINFO"    },  /* mailbox information */
+    {  T_MX,            "MX"       },  /* mail routing information */
+    {  T_TXT,           "TXT"      },  /* text strings */
+    {  T_RP,            "RP"       },  /* responsible person */
+    {  T_AFSDB,         "AFSDB"    },  /* AFS cell database */
+    {  T_X25,           "X25"      },  /* X_25 calling address */
+    {  T_ISDN,          "ISDN"     },  /* ISDN calling address */
+    {  T_RT,            "RT"       },  /* router */
+    {  T_NSAP,         "NSAP"     },  /* NSAP address */
+    {  T_NSAP_PTR,     "NSAP_PTR" },  /* reverse NSAP lookup (deprecated)
*/
+    {  T_SIG,           "SIG"      },  /* security signature */
+    {  T_KEY,           "KEY"      },  /* security key */
+    {  T_PX,            "PX"       },  /* X.400 mail mapping */
+    {  T_GPOS,          "GPOS"     },  /* geographical position
(withdrawn) */
+    {  T_AAAA,          "AAAA"     },  /* IP6 Address */
+    {  T_LOC,           "LOC"      },  /* Location Information */
+    {  T_NXT,           "NXT"      },  /* Next Valid Name in Zone */
+    {  T_EID,           "EID"      },  /* Endpoint identifier */
+    {  T_NIMLOC,        "NIMLOC"   },  /* Nimrod locator */
+    {  T_SRV,           "SRV"      },  /* Server selection */
+    {  T_ATMA,          "ATMA"     },  /* ATM Address */
+    {  T_NAPTR,         "NAPTR"    },  /* Naming Authority PoinTeR */
+    {  T_KX,            "KX"       },  /* Key Exchanger */
+    {  T_CERT,          "CERT"     },  /* CERT */
+    {  T_A6,            "A6"       },  /* A6 */
+    {  T_DNAME,         "DNAME"    },  /* DNAME */
+    {  T_SINK,          "SINK"     },  /* SINK */
+    {  T_OPT,           "OPT"      },  /* OPT pseudo-RR, RFC2671 */
+    {  T_APL,           "APL"      },  /* APL */
+    {  T_DS,            "DS"       },  /* Delegation Signer */
+    {  T_SSHFP,         "SSHFP"    },  /* SSH Key Fingerprint */
+    {  T_RRSIG,         "RRSIG"    },  /* RRSIG */
+    {  T_NSEC,          "NSEC"     },  /* NSEC */
+    {  T_DNSKEY,        "DNSKEY"   },  /* DNSKEY */
+    {  T_NSEC3PARAM,    "NSEC3PARAM" },  /* NSEC */
+    {  T_UINFO,         "UINFO"    },  /* user (finger) information */
+    {  T_UID,           "UID"      },  /* user ID */
+    {  T_GID,           "GID"      },  /* group ID */
+    {  T_UNSPEC,        "UNSPEC"   },  /* Unspecified format (binary data)
*/
+    {  T_TKEY,          "TKEY"     },  /* Transaction Key */
+    {  T_TSIG,          "TSIG"     },  /* Transaction Signature */
+    {  T_IXFR,          "IXFR"     },  /* incremental zone transfer */
+    {  T_AXFR,          "AXFR"     },  /* transfer zone of authority */
+    {  T_MAILB,         "MAILB"    },  /* transfer mailbox records */
+    {  T_MAILA,         "MAILA"    },  /* transfer mail agent records */
+    {  T_ANY,           "ANY"      },  /* wildcard match */
+};
+
+#define nr_of_rdtypes (sizeof(rdtype_kw)/sizeof(rdtype_kw[0]))
+
+static int
+s_cmpkw(const void *k,const void *e){
+    return strcasecmp(k,((const struct kw *)e)->str)==0?0:-1;
+}
+
+static int
+s_cmpkw_num(const void *k,const void *e){
+    return *(unsigned int *) k - ((const struct kw *)e)->num;
+}
+
+
+static unsigned int
+s_get_rdclass(char *optarg){
+    struct kw *class;
+
+    size_t nr = nr_of_rdclasses;
+    class = lfind(optarg, rdclass_kw, &nr,
+      sizeof(struct kw), s_cmpkw);
+
+    if (class)
+    return class->num;
+    else
+    return (unsigned int)-1;
+}
+
+static const char *
+s_get_rdclassbyint(unsigned int rdclass){
+    struct kw *class;
+
+    class = bsearch(&rdclass, rdclass_kw, nr_of_rdclasses,
+      sizeof(struct kw), s_cmpkw_num);
+
+    if (class)
+    return class->str;
+    else
+    return "??";
+}
+
+
+static unsigned int
+s_get_rdtype(char *optarg){
+    struct kw *type;
+
+    size_t nr = nr_of_rdtypes;
+    type = lfind(optarg, rdtype_kw, &nr,
+      sizeof(struct kw), s_cmpkw);
+
+    if (type)
+    return type->num;
+    else
+    return (unsigned int)-1;
+}
+
+static const char *
+s_get_rdtypebyint(unsigned int rdtype){
+    struct kw *type;
+
+    type = bsearch(&rdtype, rdtype_kw, nr_of_rdtypes,
+      sizeof(struct kw), s_cmpkw_num);
+
+    if (type)
+    return type->str;
+    else
+    return "??";
+}
+
+
+void
+usage(void){
+    printf("%s [-c class] [-h] [-s server] [-t type]
fdqn\n",getprogname());
+}
+
+static char *
+print_dname(const char *_dname, char *buf, size_t max)
+{
+    const unsigned char *dname = _dname;
+    char    *res;
+    size_t   left, n, count;
+
+    if (_dname[0] == 0) {
+    (void)strlcpy(buf, ".", max);
+    return buf;
+    }
+
+    res = buf;
+    left = max - 1;
+    for (n = 0; dname[0] && left; n += dname[0]) {
+    count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
+    memmove(buf, dname + 1, count);
+    dname += dname[0] + 1;
+    left -= count;
+    buf += count;
+    if (left) {
+        left -= 1;
+        *buf++ = '.';
+    }
+    }
+    buf[0] = 0;
+
+    return (res);
+}
+
+static ssize_t
+dname_expand(const unsigned char *data, size_t len, size_t offset,
+  size_t *newoffset, char *dst, size_t max)
+{
+    size_t           n, count, end, ptr, start;
+    ssize_t          res;
+
+    if (offset >= len)
+    return (-1);
+
+    res = 0;
+    end = start = offset;
+
+    for (; (n = data[offset]); ) {
+    if ((n & 0xc0) == 0xc0) {
+        if (offset + 2 > len)
+        return (-1);
+        ptr = 256 * (n & ~0xc0) + data[offset + 1];
+        if (ptr >= start)
+        return (-1);
+        if (end < offset + 2)
+        end = offset + 2;
+        offset = start = ptr;
+        continue;
+    }
+    if (offset + n + 1 > len)
+        return (-1);
+
+    /* copy n + at offset+1 */
+    if (dst != NULL && max != 0) {
+        count = (max < n + 1) ? (max) : (n + 1);
+        memmove(dst, data + offset, count);
+        dst += count;
+        max -= count;
+    }
+    res += n + 1;
+    offset += n + 1;
+    if (end < offset)
+        end = offset;
+    }
+    if (end < offset + 1)
+    end = offset + 1;
+
+    if (dst != NULL && max != 0)
+    dst[0] = 0;
+    if (newoffset)
+    *newoffset = end;
+    return (res + 1);
+}
+
+
+
+static int
+unpack_data(struct unpack *p, void *data, size_t len)
+{
+    if (p->err)
+    return (-1);
+
+    if (p->len - p->offset < len) {
+    p->err = "too short";
+    return (-1);
+    }
+
+    memmove(data, p->buf + p->offset, len);
+    p->offset += len;
+
+    return (0);
+}
+
+static int
+unpack_u16(struct unpack *p, uint16_t *u16)
+{
+    if (unpack_data(p, u16, 2) == -1)
+    return (-1);
+
+    *u16 = ntohs(*u16);
+
+    return (0);
+}
+
+static int
+unpack_u8(struct unpack *p, uint8_t *u8)
+{
+    if (unpack_data(p, u8, 1) == -1)
+    return (-1);
+
+    return (0);
+}
+
+
+static int
+unpack_u32(struct unpack *p, uint32_t *u32)
+{
+    if (unpack_data(p, u32, 4) == -1)
+    return (-1);
+
+    *u32 = ntohl(*u32);
+
+    return (0);
+}
+
+static int
+unpack_inaddr(struct unpack *p, struct in_addr *a)
+{
+    return (unpack_data(p, a, 4));
+}
+
+static int
+unpack_in6addr(struct unpack *p, struct in6_addr *a6)
+{
+    return (unpack_data(p, a6, 16));
+}
+
+static int
+unpack_dname(struct unpack *p, char *dst, size_t max)
+{
+    ssize_t e;
+
+    if (p->err)
+    return (-1);
+
+    e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max);
+    if (e == -1) {
+    p->err = "bad domain name";
+    return (-1);
+    }
+    if (e < 0 || e > MAXDNAME) {
+    p->err = "domain name too long";
+    return (-1);
+    }
+
+    return (0);
+}
+
+static int
+unpack_hex(struct unpack *p, char *dst, uint16_t max)
+{
+    int i,l,len=BUFSIZ;
+    char *s=dst;
+
+    for (i=0 ; i<max ; i++)
+    {
+        l=snprintf(s,len,"%02X",*(unsigned char*)(p->buf+p->offset++));
+        s+=2;
+        len-=2;
+        if (i>0 && i%27==0){
+            snprintf(s,len," ");
+            s++; len--;
+        }
+
+    }
+
+    return (0);
+}
+
+
+static void
+unpack_init(struct unpack *unpack, const char *buf, size_t len)
+{
+    unpack->buf = buf;
+    unpack->len = len;
+    unpack->offset = 0;
+    unpack->err = NULL;
+}
+
+static int
+unpack_header(struct unpack *p, struct dns_header *h)
+{
+    if (unpack_data(p, h, HFIXEDSZ) == -1)
+    return (-1);
+
+    h->flags = ntohs(h->flags);
+    h->qdcount = ntohs(h->qdcount);
+    h->ancount = ntohs(h->ancount);
+    h->nscount = ntohs(h->nscount);
+    h->arcount = ntohs(h->arcount);
+
+    return (0);
+}
+
+static int
+unpack_query(struct unpack *p, struct dns_query *q)
+{
+    unpack_dname(p, q->q_dname, sizeof(q->q_dname));
+    unpack_u16(p, &q->q_type);
+    unpack_u16(p, &q->q_class);
+
+    return (p->err) ? (-1) : (0);
+}
+
+
+
+static int
+unpack_rr(struct unpack *p, struct dns_rr *rr)
+{
+    uint8_t        txtsz;
+    uint16_t        rdlen;
+    size_t          save_offset;
+
+    unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
+    unpack_u16(p, &rr->rr_type);
+    unpack_u16(p, &rr->rr_class);
+    unpack_u32(p, &rr->rr_ttl);
+    unpack_u16(p, &rdlen);
+
+    if (p->err)
+    return (-1);
+
+    if (p->len - p->offset < rdlen) {
+    p->err = "too short";
+    return (-1);
+    }
+
+    save_offset = p->offset;
+
+    switch (rr->rr_type) {
+
+    case T_CNAME:
+    unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
+    break;
+
+    case T_MX:
+    unpack_u16(p, &rr->rr.mx.preference);
+    unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
+    break;
+
+    case T_NS:
+    unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
+    break;
+
+    case T_PTR:
+    unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
+    break;
+
+    case T_SOA:
+    unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
+    unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
+    unpack_u32(p, &rr->rr.soa.serial);
+    unpack_u32(p, &rr->rr.soa.refresh);
+    unpack_u32(p, &rr->rr.soa.retry);
+    unpack_u32(p, &rr->rr.soa.expire);
+    unpack_u32(p, &rr->rr.soa.minimum);
+    break;
+
+    case T_A:
+    if (rr->rr_class != C_IN)
+        goto other;
+    unpack_inaddr(p, &rr->rr.in_a.addr);
+    break;
+
+    case T_AAAA:
+    if (rr->rr_class != C_IN)
+        goto other;
+    unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
+    break;
+
+    case T_SSHFP:
+    if (rr->rr_class != C_IN)
+        goto other;
+    unpack_u8(p, &rr->rr.sshfp.algorithm);
+    unpack_u8(p, &rr->rr.sshfp.type);
+    unpack_hex(p, rr->rr.sshfp.fingerprint, rdlen-2);
+    break;
+
+    case T_TXT:
+    if (rr->rr_class != C_IN)
+        goto other;
+    unpack_u8(p,&txtsz);
+        unpack_data(p,
rr->rr.txt.txt,txtsz<sizeof(rr->rr.txt.txt)?txtsz:sizeof(rr->rr.txt.txt));
+        break;
+
+    case T_RRSIG:
+    if (rr->rr_class != C_IN)
+        goto other;
+    unpack_u16(p, &rr->rr.rrsig.type_covered);
+    unpack_u8(p, &rr->rr.rrsig.algorithm);
+    unpack_u8(p, &rr->rr.rrsig.labels);
+    unpack_u32(p, &rr->rr.rrsig.original_ttl);
+    unpack_u32(p, &rr->rr.rrsig.signature_expiration);
+    unpack_u32(p, &rr->rr.rrsig.signature_inception);
+    unpack_u16(p, &rr->rr.rrsig.key_tag);
+    unpack_dname(p, rr->rr.rrsig.signer_name, sizeof
rr->rr.rrsig.signer_name);
+    unpack_hex(p, rr->rr.rrsig.fingerprint, rdlen - p->offset);
+    break;
+
+    default:
+    other:
+    rr->rr.other.rdata = p->buf + p->offset;
+    rr->rr.other.rdlen = rdlen;
+    p->offset += rdlen;
+    }
+
+    if (p->err)
+    return (-1);
+
+    /* make sure that the advertised rdlen is really ok */
+    if (p->offset - save_offset != rdlen)
+    p->err = "bad dlen";
+
+    return (p->err) ? (-1) : (0);
+}
+
+char *
+_strdname(const char *_dname, char *buf, size_t max)
+{
+    const unsigned char *dname = _dname;
+    char    *res;
+    size_t   left, n, count;
+
+    if (_dname[0] == 0) {
+    strlcpy(buf, ".", max);
+    return buf;
+    }
+
+    res = buf;
+    left = max - 1;
+    for (n = 0; dname[0] && left; n += dname[0]) {
+    count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
+    memmove(buf, dname + 1, count);
+    dname += dname[0] + 1;
+    left -= count;
+    buf += count;
+    if (left) {
+        left -= 1;
+        *buf++ = '.';
+    }
+    }
+    buf[0] = 0;
+
+    return (res);
+}
+
+
+
+const char *
+print_rr(const struct dns_rr *rr, char *buf, size_t max)
+{
+    char    *res;
+    char     tmp[256];
+    char     tmp2[256];
+    int      r,i;
+    char    *b2;
+    char    *b2p    ;
+    char    *b2l;
+
+    res = buf;
+
+    r = snprintf(buf, max, "%s %u %s %s ",
+      print_dname(rr->rr_dname, tmp, sizeof tmp),
+      rr->rr_ttl,
+      s_get_rdclassbyint(rr->rr_class),
+      s_get_rdtypebyint(rr->rr_type));
+    if (r == -1) {
+    buf[0] = '\0';
+    return (buf);
+    }
+
+    if ((size_t)r >= max)
+    return (buf);
+
+    max -= r;
+    buf += r;
+
+    switch (rr->rr_type) {
+    case T_CNAME:
+    print_dname(rr->rr.cname.cname, buf, max);
+    break;
+    case T_MX:
+    snprintf(buf, max, "%lu %s",
+      (unsigned long)rr->rr.mx.preference,
+      print_dname(rr->rr.mx.exchange, tmp, sizeof tmp));
+    break;
+    case T_NS:
+    print_dname(rr->rr.ns.nsname, buf, max);
+    break;
+    case T_PTR:
+    print_dname(rr->rr.ptr.ptrname, buf, max);
+    break;
+    case T_SOA:
+    snprintf(buf, max, "%s %s %lu %lu %lu %lu %lu",
+      print_dname(rr->rr.soa.rname, tmp, sizeof tmp),
+      print_dname(rr->rr.soa.mname, tmp2, sizeof tmp2),
+      (unsigned long)rr->rr.soa.serial,
+      (unsigned long)rr->rr.soa.refresh,
+      (unsigned long)rr->rr.soa.retry,
+      (unsigned long)rr->rr.soa.expire,
+      (unsigned long)rr->rr.soa.minimum);
+    break;
+    case T_A:
+    if (rr->rr_class != C_IN)
+        goto other;
+    snprintf(buf, max, "%s", inet_ntop(AF_INET,
+        &rr->rr.in_a.addr, tmp, sizeof tmp));
+    break;
+    case T_AAAA:
+    if (rr->rr_class != C_IN)
+        goto other;
+    snprintf(buf, max, "%s", inet_ntop(AF_INET6,
+        &rr->rr.in_aaaa.addr6, tmp, sizeof tmp));
+    break;
+    case T_SSHFP:
+    if (rr->rr_class != C_IN)
+        goto other;
+    snprintf(buf, max, "%d %d %s", rr->rr.sshfp.algorithm,
+      rr->rr.sshfp.type, rr->rr.sshfp.fingerprint);
+    break;
+
+    case T_TXT:
+    if (rr->rr_class != C_IN)
+        goto other;
+    snprintf(buf,max, "\"%s\"", rr->rr.txt.txt);
+        break;
+
+    case T_RRSIG:
+    if (rr->rr_class != C_IN)
+        goto other;
+    snprintf(buf, max, "%s %u %u %u %u ( %u %u %s %s)",
+      s_get_rdtypebyint(rr->rr.rrsig.type_covered),
+      rr->rr.rrsig.algorithm,
+      rr->rr.rrsig.labels,
+      rr->rr.rrsig.original_ttl,
+      rr->rr.rrsig.signature_expiration,
+      rr->rr.rrsig.signature_inception,
+      rr->rr.rrsig.key_tag,
+      print_dname(rr->rr.rrsig.signer_name, tmp, sizeof tmp),
+      rr->rr.rrsig.fingerprint
+        );
+    break;
+
+/*
+  case T_SRV:
+  if (rr->rr_class != C_IN)
+  goto other;
+
+  break;
+
+*/
+    default:
+    other:
+    b2=calloc(3,(int)rr->rr.other.rdlen+1);
+    b2p=b2;
+    b2l=b2+(3*(int)rr->rr.other.rdlen);
+    for (i=0;i<(int)rr->rr.other.rdlen;i++){
+        b2p+=snprintf(b2p,b2l-b2p,"%02X ",((unsigned char
*)rr->rr.other.rdata)[i]);
+    }
+    snprintf(buf, max, "(rdlen=%i) %s", (int)rr->rr.other.rdlen, b2);
+    free(b2);
+    break;
+    }
+
+    return (res);
+}
+
+static const char *
+rcodetostr(uint16_t v)
+{
+    switch (v) {
+    case NOERROR:   return "NOERROR";
+    case FORMERR:   return "FORMERR";
+    case SERVFAIL:  return "SERVFAIL";
+    case NXDOMAIN:  return "NXDOMAIN";
+    case NOTIMP:    return "NOTIMP";
+    case REFUSED:   return "REFUSED";
+    default:        return "?";
+    }
+}
+
+static const char *
+print_header(const struct dns_header *h, char *buf, size_t max)
+{
+    snprintf(buf, max,
+      "id:0x%04x %s op:%i %s %s %s %s z:%i r:%s qd:%i an:%i ns:%i ar:%i",
+      ((int)h->id),
+      (h->flags & QR_MASK) ? "QR":"  ",
+      (int)(OPCODE(h->flags) >> OPCODE_SHIFT),
+      (h->flags & AA_MASK) ? "AA":"  ",
+      (h->flags & TC_MASK) ? "TC":"  ",
+      (h->flags & RD_MASK) ? "RD":"  ",
+      (h->flags & RA_MASK) ? "RA":"  ",
+      ((h->flags & Z_MASK) >> Z_SHIFT),
+      rcodetostr(RCODE(h->flags)),
+      h->qdcount, h->ancount, h->nscount, h->arcount);
+
+    return (buf);
+}
+
+
+
+
+/*
+ * Simple DNS resolver
+ */
+int
+main(int argc, char **argv)
+{
+    unsigned int rdclass=C_IN;
+    unsigned int rdtype=T_ANY;
+    int ch;
+    char *server =NULL;
+    char                buf[BUFSIZ];
+
+    struct asr_query *aq;
+    struct asr_result ar;
+    struct unpack        p;
+    struct dns_header    hdr;
+    struct dns_query     q;
+    struct dns_rr        rr;
+    char                 response_server[INET6_ADDRSTRLEN];
+
+    if(pledge("stdio dns",NULL)==-1)
+    err(1,"pledge");
+
+    while ((ch = getopt(argc, argv, "c:ht:s:")) != -1) {
+    switch(ch) {
+    case 'c':
+        rdclass=s_get_rdclass(optarg);
+        break;
+    case 'h':
+        usage();
+        return 0;
+    case 't':
+        rdtype=s_get_rdtype(optarg);
+        break;
+        case 's':
+        server = optarg;
+        break;
+        default:
+        usage();
+        exit(1);
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc == 0){
+        usage();
+    exit(1);
+    }
+
+
+    if (server)
+    setenv ("NAMESERVER",server,1);
+
+    aq = res_query_async(argv[0], rdclass, rdtype, NULL);
+
+    asr_run_sync(aq,&ar);
+
+    if (ar.ar_errno == ETIMEDOUT)
+        errx(1,"timeout to query\n");
+
+
+    switch(ar.ar_ns.ss_family) {
+    case AF_INET:
+    inet_ntop(AF_INET, &(((struct sockaddr_in*) &ar.ar_ns)->sin_addr),
+      response_server,sizeof(response_server));
+    break;
+    case AF_INET6:
+    inet_ntop(AF_INET6, &((struct sockaddr_in6*) &ar.ar_ns)->sin6_addr,
+      response_server,sizeof(response_server));
+    break;
+    default:
+    errx(1,"Unknow Address Family: %d\n", ar.ar_ns.ss_family);
+    }
+
+
+
+    unpack_init(&p, ar.ar_data, ar.ar_datalen);
+    unpack_header(&p, &hdr);
+    printf("response from %s", response_server);
+    if (hdr.flags & AA_MASK)
+    printf(" (autoritative)\n");
+    else
+    printf("\n");
+    print_header(&hdr, buf, BUFSIZ-1);
+    printf("%s\n", buf);
+    unpack_query(&p, &q);
+
+    for (; hdr.ancount; hdr.ancount--) {
+    unpack_rr(&p, &rr);
+    print_rr(&rr, buf, BUFSIZ-1);
+    printf("%s\n", buf);
+    }
+
+    free(ar.ar_data);
+
+    exit(0);
+}
+
Index: usr.bin/Makefile
===================================================================
RCS file: /cvs/src/usr.bin/Makefile,v
retrieving revision 1.153
diff -u -p -u -r1.153 Makefile
--- usr.bin/Makefile    16 Jul 2015 20:50:40 -0000    1.153
+++ usr.bin/Makefile    26 Jul 2016 03:45:44 -0000
@@ -19,7 +19,7 @@ SUBDIR= apply arch at aucat audioctl awk
     nfsstat nice nm nl nohup openssl pagesize passwd paste patch pctr \
     pkg-config pkill \
     pr printenv printf quota radioctl rcs rdist rdistd \
-    readlink renice rev rpcgen rpcinfo rs rup rusers rwall \
+    readlink renice resolv rev rpcgen rpcinfo rs rup rusers rwall \
     sdiff script sed sendbug shar showmount signify skey \
     skeyaudit skeyinfo skeyinit sndiod \
     sort spell split sqlite3 ssh stat su systat \
Index: lib/libc/asr/asr.c
===================================================================
RCS file: /cvs/src/lib/libc/asr/asr.c,v
retrieving revision 1.54
diff -u -p -u -r1.54 asr.c
--- lib/libc/asr/asr.c    18 Jun 2016 15:25:28 -0000    1.54
+++ lib/libc/asr/asr.c    26 Jul 2016 03:45:44 -0000
@@ -61,7 +61,7 @@ static void *__THREAD_NAME(_asr);
 static struct asr *_asr = NULL;

 /* Allocate and configure an async "resolver". */
-static void *
+void *
 _asr_resolver(void)
 {
     static int     init = 0;
@@ -744,6 +744,17 @@ asr_ctx_envopts(struct asr_ctx *ac)
         if (s < sizeof buf)
             asr_ctx_parse(ac, buf);
     }
+
+        if ((e = getenv("NAMESERVER")) != NULL) {
+                strlcpy(buf, "nameserver ", sizeof buf);
+                strlcat(buf, e, sizeof buf);
+                s = strlcat(buf, "\n", sizeof buf);
+                if (s < sizeof buf)
+            ac->ac_nscount=0;
+                        asr_ctx_parse(ac, buf);
+        }
+
+
 }

 /*
Index: lib/libc/asr/asr_private.h
===================================================================
RCS file: /cvs/src/lib/libc/asr/asr_private.h,v
retrieving revision 1.38
diff -u -p -u -r1.38 asr_private.h
--- lib/libc/asr/asr_private.h    16 Dec 2015 16:32:30 -0000    1.38
+++ lib/libc/asr/asr_private.h    26 Jul 2016 03:45:44 -0000
@@ -310,7 +310,7 @@ ssize_t _asr_dname_from_fqdn(const char
 ssize_t _asr_addr_as_fqdn(const char *, int, char *, size_t);

 /* asr.c */
-static void *_asr_resolver(void);
+void *_asr_resolver(void);
 void _asr_resolver_done(void *);
 struct asr_ctx *_asr_use_resolver(void *);
 struct asr_ctx *_asr_no_resolver(void);

Reply | Threaded
Open this post in threaded view
|

Re: simple DNS lookup utility

Peter J. Philipp-3
On Tue, Jul 26, 2016 at 01:00:43AM -0300, Rodrigo Mosconi wrote:

> Hi,
>
> For a course work, I wrote a simple DNS lookup utility using only the
> native libc.  It`s not yet a complete replacement for dig/host/nslookup,
> but I can work to improve it later.
> I would like to receive any comments about it.
>
>
> Follow the diff.
>
> thx,
>
> Mosconi

Hello Rodrigo Mosconi,

I haven't used this utility only read the code a little.  I think the
bsearch of the RR types is cool.  I did think that when you cast to
unsigned long for some SOA values you may be doing something unintended,
it's better to cast to uint32_t IMO if casting at all.  The reason being
an unsigned long on amd64 is 8 bytes size, and 4 bytes size on i386.

Anyhow since you're leaning on libasr you're getting experience with it.
There was a call by OpenSMTPD on twitter a while back that "if DNSSEC is put
into libasr, they would look into DANE".  I don't know where the status of
that is now.  I did some code and posted it to the freenode IRC channel
#opensmtpd, since eric was very busy at the time I don't know if he saw it.

What you can do is perhaps look into getting a full dnssec stack into libasr
as a follow up project if you want.  I'd try to get in touch with eric@ if
you're interested in that.  Perhaps you guys can form a coding group to make
this horrendously difficult project easier.

If you're interested in that I'd recommend to you this book that I also have
that I use for my DNSSEC implementations.  It's called "The DNSSEC
Specifications" by Reed Media Services.  It's basically the RFC's printed out
and put into a book, but it's handy for dealing with DNSSEC since you have
an offline copy of the RFC's to read at places where there is no computer.

If that's not the way you want to go, perhaps you want to write a DNS server.
I did this and my code can be found at http://delphinusdns.centroid.eu.  I'm
currently programming on a sign utility for DNSSEC signing zones.  DNS is a
never ending subject and I've been programming 10+ years on my DNS server, what
I have learned is that it's a long term commitment to do any code for DNS.

So in conclusion, you've proved that you can code DNS, a few doors open for
you but they promise to be a long journey to reach the final destination and
a lot of hard work.  You can choose to close those doors and stick with the
achievement of a dig like tool and persue other short term projects or you
can take the leap, but it'll consume your life for years on end if you stay
at it.  It can be profitable for you if you market it right, or it can keep
you unfed for a decade or longer.  I'm just saying. :-)

To reiterate I think your code looks fairly good with minor nits given from
a mediocre programmer such as me.

Best Regards,

-peter

Reply | Threaded
Open this post in threaded view
|

Re: simple DNS lookup utility

Rodrigo Mosconi-3
2016-07-26 7:00 GMT-03:00 Peter J. Philipp <[hidden email]>:

> On Tue, Jul 26, 2016 at 01:00:43AM -0300, Rodrigo Mosconi wrote:
> > Hi,
> >
> > For a course work, I wrote a simple DNS lookup utility using only the
> > native libc.  It`s not yet a complete replacement for dig/host/nslookup,
> > but I can work to improve it later.
> > I would like to receive any comments about it.
> >
> >
> > Follow the diff.
> >
> > thx,
> >
> > Mosconi
>
> Hello Rodrigo Mosconi,
>
> I haven't used this utility only read the code a little.  I think the
> bsearch of the RR types is cool.  I did think that when you cast to
> unsigned long for some SOA values you may be doing something unintended,
> it's better to cast to uint32_t IMO if casting at all.  The reason being
> an unsigned long on amd64 is 8 bytes size, and 4 bytes size on i386.
>
> Anyhow since you're leaning on libasr you're getting experience with it.
> There was a call by OpenSMTPD on twitter a while back that "if DNSSEC is
> put
> into libasr, they would look into DANE".  I don't know where the status of
> that is now.  I did some code and posted it to the freenode IRC channel
> #opensmtpd, since eric was very busy at the time I don't know if he saw it.
>
> What you can do is perhaps look into getting a full dnssec stack into
> libasr
> as a follow up project if you want.  I'd try to get in touch with eric@ if
> you're interested in that.  Perhaps you guys can form a coding group to
> make
> this horrendously difficult project easier.
>
> If you're interested in that I'd recommend to you this book that I also
> have
> that I use for my DNSSEC implementations.  It's called "The DNSSEC
> Specifications" by Reed Media Services.  It's basically the RFC's printed
> out
> and put into a book, but it's handy for dealing with DNSSEC since you have
> an offline copy of the RFC's to read at places where there is no computer.
>
> If that's not the way you want to go, perhaps you want to write a DNS
> server.
> I did this and my code can be found at http://delphinusdns.centroid.eu.
> I'm
> currently programming on a sign utility for DNSSEC signing zones.  DNS is a
> never ending subject and I've been programming 10+ years on my DNS server,
> what
> I have learned is that it's a long term commitment to do any code for DNS.
>
> So in conclusion, you've proved that you can code DNS, a few doors open for
> you but they promise to be a long journey to reach the final destination
> and
> a lot of hard work.  You can choose to close those doors and stick with the
> achievement of a dig like tool and persue other short term projects or you
> can take the leap, but it'll consume your life for years on end if you stay
> at it.  It can be profitable for you if you market it right, or it can keep
> you unfed for a decade or longer.  I'm just saying. :-)
>
> To reiterate I think your code looks fairly good with minor nits given from
> a mediocre programmer such as me.
>
> Best Regards,
>
> -peter
>

Thanks Peter,

Related with DNS, I`m thinking on improve it later  to become more like
dig. For now,
I`m finishing a 40 pages report about it.. (yes, the report is bigger than
the code...).

[]`s
Mosconi