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 (at) 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 */ 134 static char *RCSID __attribute__ ((unused)) = "$USAGI: ninfod_name.c,v 1.15 2003-01-11 14:33:28 yoshfuji Exp $"; 135 136 /* Variables */ 137 static struct utsname utsname; 138 static char *uts_nodename = utsname.nodename; 139 140 char nodename[MAX_DNSNAME_SIZE]; 141 static size_t nodenamelen; 142 143 static struct ipv6_mreq nigroup; 144 145 /* ---------- */ 146 /* Functions */ 147 int 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 153 static 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 209 static 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 261 static 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 /* ---------- */ 290 void 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 */ 356 int 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