xref: /dragonfly/games/hunt/hunt/list.c (revision cfd1aba3)
1 /*-
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 $
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 *
45 next_driver(void)
46 {
47 
48 	return next_driver_fd(-1);
49 }
50 
51 struct driver *
52 next_driver_fd(int fd)
53 {
54 	fd_set	r;
55 	int	maxfd = -1;
56 	int	i, s, ret;
57 	socklen_t len;
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 
94 	if (fd != -1 && FD_ISSET(fd, &r))
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 *
136 driver_name(struct driver *driver)
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;
147 		hp = gethostbyaddr(&sin->sin_addr, sizeof sin->sin_addr,
148 		    AF_INET);
149 		if (hp != NULL)
150 			name = hp->h_name;
151 		else {
152 			name = inet_ntop(AF_INET, &sin->sin_addr,
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:
184 		((struct sockaddr_in *)addr)->sin_port =
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
198 probe_cleanup(void)
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
212 probe_drivers(u_int16_t req, char *preferred)
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;
269                 if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0)
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 }
313