nrelease - fix/improve livecd
[dragonfly.git] / games / hunt / hunt / list.c
CommitLineData
6693db17 1/*-
82a5c12e
MD
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 $
82a5c12e
MD
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
34struct driver *drivers = NULL;
35int numdrivers = 0;
36int maxdrivers = 0;
37
38u_int16_t Server_port;
39
40static int numprobes = 0;
41static int probe_sock[64];
42static struct timeval probe_timeout;
43
44struct driver *
6beb426b 45next_driver(void)
82a5c12e
MD
46{
47
48 return next_driver_fd(-1);
49}
50
51struct driver *
6beb426b 52next_driver_fd(int fd)
82a5c12e
MD
53{
54 fd_set r;
55 int maxfd = -1;
435f923e
SW
56 int i, s, ret;
57 socklen_t len;
82a5c12e
MD
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
6693db17 94 if (fd != -1 && FD_ISSET(fd, &r))
82a5c12e
MD
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. */
135const char *
6beb426b 136driver_name(struct driver *driver)
82a5c12e
MD
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;
15b85273
SW
147 hp = gethostbyaddr(&sin->sin_addr, sizeof sin->sin_addr,
148 AF_INET);
82a5c12e
MD
149 if (hp != NULL)
150 name = hp->h_name;
151 else {
6693db17 152 name = inet_ntop(AF_INET, &sin->sin_addr,
82a5c12e
MD
153 buf, sizeof buf);
154 }
155 }
156
157 return name;
158}
159
160static int
161start_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:
6693db17 184 ((struct sockaddr_in *)addr)->sin_port =
82a5c12e
MD
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
197void
6beb426b 198probe_cleanup(void)
82a5c12e
MD
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 */
211void
6beb426b 212probe_drivers(u_int16_t req, char *preferred)
82a5c12e
MD
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;
6693db17 269 if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0)
82a5c12e
MD
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);
d9f85b33 309 }
310 free(inbuf);
311 close(fd);
82a5c12e 312}