xref: /dragonfly/games/hunt/hunt/list.c (revision d9f85b3315fdf3905eee2057d79dd723366a8bf6)
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 *
next_driver(void)45 next_driver(void)
46 {
47 
48           return next_driver_fd(-1);
49 }
50 
51 struct driver *
next_driver_fd(int fd)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 *
driver_name(struct driver * driver)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
start_probe(struct sockaddr * addr,u_int16_t req)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
probe_cleanup(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
probe_drivers(u_int16_t req,char * preferred)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           close(fd);
312 }
313