1/* $USAGI: ninfod_name.c,v 1.15 2003-01-11 14:33:28 yoshfuji Exp $ */ 2/* 3 * Copyright (C) 2002 USAGI/WIDE Project. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the project nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30/* 31 * Author: 32 * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> 33 */ 34 35#if HAVE_CONFIG_H 36#include "config.h" 37#endif 38 39#if HAVE_SYS_TYPES_H 40# include <sys/types.h> 41#endif 42#if STDC_HEADERS 43# include <stdio.h> 44# include <stdlib.h> 45# include <stddef.h> 46# include <ctype.h> 47#else 48# if HAVE_STDLIB_H 49# include <stdlib.h> 50# endif 51#endif 52#if HAVE_STRING_H 53# if !STDC_HEADERS && HAVE_MEMORY_H 54# include <memory.h> 55# endif 56# include <string.h> 57#endif 58#if HAVE_STRINGS_H 59# include <strings.h> 60#endif 61#if HAVE_INTTYPES_H 62# include <inttypes.h> 63#else 64# if HAVE_STDINT_H 65# include <stdint.h> 66# endif 67#endif 68#if HAVE_UNISTD_H 69# include <unistd.h> 70#endif 71 72#if TIME_WITH_SYS_TIME 73# include <sys/time.h> 74# include <time.h> 75#else 76# if HAVE_SYS_TIME_H 77# include <sys/time.h> 78# else 79# include <time.h> 80# endif 81#endif 82 83#if HAVE_SYS_UIO_H 84#include <sys/uio.h> 85#endif 86 87#include <sys/socket.h> 88 89#if HAVE_NETINET_IN_H 90# include <netinet/in.h> 91#endif 92 93#if HAVE_NETINET_ICMP6_H 94# include <netinet/icmp6.h> 95#endif 96#ifndef HAVE_STRUCT_ICMP6_NODEINFO 97# include "icmp6_nodeinfo.h" 98#endif 99 100#include <arpa/inet.h> 101 102#if defined(HAVE_GNUTLS_OPENSSL_H) 103# include <gnutls/openssl.h> 104#elif defined(HAVE_OPENSSL_MD5_H) 105# include <openssl/md5.h> 106#endif 107 108#if HAVE_SYS_UTSNAME_H 109# include <sys/utsname.h> 110#endif 111#if HAVE_NETDB_H 112# include <netdb.h> 113#endif 114#include <errno.h> 115 116#if HAVE_SYSLOG_H 117# include <syslog.h> 118#endif 119 120#include "ninfod.h" 121 122#ifndef offsetof 123# define offsetof(aggregate,member) ((size_t)&((aggregate *)0)->member) 124#endif 125 126/* Hmm,,, */ 127#ifndef IPV6_JOIN_GROUP 128# define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP 129# define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP 130#endif 131 132/* ---------- */ 133/* ID */ 134static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_name.c,v 1.15 2003-01-11 14:33:28 yoshfuji Exp $"; 135 136/* Variables */ 137static struct utsname utsname; 138static char *uts_nodename = utsname.nodename; 139 140char nodename[MAX_DNSNAME_SIZE]; 141static size_t nodenamelen; 142 143static struct ipv6_mreq nigroup; 144 145/* ---------- */ 146/* Functions */ 147int check_nigroup(const struct in6_addr *addr) 148{ 149 return IN6_IS_ADDR_MULTICAST(&nigroup.ipv6mr_multiaddr) && 150 IN6_ARE_ADDR_EQUAL(&nigroup.ipv6mr_multiaddr, addr); 151} 152 153static int encode_dnsname(const char *name, 154 char *buf, size_t buflen, 155 int fqdn) 156{ 157 size_t namelen; 158 int i; 159 160 if (buflen < 0) 161 return -1; 162 163 namelen = strlen(name); 164 if (namelen == 0) 165 return 0; 166 if (namelen > 255 || buflen < namelen+1) 167 return -1; 168 169 i = 0; 170 while(i <= namelen) { 171 const char *e; 172 int llen, ii; 173 174 e = strchr(&name[i], '.'); 175 if (e == NULL) 176 e = name + namelen; 177 llen = e - &name[i]; 178 if (llen == 0) { 179 if (*e) 180 return -1; 181 if (fqdn < 0) 182 return -1; 183 fqdn = 1; 184 break; 185 } 186 if (llen >= 0x40) 187 return -1; 188 buf[i] = llen; 189 for (ii = 0; ii < llen; ii++) { 190 if (!isascii(name[i+ii])) 191 return -1; 192 if (ii == 0 || ii == llen-1) { 193 if (!isalpha(name[i+ii]) && !isdigit(name[i+ii])) 194 return -1; 195 } else if (!isalnum(name[i+ii]) && name[i+ii] != '-') 196 return -1; 197 buf[i+ii+1] = isupper(name[i+ii]) ? tolower(name[i+ii]) : name[i+ii]; 198 } 199 i += llen + 1; 200 } 201 if (buflen < i + 1 + !(fqdn > 0)) 202 return -1; 203 buf[i++] = 0; 204 if (!(fqdn > 0)) 205 buf[i++] = 0; 206 return i; 207} 208 209static int compare_dnsname(const char *s, size_t slen, 210 const char *n, size_t nlen) 211{ 212 const char *s0 = s, *n0 = n; 213 int done = 0, retcode = 0; 214 if (slen < 1 || nlen < 1) 215 return -1; /* invalid length */ 216 /* simple case */ 217 if (slen == nlen && memcmp(s, n, slen) == 0) 218 return 0; 219 if (*(s0 + slen - 1) || *(n0 + nlen - 1)) 220 return -1; /* invalid termination */ 221 while (s < s0 + slen && n < n0 + nlen) { 222 if (*s >= 0x40 || *n >= 0x40) 223 return -1; /* DNS compression is not allowed here */ 224 if (s + *s + 1 > s0 + slen || n + *n + 1 > n0 + nlen) 225 return -1; /* overrun */ 226 if (*s == '\0') { 227 if (s == s0 + slen - 1) 228 break; /* FQDN */ 229 else if (s + 1 == s0 + slen - 1) 230 return retcode; /* truncated */ 231 else 232 return -1; /* more than one subject */ 233 } 234 if (!done) { 235 if (*n == '\0') { 236 if (n == n0 + nlen - 1) { 237 done = 1; /* FQDN */ 238 } else if (n + 1 == n0 + nlen - 1) { 239 retcode = 1; // trunc 240 done = 1; 241 } else 242 return -1; 243 } else { 244 if (*s != *n) { 245 done = 1; 246 retcode = 1; 247 } else { 248 if (memcmp(s+1, n+1, *s)) { 249 done = 1; 250 retcode = 1; 251 } 252 } 253 } 254 } 255 s += *s + 1; 256 n += done ? 0 : (*n + 1); 257 } 258 return retcode; 259} 260 261static int nodeinfo_group(const char *dnsname, int namelen, 262 struct in6_addr *nigroup) 263{ 264 MD5_CTX ctxt; 265 unsigned char digest[16]; 266 267 if (!dnsname || !nigroup) 268 return -1; 269 270 MD5_Init(&ctxt); 271 MD5_Update(&ctxt, dnsname, *dnsname); 272 MD5_Final(digest, &ctxt); 273 274#ifdef s6_addr32 275 nigroup->s6_addr32[0] = htonl(0xff020000); 276 nigroup->s6_addr32[1] = 0; 277 nigroup->s6_addr32[2] = htonl(0x00000002); 278#else 279 memset(nigroup, 0, sizeof(*nigroup)); 280 nigroup->s6_addr[ 0] = 0xff; 281 nigroup->s6_addr[ 1] = 0x02; 282 nigroup->s6_addr[11] = 0x02; 283#endif 284 memcpy(&nigroup->s6_addr[12], digest, 4); 285 286 return 0; 287} 288 289/* ---------- */ 290void init_nodeinfo_nodename(int forced) 291{ 292 struct utsname newname; 293 int len; 294 int changed = 0; 295 296 DEBUG(LOG_DEBUG, "%s()\n", __func__); 297 298 uname(&newname); 299 changed = strcmp(newname.nodename, utsname.nodename); 300 301 if (!changed && !forced) 302 return; 303 304 memcpy(&utsname, &newname, sizeof(newname)); 305 306 /* leave old group */ 307 if ((changed || forced) && !IN6_IS_ADDR_UNSPECIFIED(&nigroup.ipv6mr_multiaddr)) { 308 if (setsockopt(sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &nigroup, sizeof(nigroup)) < 0) { 309#if ENABLE_DEBUG 310 char niaddrbuf[INET6_ADDRSTRLEN]; 311 if (inet_ntop(AF_INET6, &nigroup, niaddrbuf, sizeof(niaddrbuf)) == NULL) 312 strcpy(niaddrbuf, "???"); 313#endif 314 DEBUG(LOG_WARNING, 315 "%s(): failed to leave group %s.\n", 316 __func__, niaddrbuf); 317 memset(&nigroup, 0, sizeof(nigroup)); 318 } 319 } 320 321 len = encode_dnsname(uts_nodename, 322 nodename, 323 sizeof(nodename), 324 0); 325 326 /* setup ni reply */ 327 nodenamelen = len > 0 ? len : 0; 328 329 /* setup ni group */ 330 if (changed || forced) { 331 if (nodenamelen) { 332 memset(&nigroup, 0, sizeof(nigroup)); 333 nodeinfo_group(nodename, len, &nigroup.ipv6mr_multiaddr); 334 nigroup.ipv6mr_interface = 0; 335 if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &nigroup, sizeof(nigroup)) < 0) { 336#if ENABLE_DEBUG 337 char niaddrbuf[INET6_ADDRSTRLEN]; 338 if (inet_ntop(AF_INET6, &nigroup, niaddrbuf, sizeof(niaddrbuf)) == NULL) 339 strcpy(niaddrbuf, "???"); 340#endif 341 DEBUG(LOG_WARNING, 342 "%s(): failed to join group %s.\n", 343 __func__, niaddrbuf); 344 memset(&nigroup, 0, sizeof(nigroup)); 345 } 346 } else { 347 memset(&nigroup, 0, sizeof(nigroup)); 348 } 349 } 350 351 return; 352} 353 354/* ---------- */ 355/* nodename */ 356int pr_nodeinfo_nodename(CHECKANDFILL_ARGS) 357{ 358 DEBUG(LOG_DEBUG, "%s()\n", __func__); 359 360 if (subject) { 361 if (!nodenamelen || 362 compare_dnsname(subject, subjlen, 363 nodename, 364 nodenamelen)) 365 return 1; 366 if (subj_if) 367 *subj_if = p->pktinfo.ipi6_ifindex; 368 } 369 370 if (reply) { 371 uint32_t ttl = 0; 372 373 p->reply.ni_type = ICMP6_NI_REPLY; 374 p->reply.ni_code = ICMP6_NI_SUCCESS; 375 p->reply.ni_cksum = 0; 376 p->reply.ni_qtype = htons(NI_QTYPE_DNSNAME); 377 p->reply.ni_flags = 0; 378 379 p->replydatalen = nodenamelen ? sizeof(ttl)+nodenamelen : 0; 380 p->replydata = nodenamelen ? ni_malloc(p->replydatalen) : NULL; 381 if (p->replydata) { 382 memcpy(p->replydata, &ttl, sizeof(ttl)); 383 memcpy(p->replydata + sizeof(ttl), &nodename, nodenamelen); 384 } 385 } 386 387 return 0; 388} 389 390