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 */
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 */
check_nigroup(const struct in6_addr * addr)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 
encode_dnsname(const char * name,char * buf,size_t buflen,int fqdn)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 
compare_dnsname(const char * s,size_t slen,const char * n,size_t nlen)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 
nodeinfo_group(const char * dnsname,int namelen,struct in6_addr * nigroup)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 /* ---------- */
init_nodeinfo_nodename(int forced)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 */
pr_nodeinfo_nodename(CHECKANDFILL_ARGS)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