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