Merge branch 'vendor/LIBARCHIVE' into HEAD
[dragonfly.git] / games / hunt / hunt / list.c
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  * $DragonFly: src/games/hunt/hunt/list.c,v 1.2 2008/09/04 16:12:51 swildner Exp $
9  */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <netdb.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <err.h>
18
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/sockio.h>
22 #include <sys/ioctl.h>
23
24 #include <netinet/in.h>
25 #include <net/if.h>
26
27 #include <arpa/inet.h>
28
29 #include "hunt.h"
30 #include "list.h"
31
32 /* Wait at most 5 seconds for a reply */
33 #define LIST_DELAY      5
34
35 struct driver *drivers = NULL;
36 int numdrivers = 0;
37 int maxdrivers = 0;
38
39 u_int16_t Server_port;
40
41 static int numprobes = 0;
42 static int probe_sock[64];
43 static struct timeval probe_timeout;
44
45 struct driver *
46 next_driver(void)
47 {
48
49         return next_driver_fd(-1);
50 }
51
52 struct driver *
53 next_driver_fd(int fd)
54 {
55         fd_set  r;
56         int     maxfd = -1;
57         int     i, s, ret, 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 }