/* $USAGI: ninfod_name.c,v 1.15 2003-01-11 14:33:28 yoshfuji Exp $ */
/*
* Copyright (C) 2002 USAGI/WIDE Project.
* 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.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS 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.
*/
/*
* Author:
* YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if STDC_HEADERS
# include <stdio.h>
# include <stdlib.h>
# include <stddef.h>
# include <ctype.h>
#else
# if HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#if HAVE_STRING_H
# if !STDC_HEADERS && HAVE_MEMORY_H
# include <memory.h>
# endif
# include <string.h>
#endif
#if HAVE_STRINGS_H
# include <strings.h>
#endif
#if HAVE_INTTYPES_H
# include <inttypes.h>
#else
# if HAVE_STDINT_H
# include <stdint.h>
# endif
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#if HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#include <sys/socket.h>
#if HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#if HAVE_NETINET_ICMP6_H
# include <netinet/icmp6.h>
#endif
#ifndef HAVE_STRUCT_ICMP6_NODEINFO
# include "icmp6_nodeinfo.h"
#endif
#include <arpa/inet.h>
#if defined(HAVE_GNUTLS_OPENSSL_H)
# include <gnutls/openssl.h>
#elif defined(HAVE_OPENSSL_MD5_H)
# include <openssl/md5.h>
#endif
#if HAVE_SYS_UTSNAME_H
# include <sys/utsname.h>
#endif
#if HAVE_NETDB_H
# include <netdb.h>
#endif
#include <errno.h>
#if HAVE_SYSLOG_H
# include <syslog.h>
#endif
#include "ninfod.h"
#ifndef offsetof
# define offsetof(aggregate,member) ((size_t)&((aggregate *)0)->member)
#endif
/* Hmm,,, */
#ifndef IPV6_JOIN_GROUP
# define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
# define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
#endif
/* ---------- */
/* ID */
static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_name.c,v 1.15 2003-01-11 14:33:28 yoshfuji Exp $";
/* Variables */
static struct utsname utsname;
static char *uts_nodename = utsname.nodename;
char nodename[MAX_DNSNAME_SIZE];
static size_t nodenamelen;
static struct ipv6_mreq nigroup;
/* ---------- */
/* Functions */
int check_nigroup(const struct in6_addr *addr)
{
return IN6_IS_ADDR_MULTICAST(&nigroup.ipv6mr_multiaddr) &&
IN6_ARE_ADDR_EQUAL(&nigroup.ipv6mr_multiaddr, addr);
}
static int encode_dnsname(const char *name,
char *buf, size_t buflen,
int fqdn)
{
size_t namelen;
int i;
if (buflen < 0)
return -1;
namelen = strlen(name);
if (namelen == 0)
return 0;
if (namelen > 255 || buflen < namelen+1)
return -1;
i = 0;
while(i <= namelen) {
const char *e;
int llen, ii;
e = strchr(&name[i], '.');
if (e == NULL)
e = name + namelen;
llen = e - &name[i];
if (llen == 0) {
if (*e)
return -1;
if (fqdn < 0)
return -1;
fqdn = 1;
break;
}
if (llen >= 0x40)
return -1;
buf[i] = llen;
for (ii = 0; ii < llen; ii++) {
if (!isascii(name[i+ii]))
return -1;
if (ii == 0 || ii == llen-1) {
if (!isalpha(name[i+ii]) && !isdigit(name[i+ii]))
return -1;
} else if (!isalnum(name[i+ii]) && name[i+ii] != '-')
return -1;
buf[i+ii+1] = isupper(name[i+ii]) ? tolower(name[i+ii]) : name[i+ii];
}
i += llen + 1;
}
if (buflen < i + 1 + !(fqdn > 0))
return -1;
buf[i++] = 0;
if (!(fqdn > 0))
buf[i++] = 0;
return i;
}
static int compare_dnsname(const char *s, size_t slen,
const char *n, size_t nlen)
{
const char *s0 = s, *n0 = n;
int done = 0, retcode = 0;
if (slen < 1 || nlen < 1)
return -1; /* invalid length */
/* simple case */
if (slen == nlen && memcmp(s, n, slen) == 0)
return 0;
if (*(s0 + slen - 1) || *(n0 + nlen - 1))
return -1; /* invalid termination */
while (s < s0 + slen && n < n0 + nlen) {
if (*s >= 0x40 || *n >= 0x40)
return -1; /* DNS compression is not allowed here */
if (s + *s + 1 > s0 + slen || n + *n + 1 > n0 + nlen)
return -1; /* overrun */
if (*s == '\0') {
if (s == s0 + slen - 1)
break; /* FQDN */
else if (s + 1 == s0 + slen - 1)
return retcode; /* truncated */
else
return -1; /* more than one subject */
}
if (!done) {
if (*n == '\0') {
if (n == n0 + nlen - 1) {
done = 1; /* FQDN */
} else if (n + 1 == n0 + nlen - 1) {
retcode = 1; // trunc
done = 1;
} else
return -1;
} else {
if (*s != *n) {
done = 1;
retcode = 1;
} else {
if (memcmp(s+1, n+1, *s)) {
done = 1;
retcode = 1;
}
}
}
}
s += *s + 1;
n += done ? 0 : (*n + 1);
}
return retcode;
}
static int nodeinfo_group(const char *dnsname, int namelen,
struct in6_addr *nigroup)
{
MD5_CTX ctxt;
unsigned char digest[16];
if (!dnsname || !nigroup)
return -1;
MD5_Init(&ctxt);
MD5_Update(&ctxt, dnsname, *dnsname);
MD5_Final(digest, &ctxt);
#ifdef s6_addr32
nigroup->s6_addr32[0] = htonl(0xff020000);
nigroup->s6_addr32[1] = 0;
nigroup->s6_addr32[2] = htonl(0x00000002);
#else
memset(nigroup, 0, sizeof(*nigroup));
nigroup->s6_addr[ 0] = 0xff;
nigroup->s6_addr[ 1] = 0x02;
nigroup->s6_addr[11] = 0x02;
#endif
memcpy(&nigroup->s6_addr[12], digest, 4);
return 0;
}
/* ---------- */
void init_nodeinfo_nodename(int forced)
{
struct utsname newname;
int len;
int changed = 0;
DEBUG(LOG_DEBUG, "%s()\n", __func__);
uname(&newname);
changed = strcmp(newname.nodename, utsname.nodename);
if (!changed && !forced)
return;
memcpy(&utsname, &newname, sizeof(newname));
/* leave old group */
if ((changed || forced) && !IN6_IS_ADDR_UNSPECIFIED(&nigroup.ipv6mr_multiaddr)) {
if (setsockopt(sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &nigroup, sizeof(nigroup)) < 0) {
#if ENABLE_DEBUG
char niaddrbuf[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, &nigroup, niaddrbuf, sizeof(niaddrbuf)) == NULL)
strcpy(niaddrbuf, "???");
#endif
DEBUG(LOG_WARNING,
"%s(): failed to leave group %s.\n",
__func__, niaddrbuf);
memset(&nigroup, 0, sizeof(nigroup));
}
}
len = encode_dnsname(uts_nodename,
nodename,
sizeof(nodename),
0);
/* setup ni reply */
nodenamelen = len > 0 ? len : 0;
/* setup ni group */
if (changed || forced) {
if (nodenamelen) {
memset(&nigroup, 0, sizeof(nigroup));
nodeinfo_group(nodename, len, &nigroup.ipv6mr_multiaddr);
nigroup.ipv6mr_interface = 0;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &nigroup, sizeof(nigroup)) < 0) {
#if ENABLE_DEBUG
char niaddrbuf[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, &nigroup, niaddrbuf, sizeof(niaddrbuf)) == NULL)
strcpy(niaddrbuf, "???");
#endif
DEBUG(LOG_WARNING,
"%s(): failed to join group %s.\n",
__func__, niaddrbuf);
memset(&nigroup, 0, sizeof(nigroup));
}
} else {
memset(&nigroup, 0, sizeof(nigroup));
}
}
return;
}
/* ---------- */
/* nodename */
int pr_nodeinfo_nodename(CHECKANDFILL_ARGS)
{
DEBUG(LOG_DEBUG, "%s()\n", __func__);
if (subject) {
if (!nodenamelen ||
compare_dnsname(subject, subjlen,
nodename,
nodenamelen))
return 1;
if (subj_if)
*subj_if = p->pktinfo.ipi6_ifindex;
}
if (reply) {
uint32_t ttl = 0;
p->reply.ni_type = ICMP6_NI_REPLY;
p->reply.ni_code = ICMP6_NI_SUCCESS;
p->reply.ni_cksum = 0;
p->reply.ni_qtype = htons(NI_QTYPE_DNSNAME);
p->reply.ni_flags = 0;
p->replydatalen = nodenamelen ? sizeof(ttl)+nodenamelen : 0;
p->replydata = nodenamelen ? ni_malloc(p->replydatalen) : NULL;
if (p->replydata) {
memcpy(p->replydata, &ttl, sizeof(ttl));
memcpy(p->replydata + sizeof(ttl), &nodename, nodenamelen);
}
}
return 0;
}