xref: /illumos-gate/usr/src/cmd/hal/addons/network-devices/common.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  *
5  * Licensed under the Academic Free License version 2.1
6  */
7 
8 #pragma ident	"%Z%%M%	%I%	%E% SMI"
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <sys/ioctl.h>
18 #include <sys/sockio.h>
19 #include <net/if.h>
20 #include <net/if_arp.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <netdb.h>
24 
25 #include <libhal.h>
26 #include <logger.h>
27 
28 #include <glib.h>
29 
30 #include "network-discovery.h"
31 #define	NP(x)	(x?x:"NULL")
32 
33 extern int snmp_printer_info(char *hostname, char *community,
34 		char **manufacturer, char **model, char **description,
35 		char **serial_no, char ***command_set, char **uri);
36 
37 void
38 network_device_name_to_udi(char *udi, size_t size, ...)
39 {
40 	va_list ap;
41 	char *element;
42 	int i;
43 
44 	udi[0] = '\0';
45 	va_start(ap, size);
46 	while ((element = va_arg(ap, char *)) != NULL) {
47 		if (element[0] != '/')
48 			strlcat(udi, "/", size);
49 		strlcat(udi, element, size);
50 	}
51 	va_end(ap);
52 
53 	for (i = 0; udi[i] != NULL; i++)
54 		if (udi[i] == '.')
55 			udi[i] = '_';
56 }
57 
58 static void nop(int sig) {}
59 
60 static int
61 test_socket_access(struct in6_addr *addr, int port)
62 {
63 	int sd, rc;
64 	struct sockaddr_in6 sin6;
65 	void (*hndlr)(int);
66 
67 	memset(&sin6, 0, sizeof (sin6));
68 	sin6.sin6_family = AF_INET6;
69 	memcpy(&sin6.sin6_addr, addr, sizeof (*addr));
70 	sin6.sin6_port = htons(port);
71 
72 	sd = socket(AF_INET6, SOCK_STREAM, 0);
73 	hndlr = signal(SIGALRM, nop);
74 	alarm(1);
75 	rc = connect(sd, (struct sockaddr *)&sin6, sizeof (sin6));
76 	alarm(0);
77 	if (hndlr != NULL)
78 		signal(SIGALRM, hndlr);
79 	close(sd);
80 
81 	return ((rc < 0) ? 1 : 0);
82 }
83 
84 int
85 is_listening(char *hostname, int port)
86 {
87 	char *uri = NULL, addr_string[INET6_ADDRSTRLEN];
88 	struct in6_addr ipv6addr[1];
89 	int errnum;
90 	struct hostent *hp;
91 
92 	hp = getipnodebyname(hostname, AF_INET6,
93 			AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, &errnum);
94 	if (hp != NULL) {
95 		(void) memcpy(&ipv6addr, hp->h_addr_list[0], hp->h_length);
96 	} else
97 		return (-1);
98 
99 	return (test_socket_access(ipv6addr, port));
100 }
101 
102 static char *
103 addr_to_string(char *prefix, uchar_t *mac, int mac_len, char *buf, int buf_len)
104 {
105 	int i, n = 0;
106 
107 	buf[0] = '\0';
108 	if (prefix != NULL)
109 		n = sprintf(buf, prefix);
110 	for (i = 0; ((i < (mac_len)) && (n < buf_len)); i++)
111 		n += sprintf(buf + n, "%2.2X", *mac++);
112 
113 	return (buf);
114 }
115 
116 static char *
117 pseudo_serialno_from_addr(char *name)
118 {
119 	int sd, rc, errnum;
120 	char buf[128];
121 	struct hostent *hp;
122 	struct xarpreq ar;
123 
124 	if (name == NULL)
125 		return (NULL);
126 
127 	memset(&ar, 0, sizeof (ar));
128 
129 	hp = getipnodebyname(name, AF_INET6, AI_ADDRCONFIG, &errnum);
130 	if (hp != NULL) {
131 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ar.xarp_pa;
132 
133 		sin6->sin6_family = AF_INET6;
134 		(void) memcpy(&sin6->sin6_addr, hp->h_addr_list[0],
135 				hp->h_length);
136 	} else {
137 		struct sockaddr_in *sin = (struct sockaddr_in *)&ar.xarp_pa;
138 
139 		sin->sin_family = AF_INET;
140 		sin->sin_addr.s_addr = inet_addr(name);
141 	}
142 
143 	sd = socket(AF_INET, SOCK_DGRAM, 0);
144 
145 	ar.xarp_ha.sdl_family = AF_LINK;
146 	rc = ioctl(sd, SIOCGXARP, (caddr_t)&ar);
147 
148 	close(sd);
149 
150 	if (ar.xarp_flags & ATF_COM) {  /* use the MAC address */
151 		uchar_t *ea = (uchar_t *)LLADDR(&ar.xarp_ha);
152 
153 		addr_to_string("LLADDR-", ea, ar.xarp_ha.sdl_alen,
154 					buf, sizeof (buf));
155 
156 	} else if (hp != NULL) {	  /* use the IPv6 address */
157 		addr_to_string("IPV6ADDR-", (uchar_t *)&hp->h_addr_list[0],
158 					hp->h_length, buf, sizeof (buf));
159 	} else {			  /* use the IPv4 address */
160 		struct sockaddr_in *sin = (struct sockaddr_in *)&ar.xarp_pa;
161 
162 		addr_to_string("IPV4ADDR-", (uchar_t *)&sin->sin_addr.s_addr, 4,
163 					buf, sizeof (buf));
164 	}
165 
166 	return (strdup(buf));
167 }
168 
169 int
170 add_network_printer(LibHalContext *ctx, char *base, char *hostaddr,
171 		char *device, char *community)
172 {
173 	DBusError error;
174 	int rc = -1;
175 	char udi[128];
176 	char *tmp_udi = NULL;
177 	static char *parent = NULL;
178 	char *manufacturer = NULL, *model = NULL, *description = NULL,
179 	     *uri = NULL, *sn, *serial;
180 
181 	sn = serial = pseudo_serialno_from_addr(hostaddr);
182 
183 	if (parent == NULL)
184 		parent = getenv("UDI");
185 
186 	dbus_error_init(&error);
187 
188 	network_device_name_to_udi(udi, sizeof (udi), base, serial, NULL);
189 
190 	if (libhal_device_exists(ctx, udi, &error) == TRUE)
191 		goto out;
192 
193 	if ((tmp_udi = libhal_new_device(ctx, &error)) == NULL)
194 		goto out;
195 
196 	snmp_printer_info(hostaddr, community, &manufacturer, &model,
197 			&description, &serial, NULL, &uri);
198 
199 	libhal_device_set_property_string(ctx, tmp_udi,
200 			"info.parent", parent, &error);
201 
202 	libhal_device_set_property_string(ctx, tmp_udi,
203 			"info.category", "printer", &error);
204 
205 	libhal_device_property_strlist_append(ctx, tmp_udi,
206 				"info.capabilities", "printer", &error);
207 	libhal_device_property_strlist_append(ctx, tmp_udi,
208 				"info.capabilities", "network_device", &error);
209 
210 	libhal_device_set_property_string(ctx, tmp_udi,
211 			"network_device.address", hostaddr, &error);
212 
213 	if ((community != NULL) && (strcasecmp(community, "public") != 0))
214 		libhal_device_set_property_string(ctx, tmp_udi,
215 			"network_device.snmp_community", community, &error);
216 
217 	if ((uri != NULL) || (device != NULL))
218 		libhal_device_set_property_string(ctx, tmp_udi,
219 			"printer.device", (uri ? uri : device), &error);
220 
221 	if (serial != NULL)
222 		libhal_device_set_property_string(ctx, tmp_udi,
223 			"printer.serial", serial, &error);
224 
225 	if (manufacturer != NULL)
226 		libhal_device_set_property_string(ctx, tmp_udi,
227 			"printer.vendor", manufacturer, &error);
228 
229 	if (model != NULL)
230 		libhal_device_set_property_string(ctx, tmp_udi,
231 			"printer.product", model, &error);
232 
233 	if (description != NULL)
234 		libhal_device_set_property_string(ctx, tmp_udi,
235 			"printer.description", description, &error);
236 
237 	/* commit the changes to the new UDI */
238 	rc = libhal_device_commit_to_gdl(ctx, tmp_udi, udi, &error);
239 
240 out:
241 	HAL_DEBUG(("result: %s (%s): %s, %s, %s, %s, %s", hostaddr, udi,
242 		NP(manufacturer), NP(model), NP(description), NP(serial),
243 		NP(uri)));
244 
245 	if (tmp_udi != NULL)
246 		free(tmp_udi);
247 	if (manufacturer != NULL)
248 		free(manufacturer);
249 	if (model != NULL)
250 		free(model);
251 	if (description != NULL)
252 		free(description);
253 	if (uri != NULL)
254 		free(uri);
255 	if (sn != NULL)
256 		free(sn);
257 
258 	if (dbus_error_is_set(&error)) {
259 		HAL_WARNING(("%s: %s", error.name, error.message));
260 		dbus_error_free(&error);
261 	}
262 
263 	HAL_DEBUG(("add: %s (%s)", hostaddr, udi));
264 
265 	return (rc);
266 }
267 
268 static int
269 number_of_interfaces(int s)
270 {
271 	int rc = -1;
272 	struct lifnum n;
273 
274 	memset(&n, 0 , sizeof (n));
275 	n.lifn_family = AF_UNSPEC;
276 	if (ioctl(s, SIOCGLIFNUM, (char *)&n) == 0)
277 		rc = n.lifn_count;
278 
279 	return (rc);
280 }
281 
282 static char *
283 broadcast_address(int s, char *ifname)
284 {
285 	char *result = NULL;
286 	struct lifreq r;
287 
288 	memset(&r, 0, sizeof (r));
289 	strncpy((char *)&r.lifr_name, ifname, sizeof (r.lifr_name));
290 	if (ioctl(s, SIOCGLIFBRDADDR, (char *)&r) == 0) {
291 		char buf[INET6_ADDRSTRLEN];
292 
293 		switch (r.lifr_broadaddr.ss_family) {
294 		case AF_INET: {
295 			struct sockaddr_in *s =
296 				(struct sockaddr_in *)&r.lifr_broadaddr;
297 			result = (char *)inet_ntop(AF_INET, &s->sin_addr,
298 							buf, sizeof (buf));
299 			}
300 			break;
301 		case AF_INET6: {
302 			struct sockaddr_in6 *s =
303 				(struct sockaddr_in6 *)&r.lifr_broadaddr;
304 			result = (char *)inet_ntop(AF_INET6, &s->sin6_addr,
305 							buf, sizeof (buf));
306 			}
307 			break;
308 		}
309 
310 		if (result != NULL)
311 			result = strdup(result);
312 	}
313 
314 	return (result);
315 }
316 
317 GList *
318 broadcast_addresses()
319 {
320 	GList *result = NULL;
321 	int s;
322 	struct lifconf c;
323 	int count;
324 
325 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
326 		return (NULL);
327 
328 	count = number_of_interfaces(s);
329 
330 	memset(&c, 0, sizeof (c));
331 	c.lifc_family = AF_UNSPEC;
332 	c.lifc_flags = IFF_BROADCAST;
333 	c.lifc_buf = calloc(count, sizeof (struct lifreq));
334 	c.lifc_len = (count * sizeof (struct lifreq));
335 
336 	if (ioctl(s, SIOCGLIFCONF, (char *)&c) == 0) {
337 		struct lifreq *r = c.lifc_req;
338 
339 		for (count = c.lifc_len / sizeof (struct lifreq);
340 		     count > 0; count--, r++) {
341 			char *address = broadcast_address(s, r->lifr_name);
342 
343 			if (address != NULL) /* add it to the list */
344 				result = g_list_append(result, address);
345 		}
346 	}
347 	free(c.lifc_buf);
348 	close(s);
349 
350 	return (result);
351 }
352