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 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] != NULL; i++) 52 if (udi[i] == '.') 53 udi[i] = '_'; 54 } 55 56 static void nop(int sig) {} 57 58 static int 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 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 * 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 * 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 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 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 * 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 * 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 }