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