common.c revision 5307:ea4512a0e608
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
33extern 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
37void
38network_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
58static void nop(int sig) {}
59
60static int
61test_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
84int
85is_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
102static char *
103addr_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
116static char *
117pseudo_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
169int
170add_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
240out:
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
268static int
269number_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
282static char *
283broadcast_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
317GList *
318broadcast_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