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