/* $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; }