- draft-ietf-ipngwg-icmp-namelookups-09

- make it compilable

It still requires root privilege and is experimental.

Obtained from:	KAME
MFC after:	1 week
This commit is contained in:
Hajimu UMEMOTO 2006-07-21 18:55:51 +00:00
parent a152f8a361
commit b88ad6b5d6

View File

@ -2178,67 +2178,120 @@ _dns_ehent(void)
/*
* experimental:
* draft-ietf-ipngwg-icmp-namelookups-02.txt
* draft-ietf-ipngwg-icmp-namelookups-09.txt
* ifindex is assumed to be encoded in addr.
*/
#include <sys/uio.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <ctype.h>
struct _icmp_host_cache {
struct _icmp_host_cache *hc_next;
int hc_ifindex;
struct in6_addr hc_addr;
char *hc_name;
};
#ifndef NI_QTYPE_NODENAME
#define NI_QTYPE_NODENAME NI_QTYPE_DNSNAME
#endif
static char *
_icmp_fqdn_query(const struct in6_addr *addr, int ifindex)
dnsdecode(sp, ep, base, buf, bufsiz)
const u_char **sp;
const u_char *ep;
const u_char *base; /*base for compressed name*/
u_char *buf;
size_t bufsiz;
{
int i;
const u_char *cp;
char cresult[MAXDNAME + 1];
const u_char *comp;
int l;
cp = *sp;
*buf = '\0';
if (cp >= ep)
return NULL;
while (cp < ep) {
i = *cp;
if (i == 0 || cp != *sp) {
if (strlcat(buf, ".", bufsiz) >= bufsiz)
return NULL; /* result overrun */
}
if (i == 0)
break;
cp++;
if ((i & 0xc0) == 0xc0 && cp - base > (i & 0x3f)) {
/* DNS compression */
if (!base)
return NULL;
comp = base + (i & 0x3f);
if (dnsdecode(&comp, cp, base, cresult,
sizeof(cresult)) == NULL)
return NULL;
if (strlcat(buf, cresult, bufsiz) >= bufsiz)
return NULL; /* result overrun */
break;
} else if ((i & 0x3f) == i) {
if (i > ep - cp)
return NULL; /* source overrun */
while (i-- > 0 && cp < ep) {
l = snprintf(cresult, sizeof(cresult),
isprint(*cp) ? "%c" : "\\%03o", *cp & 0xff);
if (l >= sizeof(cresult) || l < 0)
return NULL;
if (strlcat(buf, cresult, bufsiz) >= bufsiz)
return NULL; /* result overrun */
cp++;
}
} else
return NULL; /* invalid label */
}
if (i != 0)
return NULL; /* not terminated */
cp++;
*sp = cp;
return buf;
}
static char *
_icmp_nodeinfo_query(const struct in6_addr *addr, int ifindex)
{
int s;
struct icmp6_filter filter;
struct msghdr msg;
struct cmsghdr *cmsg;
struct in6_pktinfo *pkt;
char cbuf[256];
char buf[1024];
char cbuf[256], buf[1024], *cp, *end;
int cc;
struct icmp6_fqdn_query *fq;
struct icmp6_fqdn_reply *fr;
struct _icmp_host_cache *hc;
struct icmp6_nodeinfo niq, *nir;
struct sockaddr_in6 sin6;
struct iovec iov;
fd_set s_fds, fds;
struct timeval tout;
int len;
char *name;
static struct _icmp_host_cache *hc_head;
static int pid;
static char dnsname[MAXDNAME + 1]; /* XXX: thread unsafe */
u_int32_t r1, r2;
THREAD_LOCK();
for (hc = hc_head; hc; hc = hc->hc_next) {
if (hc->hc_ifindex == ifindex
&& IN6_ARE_ADDR_EQUAL(&hc->hc_addr, addr)) {
THREAD_UNLOCK();
return hc->hc_name; /* XXX: never freed */
}
}
THREAD_UNLOCK();
if (pid == 0)
pid = getpid();
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ICMP6_FQDN_REPLY, &filter);
ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filter);
FD_ZERO(&s_fds);
tout.tv_sec = 0;
tout.tv_usec = 200000; /*XXX: 200ms*/
tout.tv_usec = 500000; /* 500ms */
fq = (struct icmp6_fqdn_query *)buf;
fq->icmp6_fqdn_type = ICMP6_FQDN_QUERY;
fq->icmp6_fqdn_code = 0;
fq->icmp6_fqdn_cksum = 0;
fq->icmp6_fqdn_id = (u_short)getpid();
fq->icmp6_fqdn_unused = 0;
fq->icmp6_fqdn_cookie[0] = 0;
fq->icmp6_fqdn_cookie[1] = 0;
memset(&niq, 0, sizeof(niq));
niq.ni_type = ICMP6_NI_QUERY;
niq.ni_code = ICMP6_NI_SUBJ_IPV6;
niq.ni_qtype = htons(NI_QTYPE_NODENAME);
niq.ni_flags = 0;
r1 = arc4random();
r2 = arc4random();
memcpy(&niq.icmp6_ni_nonce[0], &r1, sizeof(r1));
memcpy(&niq.icmp6_ni_nonce[4], &r2, sizeof(r2));
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
@ -2251,8 +2304,8 @@ _icmp_fqdn_query(const struct in6_addr *addr, int ifindex)
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
iov.iov_base = (caddr_t)buf;
iov.iov_len = sizeof(struct icmp6_fqdn_query);
iov.iov_base = (caddr_t)&niq;
iov.iov_len = sizeof(struct icmp6_nodeinfo);
if (ifindex) {
msg.msg_control = cbuf;
@ -2268,87 +2321,119 @@ _icmp_fqdn_query(const struct in6_addr *addr, int ifindex)
msg.msg_controllen = (char *)cmsg - cbuf;
}
if ((s = _socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
/* XXX: we need root privilege here */
if ((s = _socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
return NULL;
(void)_setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER,
(char *)&filter, sizeof(filter));
cc = _sendmsg(s, &msg, 0);
if (cc < 0) {
_close(s);
return NULL;
return (NULL);
}
FD_SET(s, &s_fds);
for (;;) {
fds = s_fds;
if (_select(s + 1, &fds, NULL, NULL, &tout) <= 0) {
_close(s);
return NULL;
return (NULL);
}
len = sizeof(sin6);
cc = _recvfrom(s, buf, sizeof(buf), 0,
(struct sockaddr *)&sin6, &len);
if (cc <= 0) {
_close(s);
return NULL;
return (NULL);
}
if (cc < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
if (cc < sizeof(struct icmp6_hdr))
continue;
if (!IN6_ARE_ADDR_EQUAL(addr, &sin6.sin6_addr))
nir = (struct icmp6_nodeinfo *)buf;
if (nir->ni_type != ICMP6_NI_REPLY)
continue;
fr = (struct icmp6_fqdn_reply *)(buf + sizeof(struct ip6_hdr));
if (fr->icmp6_fqdn_type == ICMP6_FQDN_REPLY)
break;
if (nir->ni_qtype != htons(NI_QTYPE_NODENAME))
continue;
if (memcmp(nir->icmp6_ni_nonce, niq.icmp6_ni_nonce,
sizeof(nir->icmp6_ni_nonce)) != 0) {
continue;
}
if (nir->ni_code != htons(ICMP6_NI_SUCCESS))
continue; /* or should we fail? */
/* this is an expected reply. */
break;
}
_close(s);
if (fr->icmp6_fqdn_cookie[1] != 0) {
/* rfc1788 type */
name = buf + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + 4;
len = (buf + cc) - name;
} else {
len = fr->icmp6_fqdn_namelen;
name = fr->icmp6_fqdn_name;
}
if (len <= 0)
return NULL;
name[len] = 0;
if ((hc = (struct _icmp_host_cache *)malloc(sizeof(*hc))) == NULL)
return NULL;
/* XXX: limit number of cached entries */
hc->hc_ifindex = ifindex;
hc->hc_addr = *addr;
hc->hc_name = strdup(name);
THREAD_LOCK();
hc->hc_next = hc_head;
hc_head = hc;
THREAD_UNLOCK();
return hc->hc_name;
memset(dnsname, 0, sizeof(dnsname));
cp = (char *)(nir + 1);
end = ((char *)nir) + cc;
if (end - cp < sizeof(int32_t)) /* for TTL. we don't use it. */
return (NULL);
cp += sizeof(int32_t);
if (*cp == end - cp - 1) { /* an old version */
int nlen;
cp++; /* skip length */
nlen = end - cp;
if (nlen > MAXDNAME)
return (NULL); /* XXX: use it anyway? */
memcpy(dnsname, cp, nlen);
} else {
/* XXX: should we use a generic function? */
if (dnsdecode((const u_char **)(void *)&cp, end,
(const u_char *)(nir + 1), dnsname, sizeof(dnsname))
== NULL) {
return (NULL); /* bogus name */
}
/* Name-lookup special handling for truncated name. */
if (cp + 1 <= end && !*cp && strlen(dnsname) > 0)
dnsname[strlen(dnsname) - 1] = '\0';
/* There may be other names, but we ignore them. */
}
return (dnsname);
}
static struct hostent *
_icmp_ghbyaddr(const void *addr, int addrlen, int af, int *errp)
static int
_icmp_ghbyaddr(void *rval, void *cb_data, va_list ap)
{
const void *addr;
int addrlen;
int af;
int *errp;
char *hname;
int ifindex;
int ifindex = 0;
struct in6_addr addr6;
if (af != AF_INET6) {
addr = va_arg(ap, const void *);
addrlen = va_arg(ap, int);
af = va_arg(ap, int);
errp = va_arg(ap, int *);
*(struct hostent **)rval = NULL;
if (af != AF_INET6 || addrlen != sizeof(addr6)) {
/*
* Note: rfc1788 defines Who Are You for IPv4,
* but no one implements it.
*/
return NULL;
return (NS_NOTFOUND);
}
memcpy(&addr6, addr, addrlen);
ifindex = (addr6.s6_addr[2] << 8) | addr6.s6_addr[3];
addr6.s6_addr[2] = addr6.s6_addr[3] = 0;
if (IN6_IS_ADDR_LINKLOCAL(&addr6)) {
ifindex = (addr6.s6_addr[2] << 8) | addr6.s6_addr[3];
addr6.s6_addr[2] = addr6.s6_addr[3] = 0;
}
if (!IN6_IS_ADDR_LINKLOCAL(&addr6))
return NULL; /*XXX*/
if ((hname = _icmp_fqdn_query(&addr6, ifindex)) == NULL)
return NULL;
return _hpaddr(af, hname, &addr6, errp);
THREAD_LOCK();
if ((hname = _icmp_nodeinfo_query(&addr6, ifindex)) == NULL) {
THREAD_UNLOCK();
return (NS_NOTFOUND);
}
*(struct hostent **)rval =_hpaddr(af, hname, &addr6, errp);
THREAD_UNLOCK();
return (NS_SUCCESS);
}
#endif /* ICMPNL */