xref: /illumos-gate/usr/src/lib/libslp/clib/slp_net_utils.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1999 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <syslog.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/sockio.h>
35 #include <arpa/inet.h>
36 #include <net/if.h>
37 #include <unistd.h>
38 #include <netdb.h>
39 #include <nss_dbdefs.h>
40 #include <slp-internal.h>
41 #include <slp_net_utils.h>
42 
43 typedef struct slp_ifinfo {
44 	struct sockaddr_in addr;
45 	struct sockaddr_in netmask;
46 	struct sockaddr_in bc_addr;
47 	short flags;
48 } slp_ifinfo_t;
49 
50 typedef struct slp_handle_ifinfo {
51 	slp_ifinfo_t *all_ifs;
52 	int numifs;
53 } slp_handle_ifinfo_t;
54 
55 
56 static SLPError get_all_interfaces(slp_handle_ifinfo_t *info);
57 
58 /*
59  * Obtains the broadcast addresses for all local interfaces given in
60  * addrs.
61  *
62  * hp		IN / OUT holds cached-per-handle if info
63  * given_ifs	IN	an array of local interfaces
64  * num_givenifs	IN	number of addresses in given_ifs
65  * bc_addrs	OUT	an array of broadcast addresses for local interfaces
66  * num_addrs	OUT	number of addrs returned in bc_addrs
67  *
68  * Returns SLP_OK if at least one broadcast address was found; if none
69  * were found, returns err != SLP_OK and *bc_addrs = NULL;
70  * Caller must free *bc_addrs when done.
71  */
72 SLPError slp_broadcast_addrs(slp_handle_impl_t *hp, struct in_addr *given_ifs,
73 				int num_givenifs,
74 				struct sockaddr_in *bc_addrs[],
75 				int *num_addrs) {
76 
77 	SLPError err;
78 	int i, j;
79 	slp_ifinfo_t *all_ifs;
80 	slp_handle_ifinfo_t *ifinfo;
81 	int numifs;
82 
83 	if (!hp->ifinfo) {
84 		if (!(ifinfo = malloc(sizeof (*ifinfo)))) {
85 			slp_err(LOG_CRIT, 0, "slp_broadcast_addrs",
86 				"out of memory");
87 			return (SLP_MEMORY_ALLOC_FAILED);
88 		}
89 		if ((err = get_all_interfaces(ifinfo)) != SLP_OK) {
90 			free(ifinfo);
91 			return (err);
92 		}
93 		hp->ifinfo = ifinfo;
94 	}
95 	all_ifs = ((slp_handle_ifinfo_t *)hp->ifinfo)->all_ifs;
96 	numifs = ((slp_handle_ifinfo_t *)hp->ifinfo)->numifs;
97 
98 	/* allocate memory for reply */
99 	if (!(*bc_addrs = calloc(num_givenifs, sizeof (**bc_addrs)))) {
100 		slp_err(LOG_CRIT, 0, "slp_broadcast_addrs", "out of memory");
101 		return (SLP_MEMORY_ALLOC_FAILED);
102 	}
103 
104 	/* copy bc addrs for all desired interfaces which are bc-enabled */
105 	*num_addrs = 0;
106 	for (j = 0; j < num_givenifs; j++) {
107 	    for (i = 0; i < numifs; i++) {
108 
109 		if (!(all_ifs[i].flags & IFF_BROADCAST)) {
110 			continue;
111 		}
112 
113 		if (memcmp(&(all_ifs[i].addr.sin_addr.s_addr),
114 			    &(given_ifs[j].s_addr),
115 			    sizeof (given_ifs[j].s_addr)) == 0 &&
116 		    all_ifs[i].bc_addr.sin_addr.s_addr != 0) {
117 
118 		    /* got it, so copy it to bc_addrs */
119 		    (void) memcpy(
120 				    *bc_addrs + *num_addrs,
121 				    &(all_ifs[i].bc_addr),
122 				    sizeof (all_ifs[i].bc_addr));
123 		    (*num_addrs)++;
124 
125 		    break;
126 		}
127 	    }
128 	}
129 
130 	if (*num_addrs == 0) {
131 		/* none found */
132 		free (*bc_addrs);
133 		bc_addrs = NULL;
134 		return (SLP_INTERNAL_SYSTEM_ERROR);
135 	}
136 	return (SLP_OK);
137 }
138 
139 /*
140  * Returns true if addr is on a subnet local to the local host.
141  */
142 SLPBoolean slp_on_subnet(slp_handle_impl_t *hp, struct in_addr addr) {
143 	int i;
144 	struct in_addr netmask, net_addr, masked_addr;
145 	slp_ifinfo_t *all_ifs;
146 	slp_handle_ifinfo_t *ifinfo;
147 	int numifs;
148 
149 	if (!hp->ifinfo) {
150 		if (!(ifinfo = malloc(sizeof (*ifinfo)))) {
151 			slp_err(LOG_CRIT, 0, "slp_broadcast_addrs",
152 				"out of memory");
153 			return (SLP_FALSE);
154 		}
155 		if (get_all_interfaces(ifinfo) != SLP_OK) {
156 			free(ifinfo);
157 			return (SLP_FALSE);
158 		}
159 		hp->ifinfo = ifinfo;
160 	}
161 	all_ifs = ((slp_handle_ifinfo_t *)hp->ifinfo)->all_ifs;
162 	numifs = ((slp_handle_ifinfo_t *)hp->ifinfo)->numifs;
163 
164 	for (i = 0; i < numifs; i++) {
165 		/* get netmask */
166 		netmask.s_addr = all_ifs[i].netmask.sin_addr.s_addr;
167 		/* get network address */
168 		net_addr.s_addr =
169 			all_ifs[i].addr.sin_addr.s_addr & netmask.s_addr;
170 		/* apply netmask to input addr */
171 		masked_addr.s_addr = addr.s_addr & netmask.s_addr;
172 
173 		if (memcmp(&(masked_addr.s_addr), &(net_addr.s_addr),
174 				sizeof (net_addr.s_addr)) == 0) {
175 			return (SLP_TRUE);
176 		}
177 	}
178 
179 	return (SLP_FALSE);
180 }
181 
182 /*
183  * Returns true if any local interface if configured with addr.
184  */
185 SLPBoolean slp_on_localhost(slp_handle_impl_t *hp, struct in_addr addr) {
186 	int i;
187 	slp_ifinfo_t *all_ifs;
188 	slp_handle_ifinfo_t *ifinfo;
189 	int numifs;
190 
191 	if (!hp->ifinfo) {
192 		if (!(ifinfo = malloc(sizeof (*ifinfo)))) {
193 			slp_err(LOG_CRIT, 0, "slp_broadcast_addrs",
194 				"out of memory");
195 			return (SLP_FALSE);
196 		}
197 		if (get_all_interfaces(ifinfo) != SLP_OK) {
198 			free(ifinfo);
199 			return (SLP_FALSE);
200 		}
201 		hp->ifinfo = ifinfo;
202 	}
203 	all_ifs = ((slp_handle_ifinfo_t *)hp->ifinfo)->all_ifs;
204 	numifs = ((slp_handle_ifinfo_t *)hp->ifinfo)->numifs;
205 
206 	for (i = 0; i < numifs; i++) {
207 		if (memcmp(&(addr.s_addr), &(all_ifs[i].addr.sin_addr.s_addr),
208 				sizeof (addr)) == 0) {
209 
210 			return (SLP_TRUE);
211 		}
212 	}
213 
214 	return (SLP_FALSE);
215 }
216 
217 void slp_free_ifinfo(void *hi) {
218 	free(((slp_handle_ifinfo_t *)hi)->all_ifs);
219 }
220 
221 /*
222  * Populates all_ifs.
223  */
224 static SLPError get_all_interfaces(slp_handle_ifinfo_t *info) {
225 	int i, n, s = 0;
226 	int numifs;
227 	char *buf = NULL;
228 	size_t bufsize;
229 	struct ifconf ifc;
230 	struct ifreq *ifrp, ifr;
231 	slp_ifinfo_t *all_ifs = NULL;
232 	SLPError err = SLP_OK;
233 
234 	/* create a socket with which to get interface info */
235 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
236 		goto cleanup;
237 	}
238 
239 	/* how many interfaces are configured? */
240 	if (ioctl(s, SIOCGIFNUM, (char *)&numifs) < 0) {
241 		goto cleanup;
242 	}
243 
244 	/* allocate memory for ifinfo_t array */
245 	if (!(all_ifs = calloc(numifs, sizeof (*all_ifs)))) {
246 		slp_err(LOG_CRIT, 0, "get_all_interfaces", "out of memory");
247 		err = SLP_MEMORY_ALLOC_FAILED;
248 		goto cleanup;
249 	}
250 
251 	/* allocate memory for interface info */
252 	bufsize = numifs * sizeof (struct ifreq);
253 	if (!(buf = malloc(bufsize))) {
254 		slp_err(LOG_CRIT, 0, "get_all_interfaces", "out of memory");
255 		err = SLP_MEMORY_ALLOC_FAILED;
256 		goto cleanup;
257 	}
258 
259 	/* get if info */
260 	ifc.ifc_len = bufsize;
261 	ifc.ifc_buf = buf;
262 	if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
263 		goto cleanup;
264 	}
265 
266 	ifrp = ifc.ifc_req;
267 	i = 0;
268 	for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifrp++) {
269 
270 	    /* ignore if interface is not up */
271 	    (void) memset((char *)&ifr, 0, sizeof (ifr));
272 	    (void) strncpy(ifr.ifr_name, ifrp->ifr_name, sizeof (ifr.ifr_name));
273 	    if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
274 		continue;
275 	    }
276 	    if (!(ifr.ifr_flags & IFF_UP)) {
277 		continue;
278 	    }
279 
280 	    all_ifs[i].flags = ifr.ifr_flags;
281 
282 	    /* get the interface's address */
283 	    if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) {
284 		continue;
285 	    }
286 
287 	    (void) memcpy(&(all_ifs[i].addr), &ifr.ifr_addr,
288 				sizeof (all_ifs[i].addr));
289 
290 	    /* get the interface's broadcast address */
291 	    if (ioctl(s, SIOCGIFBRDADDR, (caddr_t)&ifr) < 0) {
292 		(void) memset(&(all_ifs[i].bc_addr), 0,
293 				sizeof (all_ifs[i].bc_addr));
294 	    } else {
295 		(void) memcpy(&(all_ifs[i].bc_addr), &ifr.ifr_addr,
296 				sizeof (all_ifs[i].bc_addr));
297 	    }
298 
299 	    /* get the interface's subnet mask */
300 	    if (ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifr) < 0) {
301 		(void) memset(&(all_ifs[i].netmask), 0,
302 				sizeof (all_ifs[i].netmask));
303 	    } else {
304 		(void) memcpy(&(all_ifs[i].netmask), &ifr.ifr_addr,
305 				sizeof (all_ifs[i].netmask));
306 	    }
307 
308 	    i++;
309 	}
310 
311 	/* i contains the number we actually got info on */
312 	info->numifs = i;
313 	info->all_ifs = all_ifs;
314 
315 	if (i == 0) {
316 		err = SLP_INTERNAL_SYSTEM_ERROR;
317 		free(all_ifs);
318 		info->all_ifs = NULL;
319 	}
320 
321 cleanup:
322 	if (s) (void) close(s);
323 	if (buf) free(buf);
324 
325 	return (err);
326 }
327 
328 /*
329  * Converts a SLPSrvURL to a network address. 'sa' must have been
330  * allocated by the caller.
331  * Assumes that addresses are given as specified in the protocol spec,
332  * i.e. as IP addresses and not host names.
333  */
334 SLPError slp_surl2sin(SLPSrvURL *surl, struct sockaddr_in *sa) {
335 	struct sockaddr_in *sin = (struct sockaddr_in *)sa;
336 
337 	if (slp_pton(surl->s_pcHost, &(sin->sin_addr)) < 1)
338 		return (SLP_PARAMETER_BAD);
339 	sin->sin_family = AF_INET;
340 	/* port number */
341 	sin->sin_port = htons(
342 		(surl->s_iPort == 0 ? SLP_PORT : surl->s_iPort));
343 
344 	return (SLP_OK);
345 }
346 
347 /*
348  * A wrapper around gethostbyaddr_r. This checks the useGetXXXbyYYY
349  * property first to determine whether a name service lookup should
350  * be used. If not, it converts the address in 'addr' to a string
351  * and just returns that.
352  *
353  * The core functionality herein will be replaced with getaddrinfo
354  * when it becomes available.
355  */
356 
357 char *slp_gethostbyaddr(const char *addr, int size) {
358 	char storebuf[SLP_NETDB_BUFSZ], addrbuf[INET6_ADDRSTRLEN], *cname;
359 	const char *use_xbyy;
360 	struct hostent namestruct[1], *name;
361 	int herrno;
362 
363 	/* default: copy in the IP address */
364 	cname = slp_ntop(addrbuf, INET6_ADDRSTRLEN, (const void *) addr);
365 	if (cname && !(cname = strdup(cname))) {
366 		slp_err(LOG_CRIT, 0, "slp_gethostbyaddr", "out of memory");
367 		return (NULL);
368 	}
369 
370 	if ((use_xbyy = SLPGetProperty(SLP_CONFIG_USEGETXXXBYYYY)) != NULL &&
371 	    strcasecmp(use_xbyy, "false") == 0) {
372 		return (cname);
373 	}
374 
375 	while (!(name = gethostbyaddr_r(addr, size,
376 					AF_INET,
377 					namestruct,
378 					storebuf,
379 					SLP_NETDB_BUFSZ,
380 					&herrno))) {
381 		switch (herrno) {
382 		case NO_RECOVERY:
383 		case NO_DATA:
384 			return (cname);
385 		case TRY_AGAIN:
386 			continue;
387 		default:
388 			return (cname);	/* IP address */
389 		}
390 	}
391 
392 	free(cname);
393 	if (!(cname = strdup(name->h_name))) {
394 		slp_err(LOG_CRIT, 0, "slp_gethostbyaddr", "out of memory");
395 		return (NULL);
396 	}
397 
398 	return (cname);
399 }
400 
401 /* @@@ currently getting these from libresolv2 -> change? */
402 
403 /*
404  * Converts the address pointed to by 'addr' to a string. Currently
405  * just calls inet_ntoa, but is structured to be a wrapper to
406  * inet_ntop. Returns NULL on failure.
407  *
408  * This wrapper allows callers to be protocol agnostic. Right now it
409  * only handles IPv4.
410  */
411 /*ARGSUSED*/
412 char *slp_ntop(char *buf, int buflen, const void *addr) {
413 	return (inet_ntoa(*(struct in_addr *)addr));
414 }
415 
416 /*
417  * convert from presentation format (which usually means ASCII printable)
418  * to network format (which is usually some kind of binary format).
419  * return:
420  *	1 if the address was valid for the specified address family
421  *	0 if the address wasn't valid (`dst' is untouched in this case)
422  *	-1 if some other error occurred (`dst' is untouched in this case, too)
423  *
424  * This wrapper allows callers to be protocol agnostic. Right now it
425  * only handles IPv4.
426  */
427 int slp_pton(const char *addrstr, void *addr) {
428 	return (inet_pton(AF_INET, addrstr, addr));
429 }
430