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