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