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 }