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