1/*	$NetBSD: util.c,v 1.24 2020/06/17 00:16:22 kamil Exp $	*/
2/* $FreeBSD: head/usr.sbin/rpcbind/util.c 300973 2016-05-29 20:28:01Z ngie $ */
3
4/*-
5 * Copyright (c) 2000 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Frank van der Linden.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <sys/queue.h>
36#include <net/if.h>
37#include <netinet/in.h>
38#include <assert.h>
39#include <ifaddrs.h>
40#include <poll.h>
41#include <rpc/rpc.h>
42#include <errno.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46#include <netdb.h>
47#include <netconfig.h>
48#include <stdio.h>
49#include <arpa/inet.h>
50#include <err.h>
51
52#ifdef RPCBIND_RUMP
53#include <rump/rump.h>
54#include <rump/rump_syscallshotgun.h>
55#include <rump/rump_syscalls.h>
56#endif
57
58#include "rpcbind.h"
59
60static struct sockaddr_in *local_in4;
61#ifdef INET6
62static struct sockaddr_in6 *local_in6;
63#endif
64
65static int bitmaskcmp(void *, void *, void *, int);
66
67/*
68 * For all bits set in "mask", compare the corresponding bits in
69 * "dst" and "src", and see if they match. Returns 0 if the addresses
70 * match.
71 */
72static int
73bitmaskcmp(void *dst, void *src, void *mask, int bytelen)
74{
75	int i, j;
76	u_int8_t *p1 = dst, *p2 = src, *netmask = mask;
77	u_int8_t bitmask;
78
79	for (i = 0; i < bytelen; i++) {
80		for (j = 0; j < 8; j++) {
81			bitmask = 1 << j;
82			if (!(netmask[i] & bitmask))
83				continue;
84			if ((p1[i] & bitmask) != (p2[i] & bitmask))
85				return 1;
86		}
87	}
88
89	return 0;
90}
91
92char *
93addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
94    const char *netid)
95{
96	struct ifaddrs *ifap, *ifp, *bestif;
97#ifdef INET6
98	struct sockaddr_in6 *servsin6, *sin6mask, *clntsin6, *ifsin6, *realsin6;
99	struct sockaddr_in6 *newsin6;
100#endif
101	struct sockaddr_in *servsin, *sinmask, *clntsin, *newsin, *ifsin;
102	struct netbuf *serv_nbp, *clnt_nbp = NULL, tbuf;
103	struct sockaddr *serv_sa;
104	struct sockaddr *clnt_sa;
105	struct sockaddr_storage ss;
106	struct netconfig *nconf;
107	struct sockaddr *clnt = caller->buf;
108	char *ret = NULL;
109
110#ifdef INET6
111	servsin6 = ifsin6 = newsin6 = NULL;	/* XXXGCC -Wuninitialized */
112#endif
113	servsin = newsin = NULL;		/* XXXGCC -Wuninitialized */
114
115#ifdef RPCBIND_DEBUG
116	if (debugging)
117		fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr,
118		    clnt_uaddr, netid);
119#endif
120	nconf = getnetconfigent(netid);
121	if (nconf == NULL)
122		return NULL;
123
124	/*
125	 * Local merge, just return a duplicate.
126	 */
127	if (clnt_uaddr != NULL && strncmp(clnt_uaddr, "0.0.0.0.", 8) == 0)
128		return strdup(clnt_uaddr);
129
130	serv_nbp = uaddr2taddr(nconf, serv_uaddr);
131	if (serv_nbp == NULL)
132		return NULL;
133
134	serv_sa = serv_nbp->buf;
135	if (clnt_uaddr != NULL) {
136		clnt_nbp = uaddr2taddr(nconf, clnt_uaddr);
137		if (clnt_nbp == NULL) {
138			free(serv_nbp);
139			return NULL;
140		}
141		clnt_sa = clnt_nbp->buf;
142		if (clnt_sa->sa_family == AF_LOCAL) {
143			free(serv_nbp);
144			free(clnt_nbp);
145			free(clnt_sa);
146			return strdup(serv_uaddr);
147		}
148	} else {
149		clnt_sa = malloc(clnt->sa_len);
150		if (clnt_sa == NULL) {
151			free(serv_nbp);
152			return NULL;
153		}
154		memcpy(clnt_sa, clnt, clnt->sa_len);
155	}
156
157	if (getifaddrs(&ifp) < 0) {
158		free(serv_nbp);
159		free(clnt_sa);
160		if (clnt_nbp != NULL)
161			free(clnt_nbp);
162		return 0;
163	}
164
165	/*
166	 * Loop through all interfaces. For each interface, see if the
167	 * network portion of its address is equal to that of the client.
168	 * If so, we have found the interface that we want to use.
169	 */
170	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
171		if (ifap->ifa_addr->sa_family != clnt->sa_family ||
172		    !(ifap->ifa_flags & IFF_UP))
173			continue;
174
175		switch (clnt->sa_family) {
176		case AF_INET:
177			/*
178			 * realsin: address that recvfrom gave us.
179			 * ifsin: address of interface being examined.
180			 * clntsin: address that client want us to contact
181			 *           it on
182			 * servsin: local address of RPC service.
183			 * sinmask: netmask of this interface
184			 * newsin: initially a copy of clntsin, eventually
185			 *         the merged address
186			 */
187			servsin = (struct sockaddr_in *)serv_sa;
188			clntsin = (struct sockaddr_in *)clnt_sa;
189			sinmask = (struct sockaddr_in *)ifap->ifa_netmask;
190			newsin = (struct sockaddr_in *)&ss;
191			ifsin = (struct sockaddr_in *)ifap->ifa_addr;
192			if (!bitmaskcmp(&ifsin->sin_addr, &clntsin->sin_addr,
193			    &sinmask->sin_addr, sizeof (struct in_addr))) {
194				goto found;
195			}
196			break;
197#ifdef INET6
198		case AF_INET6:
199			/*
200			 * realsin6: address that recvfrom gave us.
201			 * ifsin6: address of interface being examined.
202			 * clntsin6: address that client want us to contact
203			 *           it on
204			 * servsin6: local address of RPC service.
205			 * sin6mask: netmask of this interface
206			 * newsin6: initially a copy of clntsin, eventually
207			 *          the merged address
208			 *
209			 * For v6 link local addresses, if the client contacted
210			 * us via a link-local address, and wants us to reply
211			 * to one, use the scope id to see which one.
212			 */
213			realsin6 = (struct sockaddr_in6 *)clnt;
214			ifsin6 = (struct sockaddr_in6 *)ifap->ifa_addr;
215			inet6_getscopeid(ifsin6, 1);
216			clntsin6 = (struct sockaddr_in6 *)clnt_sa;
217			servsin6 = (struct sockaddr_in6 *)serv_sa;
218			sin6mask = (struct sockaddr_in6 *)ifap->ifa_netmask;
219			newsin6 = (struct sockaddr_in6 *)&ss;
220			if (IN6_IS_ADDR_LINKLOCAL(&ifsin6->sin6_addr) &&
221			    IN6_IS_ADDR_LINKLOCAL(&realsin6->sin6_addr) &&
222			    IN6_IS_ADDR_LINKLOCAL(&clntsin6->sin6_addr)) {
223				if (ifsin6->sin6_scope_id !=
224				    realsin6->sin6_scope_id)
225					continue;
226				goto found;
227			}
228			if (!bitmaskcmp(&ifsin6->sin6_addr,
229			    &clntsin6->sin6_addr, &sin6mask->sin6_addr,
230			    sizeof (struct in6_addr)))
231				goto found;
232			break;
233#endif
234		default:
235			goto freeit;
236		}
237	}
238	/*
239	 * Didn't find anything. Get the first possibly useful interface,
240	 * preferring "normal" interfaces to point-to-point and loopback
241	 * ones.
242	 */
243	bestif = NULL;
244	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
245		if (ifap->ifa_addr->sa_family != clnt->sa_family ||
246		    !(ifap->ifa_flags & IFF_UP))
247			continue;
248		if (!(ifap->ifa_flags & IFF_LOOPBACK) &&
249		    !(ifap->ifa_flags & IFF_POINTOPOINT)) {
250			bestif = ifap;
251			break;
252		}
253		if (bestif == NULL)
254			bestif = ifap;
255		else if ((bestif->ifa_flags & IFF_LOOPBACK) &&
256		    !(ifap->ifa_flags & IFF_LOOPBACK))
257			bestif = ifap;
258	}
259	ifap = bestif;
260found:
261	switch (clnt->sa_family) {
262	case AF_INET:
263		memcpy(newsin, ifap->ifa_addr, clnt_sa->sa_len);
264		newsin->sin_port = servsin->sin_port;
265		tbuf.len = clnt_sa->sa_len;
266		tbuf.maxlen = sizeof (struct sockaddr_storage);
267		tbuf.buf = newsin;
268		break;
269#ifdef INET6
270	case AF_INET6:
271		memcpy(newsin6, ifsin6, clnt_sa->sa_len);
272		newsin6->sin6_port = servsin6->sin6_port;
273		tbuf.maxlen = sizeof (struct sockaddr_storage);
274		tbuf.len = clnt_sa->sa_len;
275		tbuf.buf = newsin6;
276		break;
277#endif
278	default:
279		goto freeit;
280	}
281	if (ifap != NULL)
282		ret = taddr2uaddr(nconf, &tbuf);
283freeit:
284	freenetconfigent(nconf);
285	free(serv_sa);
286	free(serv_nbp);
287	if (clnt_sa != NULL)
288		free(clnt_sa);
289	if (clnt_nbp != NULL)
290		free(clnt_nbp);
291	freeifaddrs(ifp);
292
293#ifdef RPCBIND_DEBUG
294	if (debugging)
295		fprintf(stderr, "addrmerge: returning %s\n", ret);
296#endif
297	return ret;
298}
299
300void
301network_init()
302{
303#ifdef INET6
304	struct ifaddrs *ifap, *ifp;
305	struct ipv6_mreq mreq6;
306	unsigned int ifindex;
307	int s;
308#endif
309	int ecode;
310	struct addrinfo hints, *res;
311
312	memset(&hints, 0, sizeof hints);
313	hints.ai_family = AF_INET;
314	if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
315		if (debugging)
316			fprintf(stderr, "can't get local ip4 address: %s\n",
317			    gai_strerror(ecode));
318	} else {
319		local_in4 = malloc(sizeof(*local_in4));
320		if (local_in4 == NULL) {
321			if (debugging)
322				fprintf(stderr, "can't alloc local ip4 addr\n");
323		}
324		memcpy(local_in4, res->ai_addr, sizeof *local_in4);
325	}
326
327#ifdef INET6
328	hints.ai_family = AF_INET6;
329	if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
330		if (debugging)
331			fprintf(stderr, "can't get local ip6 address: %s\n",
332			    gai_strerror(ecode));
333	} else {
334		local_in6 = malloc(sizeof(*local_in6));
335		if (local_in6 == NULL) {
336			if (debugging)
337				fprintf(stderr, "can't alloc local ip6 addr\n");
338		}
339		memcpy(local_in6, res->ai_addr, sizeof *local_in6);
340	}
341
342	/*
343	 * Now join the RPC ipv6 multicast group on all interfaces.
344	 */
345	if (getifaddrs(&ifp) < 0)
346		return;
347
348	mreq6.ipv6mr_interface = 0;
349	inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr);
350
351	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
352
353	/*
354	 * Loop through all interfaces. For each interface, see if the
355	 * network portion of its address is equal to that of the client.
356	 * If so, we have found the interface that we want to use.
357	 */
358	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
359		if (ifap->ifa_addr->sa_family != AF_INET6 ||
360		    !(ifap->ifa_flags & IFF_MULTICAST))
361			continue;
362		ifindex = if_nametoindex(ifap->ifa_name);
363		if (ifindex == mreq6.ipv6mr_interface)
364			/*
365			 * Already did this one.
366			 */
367			continue;
368		mreq6.ipv6mr_interface = ifindex;
369		if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
370		    sizeof mreq6) < 0)
371			if (debugging)
372				warn("setsockopt v6 multicast");
373	}
374	freeifaddrs(ifp);
375#endif
376
377	/* close(s); */
378}
379
380struct sockaddr *
381local_sa(int af)
382{
383	switch (af) {
384	case AF_INET:
385		return (struct sockaddr *)local_in4;
386#ifdef INET6
387	case AF_INET6:
388		return (struct sockaddr *)local_in6;
389#endif
390	default:
391		return NULL;
392	}
393}
394