14e9cfc9aSjacobs /* 2*18c4e255SLin Guo - Sun Microsystems * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 34e9cfc9aSjacobs * 44e9cfc9aSjacobs * Licensed under the Academic Free License version 2.1 54e9cfc9aSjacobs */ 64e9cfc9aSjacobs 74e9cfc9aSjacobs #include <stdio.h> 84e9cfc9aSjacobs #include <stdlib.h> 94e9cfc9aSjacobs #include <unistd.h> 104e9cfc9aSjacobs #include <signal.h> 114e9cfc9aSjacobs #include <string.h> 124e9cfc9aSjacobs #include <sys/types.h> 134e9cfc9aSjacobs #include <sys/socket.h> 144e9cfc9aSjacobs #include <sys/ioctl.h> 154e9cfc9aSjacobs #include <sys/sockio.h> 164e9cfc9aSjacobs #include <net/if.h> 174e9cfc9aSjacobs #include <net/if_arp.h> 184e9cfc9aSjacobs #include <netinet/in.h> 194e9cfc9aSjacobs #include <arpa/inet.h> 204e9cfc9aSjacobs #include <netdb.h> 214e9cfc9aSjacobs 224e9cfc9aSjacobs #include <libhal.h> 234e9cfc9aSjacobs #include <logger.h> 244e9cfc9aSjacobs 254e9cfc9aSjacobs #include <glib.h> 264e9cfc9aSjacobs 274e9cfc9aSjacobs #include "network-discovery.h" 284e9cfc9aSjacobs #define NP(x) (x?x:"NULL") 294e9cfc9aSjacobs 304e9cfc9aSjacobs extern int snmp_printer_info(char *hostname, char *community, 314e9cfc9aSjacobs char **manufacturer, char **model, char **description, 324e9cfc9aSjacobs char **serial_no, char ***command_set, char **uri); 334e9cfc9aSjacobs 344e9cfc9aSjacobs void 354e9cfc9aSjacobs network_device_name_to_udi(char *udi, size_t size, ...) 364e9cfc9aSjacobs { 374e9cfc9aSjacobs va_list ap; 384e9cfc9aSjacobs char *element; 394e9cfc9aSjacobs int i; 404e9cfc9aSjacobs 414e9cfc9aSjacobs udi[0] = '\0'; 424e9cfc9aSjacobs va_start(ap, size); 434e9cfc9aSjacobs while ((element = va_arg(ap, char *)) != NULL) { 444e9cfc9aSjacobs if (element[0] != '/') 454e9cfc9aSjacobs strlcat(udi, "/", size); 464e9cfc9aSjacobs strlcat(udi, element, size); 474e9cfc9aSjacobs } 484e9cfc9aSjacobs va_end(ap); 494e9cfc9aSjacobs 504e9cfc9aSjacobs for (i = 0; udi[i] != NULL; i++) 514e9cfc9aSjacobs if (udi[i] == '.') 524e9cfc9aSjacobs udi[i] = '_'; 534e9cfc9aSjacobs } 544e9cfc9aSjacobs 554e9cfc9aSjacobs static void nop(int sig) {} 564e9cfc9aSjacobs 574e9cfc9aSjacobs static int 584e9cfc9aSjacobs test_socket_access(struct in6_addr *addr, int port) 594e9cfc9aSjacobs { 604e9cfc9aSjacobs int sd, rc; 614e9cfc9aSjacobs struct sockaddr_in6 sin6; 624e9cfc9aSjacobs void (*hndlr)(int); 634e9cfc9aSjacobs 644e9cfc9aSjacobs memset(&sin6, 0, sizeof (sin6)); 654e9cfc9aSjacobs sin6.sin6_family = AF_INET6; 664e9cfc9aSjacobs memcpy(&sin6.sin6_addr, addr, sizeof (*addr)); 674e9cfc9aSjacobs sin6.sin6_port = htons(port); 684e9cfc9aSjacobs 694e9cfc9aSjacobs sd = socket(AF_INET6, SOCK_STREAM, 0); 704e9cfc9aSjacobs hndlr = signal(SIGALRM, nop); 714e9cfc9aSjacobs alarm(1); 724e9cfc9aSjacobs rc = connect(sd, (struct sockaddr *)&sin6, sizeof (sin6)); 734e9cfc9aSjacobs alarm(0); 744e9cfc9aSjacobs if (hndlr != NULL) 754e9cfc9aSjacobs signal(SIGALRM, hndlr); 764e9cfc9aSjacobs close(sd); 774e9cfc9aSjacobs 784e9cfc9aSjacobs return ((rc < 0) ? 1 : 0); 794e9cfc9aSjacobs } 804e9cfc9aSjacobs 814e9cfc9aSjacobs int 824e9cfc9aSjacobs is_listening(char *hostname, int port) 834e9cfc9aSjacobs { 844e9cfc9aSjacobs char *uri = NULL, addr_string[INET6_ADDRSTRLEN]; 854e9cfc9aSjacobs struct in6_addr ipv6addr[1]; 864e9cfc9aSjacobs int errnum; 874e9cfc9aSjacobs struct hostent *hp; 884e9cfc9aSjacobs 894e9cfc9aSjacobs hp = getipnodebyname(hostname, AF_INET6, 904e9cfc9aSjacobs AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, &errnum); 914e9cfc9aSjacobs if (hp != NULL) { 924e9cfc9aSjacobs (void) memcpy(&ipv6addr, hp->h_addr_list[0], hp->h_length); 934e9cfc9aSjacobs } else 944e9cfc9aSjacobs return (-1); 954e9cfc9aSjacobs 964e9cfc9aSjacobs return (test_socket_access(ipv6addr, port)); 974e9cfc9aSjacobs } 984e9cfc9aSjacobs 994e9cfc9aSjacobs static char * 1004e9cfc9aSjacobs addr_to_string(char *prefix, uchar_t *mac, int mac_len, char *buf, int buf_len) 1014e9cfc9aSjacobs { 1024e9cfc9aSjacobs int i, n = 0; 1034e9cfc9aSjacobs 1044e9cfc9aSjacobs buf[0] = '\0'; 1054e9cfc9aSjacobs if (prefix != NULL) 1064e9cfc9aSjacobs n = sprintf(buf, prefix); 1074e9cfc9aSjacobs for (i = 0; ((i < (mac_len)) && (n < buf_len)); i++) 1084e9cfc9aSjacobs n += sprintf(buf + n, "%2.2X", *mac++); 1094e9cfc9aSjacobs 1104e9cfc9aSjacobs return (buf); 1114e9cfc9aSjacobs } 1124e9cfc9aSjacobs 1134e9cfc9aSjacobs static char * 1144e9cfc9aSjacobs pseudo_serialno_from_addr(char *name) 1154e9cfc9aSjacobs { 1164e9cfc9aSjacobs int sd, rc, errnum; 1174e9cfc9aSjacobs char buf[128]; 1184e9cfc9aSjacobs struct hostent *hp; 1194e9cfc9aSjacobs struct xarpreq ar; 1204e9cfc9aSjacobs 1214e9cfc9aSjacobs if (name == NULL) 1224e9cfc9aSjacobs return (NULL); 1234e9cfc9aSjacobs 1244e9cfc9aSjacobs memset(&ar, 0, sizeof (ar)); 1254e9cfc9aSjacobs 1264e9cfc9aSjacobs hp = getipnodebyname(name, AF_INET6, AI_ADDRCONFIG, &errnum); 1274e9cfc9aSjacobs if (hp != NULL) { 1284e9cfc9aSjacobs struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ar.xarp_pa; 1294e9cfc9aSjacobs 1304e9cfc9aSjacobs sin6->sin6_family = AF_INET6; 1314e9cfc9aSjacobs (void) memcpy(&sin6->sin6_addr, hp->h_addr_list[0], 1324e9cfc9aSjacobs hp->h_length); 1334e9cfc9aSjacobs } else { 1344e9cfc9aSjacobs struct sockaddr_in *sin = (struct sockaddr_in *)&ar.xarp_pa; 1354e9cfc9aSjacobs 1364e9cfc9aSjacobs sin->sin_family = AF_INET; 1374e9cfc9aSjacobs sin->sin_addr.s_addr = inet_addr(name); 1384e9cfc9aSjacobs } 1394e9cfc9aSjacobs 1404e9cfc9aSjacobs sd = socket(AF_INET, SOCK_DGRAM, 0); 1414e9cfc9aSjacobs 1424e9cfc9aSjacobs ar.xarp_ha.sdl_family = AF_LINK; 1434e9cfc9aSjacobs rc = ioctl(sd, SIOCGXARP, (caddr_t)&ar); 1444e9cfc9aSjacobs 1454e9cfc9aSjacobs close(sd); 1464e9cfc9aSjacobs 1474e9cfc9aSjacobs if (ar.xarp_flags & ATF_COM) { /* use the MAC address */ 1484e9cfc9aSjacobs uchar_t *ea = (uchar_t *)LLADDR(&ar.xarp_ha); 1494e9cfc9aSjacobs 1504e9cfc9aSjacobs addr_to_string("LLADDR-", ea, ar.xarp_ha.sdl_alen, 1514e9cfc9aSjacobs buf, sizeof (buf)); 1524e9cfc9aSjacobs 1534e9cfc9aSjacobs } else if (hp != NULL) { /* use the IPv6 address */ 1544e9cfc9aSjacobs addr_to_string("IPV6ADDR-", (uchar_t *)&hp->h_addr_list[0], 1554e9cfc9aSjacobs hp->h_length, buf, sizeof (buf)); 1564e9cfc9aSjacobs } else { /* use the IPv4 address */ 1574e9cfc9aSjacobs struct sockaddr_in *sin = (struct sockaddr_in *)&ar.xarp_pa; 1584e9cfc9aSjacobs 1594e9cfc9aSjacobs addr_to_string("IPV4ADDR-", (uchar_t *)&sin->sin_addr.s_addr, 4, 1604e9cfc9aSjacobs buf, sizeof (buf)); 1614e9cfc9aSjacobs } 1624e9cfc9aSjacobs 1634e9cfc9aSjacobs return (strdup(buf)); 1644e9cfc9aSjacobs } 1654e9cfc9aSjacobs 1664e9cfc9aSjacobs int 1674e9cfc9aSjacobs add_network_printer(LibHalContext *ctx, char *base, char *hostaddr, 1684e9cfc9aSjacobs char *device, char *community) 1694e9cfc9aSjacobs { 1704e9cfc9aSjacobs DBusError error; 1714e9cfc9aSjacobs int rc = -1; 1724e9cfc9aSjacobs char udi[128]; 1734e9cfc9aSjacobs char *tmp_udi = NULL; 1744e9cfc9aSjacobs static char *parent = NULL; 1754e9cfc9aSjacobs char *manufacturer = NULL, *model = NULL, *description = NULL, 1764e9cfc9aSjacobs *uri = NULL, *sn, *serial; 1774e9cfc9aSjacobs 1784e9cfc9aSjacobs sn = serial = pseudo_serialno_from_addr(hostaddr); 1794e9cfc9aSjacobs 1804e9cfc9aSjacobs if (parent == NULL) 1814e9cfc9aSjacobs parent = getenv("UDI"); 1824e9cfc9aSjacobs 1834e9cfc9aSjacobs dbus_error_init(&error); 1844e9cfc9aSjacobs 1854e9cfc9aSjacobs network_device_name_to_udi(udi, sizeof (udi), base, serial, NULL); 1864e9cfc9aSjacobs 1874e9cfc9aSjacobs if (libhal_device_exists(ctx, udi, &error) == TRUE) 1884e9cfc9aSjacobs goto out; 1894e9cfc9aSjacobs 1904e9cfc9aSjacobs if ((tmp_udi = libhal_new_device(ctx, &error)) == NULL) 1914e9cfc9aSjacobs goto out; 1924e9cfc9aSjacobs 1934e9cfc9aSjacobs snmp_printer_info(hostaddr, community, &manufacturer, &model, 1944e9cfc9aSjacobs &description, &serial, NULL, &uri); 1954e9cfc9aSjacobs 1964e9cfc9aSjacobs libhal_device_set_property_string(ctx, tmp_udi, 1974e9cfc9aSjacobs "info.parent", parent, &error); 1984e9cfc9aSjacobs 1994e9cfc9aSjacobs libhal_device_set_property_string(ctx, tmp_udi, 2004e9cfc9aSjacobs "info.category", "printer", &error); 2014e9cfc9aSjacobs 2024e9cfc9aSjacobs libhal_device_property_strlist_append(ctx, tmp_udi, 2034e9cfc9aSjacobs "info.capabilities", "printer", &error); 2044e9cfc9aSjacobs libhal_device_property_strlist_append(ctx, tmp_udi, 2054e9cfc9aSjacobs "info.capabilities", "network_device", &error); 2064e9cfc9aSjacobs 2074e9cfc9aSjacobs libhal_device_set_property_string(ctx, tmp_udi, 2084e9cfc9aSjacobs "network_device.address", hostaddr, &error); 2094e9cfc9aSjacobs 2104e9cfc9aSjacobs if ((community != NULL) && (strcasecmp(community, "public") != 0)) 2114e9cfc9aSjacobs libhal_device_set_property_string(ctx, tmp_udi, 2124e9cfc9aSjacobs "network_device.snmp_community", community, &error); 2134e9cfc9aSjacobs 2144e9cfc9aSjacobs if ((uri != NULL) || (device != NULL)) 2154e9cfc9aSjacobs libhal_device_set_property_string(ctx, tmp_udi, 2164e9cfc9aSjacobs "printer.device", (uri ? uri : device), &error); 2174e9cfc9aSjacobs 2184e9cfc9aSjacobs if (serial != NULL) 2194e9cfc9aSjacobs libhal_device_set_property_string(ctx, tmp_udi, 2204e9cfc9aSjacobs "printer.serial", serial, &error); 2214e9cfc9aSjacobs 2224e9cfc9aSjacobs if (manufacturer != NULL) 2234e9cfc9aSjacobs libhal_device_set_property_string(ctx, tmp_udi, 2244e9cfc9aSjacobs "printer.vendor", manufacturer, &error); 2254e9cfc9aSjacobs 2264e9cfc9aSjacobs if (model != NULL) 2274e9cfc9aSjacobs libhal_device_set_property_string(ctx, tmp_udi, 2284e9cfc9aSjacobs "printer.product", model, &error); 2294e9cfc9aSjacobs 2304e9cfc9aSjacobs if (description != NULL) 2314e9cfc9aSjacobs libhal_device_set_property_string(ctx, tmp_udi, 2324e9cfc9aSjacobs "printer.description", description, &error); 2334e9cfc9aSjacobs 2344e9cfc9aSjacobs /* commit the changes to the new UDI */ 2354e9cfc9aSjacobs rc = libhal_device_commit_to_gdl(ctx, tmp_udi, udi, &error); 2364e9cfc9aSjacobs 2374e9cfc9aSjacobs out: 2384e9cfc9aSjacobs HAL_DEBUG(("result: %s (%s): %s, %s, %s, %s, %s", hostaddr, udi, 2394e9cfc9aSjacobs NP(manufacturer), NP(model), NP(description), NP(serial), 2404e9cfc9aSjacobs NP(uri))); 2414e9cfc9aSjacobs 2424e9cfc9aSjacobs if (tmp_udi != NULL) 2434e9cfc9aSjacobs free(tmp_udi); 2444e9cfc9aSjacobs if (manufacturer != NULL) 2454e9cfc9aSjacobs free(manufacturer); 2464e9cfc9aSjacobs if (model != NULL) 2474e9cfc9aSjacobs free(model); 2484e9cfc9aSjacobs if (description != NULL) 2494e9cfc9aSjacobs free(description); 2504e9cfc9aSjacobs if (uri != NULL) 2514e9cfc9aSjacobs free(uri); 2524e9cfc9aSjacobs if (sn != NULL) 2534e9cfc9aSjacobs free(sn); 2544e9cfc9aSjacobs 2554e9cfc9aSjacobs if (dbus_error_is_set(&error)) { 2564e9cfc9aSjacobs HAL_WARNING(("%s: %s", error.name, error.message)); 2574e9cfc9aSjacobs dbus_error_free(&error); 2584e9cfc9aSjacobs } 2594e9cfc9aSjacobs 2604e9cfc9aSjacobs HAL_DEBUG(("add: %s (%s)", hostaddr, udi)); 2614e9cfc9aSjacobs 2624e9cfc9aSjacobs return (rc); 2634e9cfc9aSjacobs } 2644e9cfc9aSjacobs 2654e9cfc9aSjacobs static int 2664e9cfc9aSjacobs number_of_interfaces(int s) 2674e9cfc9aSjacobs { 2684e9cfc9aSjacobs int rc = -1; 2694e9cfc9aSjacobs struct lifnum n; 2704e9cfc9aSjacobs 2714e9cfc9aSjacobs memset(&n, 0 , sizeof (n)); 272*18c4e255SLin Guo - Sun Microsystems n.lifn_family = AF_INET; 2734e9cfc9aSjacobs if (ioctl(s, SIOCGLIFNUM, (char *)&n) == 0) 2744e9cfc9aSjacobs rc = n.lifn_count; 2754e9cfc9aSjacobs 2764e9cfc9aSjacobs return (rc); 2774e9cfc9aSjacobs } 2784e9cfc9aSjacobs 2794e9cfc9aSjacobs static char * 2804e9cfc9aSjacobs broadcast_address(int s, char *ifname) 2814e9cfc9aSjacobs { 2824e9cfc9aSjacobs char *result = NULL; 2834e9cfc9aSjacobs struct lifreq r; 2844e9cfc9aSjacobs 2854e9cfc9aSjacobs memset(&r, 0, sizeof (r)); 286*18c4e255SLin Guo - Sun Microsystems strlcpy(r.lifr_name, ifname, sizeof (r.lifr_name)); 287*18c4e255SLin Guo - Sun Microsystems if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&r) < 0) { 288*18c4e255SLin Guo - Sun Microsystems HAL_DEBUG(("broadcast_address: ioctl(SIOCGLIFFLAGS) failed.")); 289*18c4e255SLin Guo - Sun Microsystems return (NULL); 290*18c4e255SLin Guo - Sun Microsystems } 291*18c4e255SLin Guo - Sun Microsystems if ((r.lifr_flags & (IFF_UP | IFF_LOOPBACK)) != IFF_UP) { 292*18c4e255SLin Guo - Sun Microsystems return (NULL); 293*18c4e255SLin Guo - Sun Microsystems } 294*18c4e255SLin Guo - Sun Microsystems if (ioctl(s, SIOCGLIFBRDADDR, (char *)&r) >= 0) { 295*18c4e255SLin Guo - Sun Microsystems char buf[INET_ADDRSTRLEN]; 2964e9cfc9aSjacobs struct sockaddr_in *s = 2974e9cfc9aSjacobs (struct sockaddr_in *)&r.lifr_broadaddr; 298*18c4e255SLin Guo - Sun Microsystems result = (char *)inet_ntop(AF_INET, 299*18c4e255SLin Guo - Sun Microsystems &s->sin_addr, buf, sizeof (buf)); 3004e9cfc9aSjacobs if (result != NULL) 3014e9cfc9aSjacobs result = strdup(result); 3024e9cfc9aSjacobs } 3034e9cfc9aSjacobs 3044e9cfc9aSjacobs return (result); 3054e9cfc9aSjacobs } 3064e9cfc9aSjacobs 3074e9cfc9aSjacobs GList * 3084e9cfc9aSjacobs broadcast_addresses() 3094e9cfc9aSjacobs { 3104e9cfc9aSjacobs GList *result = NULL; 3114e9cfc9aSjacobs int s; 3124e9cfc9aSjacobs struct lifconf c; 3134e9cfc9aSjacobs int count; 3144e9cfc9aSjacobs 3154e9cfc9aSjacobs if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 3164e9cfc9aSjacobs return (NULL); 3174e9cfc9aSjacobs 3184e9cfc9aSjacobs count = number_of_interfaces(s); 3194e9cfc9aSjacobs 3204e9cfc9aSjacobs memset(&c, 0, sizeof (c)); 321*18c4e255SLin Guo - Sun Microsystems c.lifc_family = AF_INET; 322*18c4e255SLin Guo - Sun Microsystems c.lifc_flags = 0; 3234e9cfc9aSjacobs c.lifc_buf = calloc(count, sizeof (struct lifreq)); 3244e9cfc9aSjacobs c.lifc_len = (count * sizeof (struct lifreq)); 3254e9cfc9aSjacobs 3264e9cfc9aSjacobs if (ioctl(s, SIOCGLIFCONF, (char *)&c) == 0) { 3274e9cfc9aSjacobs struct lifreq *r = c.lifc_req; 3284e9cfc9aSjacobs 3294e9cfc9aSjacobs for (count = c.lifc_len / sizeof (struct lifreq); 3304e9cfc9aSjacobs count > 0; count--, r++) { 3314e9cfc9aSjacobs char *address = broadcast_address(s, r->lifr_name); 3324e9cfc9aSjacobs 3334e9cfc9aSjacobs if (address != NULL) /* add it to the list */ 3344e9cfc9aSjacobs result = g_list_append(result, address); 3354e9cfc9aSjacobs } 3364e9cfc9aSjacobs } 3374e9cfc9aSjacobs free(c.lifc_buf); 3384e9cfc9aSjacobs close(s); 3394e9cfc9aSjacobs 3404e9cfc9aSjacobs return (result); 3414e9cfc9aSjacobs } 342