xref: /openbsd/games/hunt/hunt/list.c (revision df69c215)
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