You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
390 lines
9.0 KiB
390 lines
9.0 KiB
/* $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;
|
|
}
|
|
|