| Commit | Line | Data |
|---|---|---|
| 6693db17 | 1 | /*- |
| 82a5c12e MD |
2 | * Copyright 2001, David Leonard. All rights reserved. |
| 3 | * Redistribution and use in source and binary forms with or without | |
| 4 | * modification are permitted provided that this notice is preserved. | |
| 5 | * This software is provided ``as is'' without express or implied warranty. | |
| 6 | * | |
| 7 | * $OpenBSD: list.c,v 1.5 2007/09/04 22:39:31 hshoexer Exp $ | |
| 82a5c12e MD |
8 | */ |
| 9 | ||
| 10 | #include <stdio.h> | |
| 11 | #include <stdlib.h> | |
| 12 | #include <string.h> | |
| 13 | #include <netdb.h> | |
| 14 | #include <unistd.h> | |
| 15 | #include <errno.h> | |
| 16 | #include <err.h> | |
| 17 | ||
| 18 | #include <sys/types.h> | |
| 19 | #include <sys/socket.h> | |
| 20 | #include <sys/sockio.h> | |
| 21 | #include <sys/ioctl.h> | |
| 22 | ||
| 23 | #include <netinet/in.h> | |
| 24 | #include <net/if.h> | |
| 25 | ||
| 26 | #include <arpa/inet.h> | |
| 27 | ||
| 28 | #include "hunt.h" | |
| 29 | #include "list.h" | |
| 30 | ||
| 31 | /* Wait at most 5 seconds for a reply */ | |
| 32 | #define LIST_DELAY 5 | |
| 33 | ||
| 34 | struct driver *drivers = NULL; | |
| 35 | int numdrivers = 0; | |
| 36 | int maxdrivers = 0; | |
| 37 | ||
| 38 | u_int16_t Server_port; | |
| 39 | ||
| 40 | static int numprobes = 0; | |
| 41 | static int probe_sock[64]; | |
| 42 | static struct timeval probe_timeout; | |
| 43 | ||
| 44 | struct driver * | |
| 6beb426b | 45 | next_driver(void) |
| 82a5c12e MD |
46 | { |
| 47 | ||
| 48 | return next_driver_fd(-1); | |
| 49 | } | |
| 50 | ||
| 51 | struct driver * | |
| 6beb426b | 52 | next_driver_fd(int fd) |
| 82a5c12e MD |
53 | { |
| 54 | fd_set r; | |
| 55 | int maxfd = -1; | |
| 435f923e SW |
56 | int i, s, ret; |
| 57 | socklen_t len; | |
| 82a5c12e MD |
58 | struct driver *driver; |
| 59 | u_int16_t resp; | |
| 60 | ||
| 61 | if (fd == -1 && numprobes == 0) | |
| 62 | return NULL; | |
| 63 | ||
| 64 | again: | |
| 65 | FD_ZERO(&r); | |
| 66 | if (fd != -1) { | |
| 67 | FD_SET(fd, &r); | |
| 68 | maxfd = fd; | |
| 69 | } | |
| 70 | for (i = 0; i < numprobes; i++) { | |
| 71 | FD_SET(probe_sock[i], &r); | |
| 72 | if (probe_sock[i] > maxfd) | |
| 73 | maxfd = probe_sock[i]; | |
| 74 | } | |
| 75 | ||
| 76 | probe_timeout.tv_sec = LIST_DELAY; | |
| 77 | probe_timeout.tv_usec = 0; | |
| 78 | ret = select(maxfd + 1, &r, NULL, NULL, &probe_timeout); | |
| 79 | ||
| 80 | if (ret == -1) { | |
| 81 | if (errno == EINTR) | |
| 82 | goto again; | |
| 83 | err(1, "select"); | |
| 84 | } | |
| 85 | ||
| 86 | if (ret == 0) { | |
| 87 | /* Timeout - close all sockets */ | |
| 88 | for (i = 0; i < numprobes; i++) | |
| 89 | close(probe_sock[i]); | |
| 90 | numprobes = 0; | |
| 91 | return NULL; | |
| 92 | } | |
| 93 | ||
| 6693db17 | 94 | if (fd != -1 && FD_ISSET(fd, &r)) |
| 82a5c12e MD |
95 | /* Keypress. Return magic number */ |
| 96 | return (struct driver *)-1; | |
| 97 | ||
| 98 | for (i = 0; i < numprobes; i++) | |
| 99 | /* Find the first ready socket */ | |
| 100 | if (FD_ISSET(probe_sock[i], &r)) | |
| 101 | break; | |
| 102 | ||
| 103 | s = probe_sock[i]; | |
| 104 | ||
| 105 | if (numdrivers >= maxdrivers) { | |
| 106 | if (maxdrivers) { | |
| 107 | maxdrivers *= 2; | |
| 108 | drivers = realloc(drivers, sizeof *driver * maxdrivers); | |
| 109 | } else { | |
| 110 | maxdrivers = 16; | |
| 111 | drivers = calloc(sizeof *driver, maxdrivers); | |
| 112 | } | |
| 113 | if (drivers == NULL) | |
| 114 | err(1, "malloc"); | |
| 115 | } | |
| 116 | driver = &drivers[numdrivers]; | |
| 117 | len = sizeof driver->addr; | |
| 118 | ret = recvfrom(s, &resp, sizeof resp, 0, &driver->addr, &len); | |
| 119 | if (ret == -1) | |
| 120 | goto again; | |
| 121 | driver->response = ntohs(resp); | |
| 122 | ||
| 123 | switch (driver->addr.sa_family) { | |
| 124 | case AF_INET: | |
| 125 | case AF_INET6: | |
| 126 | ((struct sockaddr_in *)&driver->addr)->sin_port = | |
| 127 | htons(driver->response); | |
| 128 | break; | |
| 129 | } | |
| 130 | numdrivers++; | |
| 131 | return driver; | |
| 132 | } | |
| 133 | ||
| 134 | /* Return the hostname for a driver. */ | |
| 135 | const char * | |
| 6beb426b | 136 | driver_name(struct driver *driver) |
| 82a5c12e MD |
137 | { |
| 138 | const char *name; | |
| 139 | static char buf[80]; | |
| 140 | struct hostent *hp; | |
| 141 | struct sockaddr_in *sin; | |
| 142 | ||
| 143 | name = NULL; | |
| 144 | ||
| 145 | if (driver->addr.sa_family == AF_INET) { | |
| 146 | sin = (struct sockaddr_in *)&driver->addr; | |
| 15b85273 SW |
147 | hp = gethostbyaddr(&sin->sin_addr, sizeof sin->sin_addr, |
| 148 | AF_INET); | |
| 82a5c12e MD |
149 | if (hp != NULL) |
| 150 | name = hp->h_name; | |
| 151 | else { | |
| 6693db17 | 152 | name = inet_ntop(AF_INET, &sin->sin_addr, |
| 82a5c12e MD |
153 | buf, sizeof buf); |
| 154 | } | |
| 155 | } | |
| 156 | ||
| 157 | return name; | |
| 158 | } | |
| 159 | ||
| 160 | static int | |
| 161 | start_probe(struct sockaddr *addr, u_int16_t req) | |
| 162 | { | |
| 163 | u_int16_t msg; | |
| 164 | int s; | |
| 165 | int enable; | |
| 166 | ||
| 167 | if (numprobes >= (int)(sizeof probe_sock / sizeof probe_sock[0])) { | |
| 168 | /* Just ridiculous */ | |
| 169 | return -1; | |
| 170 | } | |
| 171 | ||
| 172 | s = socket(addr->sa_family, SOCK_DGRAM, 0); | |
| 173 | if (s < 0) { | |
| 174 | warn("socket"); | |
| 175 | return -1; | |
| 176 | } | |
| 177 | ||
| 178 | enable = 1; | |
| 179 | setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof enable); | |
| 180 | ||
| 181 | switch (addr->sa_family) { | |
| 182 | case AF_INET: | |
| 183 | case AF_INET6: | |
| 6693db17 | 184 | ((struct sockaddr_in *)addr)->sin_port = |
| 82a5c12e MD |
185 | htons(Server_port); |
| 186 | break; | |
| 187 | } | |
| 188 | ||
| 189 | msg = htons(req); | |
| 190 | if (sendto(s, &msg, sizeof msg, 0, addr, addr->sa_len) == -1) | |
| 191 | warn("sendto"); | |
| 192 | probe_sock[numprobes++] = s; | |
| 193 | ||
| 194 | return 0; | |
| 195 | } | |
| 196 | ||
| 197 | void | |
| 6beb426b | 198 | probe_cleanup(void) |
| 82a5c12e MD |
199 | { |
| 200 | int i; | |
| 201 | ||
| 202 | for (i = 0; i < numprobes; i++) | |
| 203 | close(probe_sock[i]); | |
| 204 | numprobes = 0; | |
| 205 | } | |
| 206 | ||
| 207 | /* | |
| 208 | * If we have no preferred host then send a broadcast message to everyone. | |
| 209 | * Otherwise, send the request message only to the preferred host. | |
| 210 | */ | |
| 211 | void | |
| 6beb426b | 212 | probe_drivers(u_int16_t req, char *preferred) |
| 82a5c12e MD |
213 | { |
| 214 | struct sockaddr_in *target; | |
| 215 | struct sockaddr_in localhost; | |
| 216 | struct hostent *he; | |
| 217 | char *inbuf = NULL, *ninbuf; | |
| 218 | struct ifconf ifc; | |
| 219 | struct ifreq *ifr; | |
| 220 | int fd, inlen = 8192; | |
| 221 | int i, len; | |
| 222 | ||
| 223 | numdrivers = 0; | |
| 224 | ||
| 225 | probe_cleanup(); | |
| 226 | ||
| 227 | /* Send exclusively to a preferred host. */ | |
| 228 | if (preferred) { | |
| 229 | struct sockaddr_in sin; | |
| 230 | ||
| 231 | target = NULL; | |
| 232 | ||
| 233 | if (!target) { | |
| 234 | sin.sin_family = AF_INET; | |
| 235 | sin.sin_len = sizeof sin; | |
| 236 | if (inet_pton(AF_INET, preferred, &sin.sin_addr) == 1) | |
| 237 | target = &sin; | |
| 238 | } | |
| 239 | ||
| 240 | if (!target && (he = gethostbyname(preferred)) != NULL) { | |
| 241 | sin.sin_family = he->h_addrtype; | |
| 242 | sin.sin_len = sizeof sin; | |
| 243 | memcpy(&sin.sin_addr, he->h_addr, he->h_length); | |
| 244 | target = &sin; | |
| 245 | } | |
| 246 | ||
| 247 | if (!target) | |
| 248 | errx(1, "Bad hostname: %s", preferred); | |
| 249 | ||
| 250 | start_probe((struct sockaddr *)target, req); | |
| 251 | return; | |
| 252 | } | |
| 253 | ||
| 254 | /* Send a query to the local machine: */ | |
| 255 | localhost.sin_family = AF_INET; | |
| 256 | localhost.sin_len = sizeof localhost; | |
| 257 | localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
| 258 | start_probe((struct sockaddr *)&localhost, req); | |
| 259 | ||
| 260 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) | |
| 261 | err(1, "socket"); | |
| 262 | ||
| 263 | /* Find all attached networks: */ | |
| 264 | while (1) { | |
| 265 | ifc.ifc_len = inlen; | |
| 266 | if ((ninbuf = realloc(inbuf, inlen)) == NULL) | |
| 267 | err(1, "malloc"); | |
| 268 | ifc.ifc_buf = inbuf = ninbuf; | |
| 6693db17 | 269 | if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) |
| 82a5c12e MD |
270 | err(1, "SIOCGIFCONF"); |
| 271 | if (ifc.ifc_len + (int)sizeof(*ifr) < inlen) | |
| 272 | break; | |
| 273 | inlen *= 2; | |
| 274 | } | |
| 275 | ||
| 276 | /* Send a request to every attached broadcast address: */ | |
| 277 | ifr = ifc.ifc_req; | |
| 278 | for (i = 0; i < ifc.ifc_len; | |
| 279 | i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) { | |
| 280 | len = sizeof(ifr->ifr_name) + | |
| 281 | (ifr->ifr_addr.sa_len > sizeof(struct sockaddr) ? | |
| 282 | ifr->ifr_addr.sa_len : sizeof(struct sockaddr)); | |
| 283 | ||
| 284 | if (ifr->ifr_addr.sa_family != AF_INET) | |
| 285 | continue; | |
| 286 | ||
| 287 | if (ioctl(fd, SIOCGIFFLAGS, (caddr_t)ifr) < 0) { | |
| 288 | warn("%s: SIOCGIFFLAGS", ifr->ifr_name); | |
| 289 | continue; | |
| 290 | } | |
| 291 | if ((ifr->ifr_flags & IFF_UP) == 0) | |
| 292 | continue; | |
| 293 | if ((ifr->ifr_flags & IFF_BROADCAST) != 0) { | |
| 294 | if (ioctl(fd, SIOCGIFBRDADDR, (caddr_t)ifr) < 0) { | |
| 295 | warn("%s: SIOCGIFBRDADDR", ifr->ifr_name); | |
| 296 | continue; | |
| 297 | } | |
| 298 | target = (struct sockaddr_in *)&ifr->ifr_dstaddr; | |
| 299 | } else if ((ifr->ifr_flags & IFF_POINTOPOINT) != 0) { | |
| 300 | if (ioctl(fd, SIOCGIFDSTADDR, (caddr_t)ifr) < 0) { | |
| 301 | warn("%s: SIOCGIFDSTADDR", ifr->ifr_name); | |
| 302 | continue; | |
| 303 | } | |
| 304 | target = (struct sockaddr_in *)&ifr->ifr_broadaddr; | |
| 305 | } else | |
| 306 | continue; | |
| 307 | ||
| 308 | start_probe((struct sockaddr *)target, req); | |
| 309 | } | |
| 310 | free(inbuf); | |
| 311 | (void) close(fd); | |
| 312 | } |