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
28 struct driver *drivers = NULL;
29 int numdrivers = 0;
30 int maxdrivers = 0;
31
32 u_int16_t Server_port;
33
34 static int numprobes = 0;
35 static int probe_sock[64];
36 static struct timeval probe_timeout;
37
38 struct driver *
next_driver(void)39 next_driver(void)
40 {
41
42 return next_driver_fd(-1);
43 }
44
45 struct driver *
next_driver_fd(int fd)46 next_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. */
130 const char *
driver_name(struct driver * driver)131 driver_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
155 static int
start_probe(struct sockaddr * addr,u_int16_t req)156 start_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
192 void
probe_cleanup(void)193 probe_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 */
206 void
probe_drivers(u_int16_t req,char * preferred)207 probe_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