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