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