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 } 342