]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - hunt/hunt/server.c
a909d2d0ffd094430ec427693cdf44af90f514c4
[bsdgames-darwin.git] / hunt / hunt / server.c
1 /* $NetBSD: server.c,v 1.6 2014/03/30 04:31:21 dholland Exp $ */
2 /*
3 * Copyright (c) 1983-2003, Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * + Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * + Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * + Neither the name of the University of California, San Francisco nor
16 * the names of its contributors may be used to endorse or promote
17 * products derived from this software without specific prior written
18 * permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: server.c,v 1.6 2014/03/30 04:31:21 dholland Exp $");
35
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/poll.h>
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <signal.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <assert.h>
48 #include <ifaddrs.h>
49
50 #include "hunt_common.h"
51 #include "pathnames.h"
52 #include "hunt_private.h"
53
54 #ifdef INTERNET
55
56 /*
57 * Code for finding and talking to hunt daemons.
58 */
59
60 static SOCKET *daemons;
61 static unsigned int numdaemons, maxdaemons;
62
63 static SOCKET *brdv;
64 static int brdc;
65
66 static bool initial = true;
67 static struct in_addr local_address;
68 static const char *explicit_host;
69 static uint16_t port;
70
71 void
72 serverlist_setup(const char *explicit_host_arg, uint16_t port_arg)
73 {
74 char local_name[MAXHOSTNAMELEN + 1];
75 struct hostent *hp;
76
77 if (gethostname(local_name, sizeof(local_name)) < 0) {
78 leavex(1, "Sorry, I have no hostname.");
79 }
80 local_name[sizeof(local_name) - 1] = '\0';
81 if ((hp = gethostbyname(local_name)) == NULL) {
82 leavex(1, "Can't find myself.");
83 }
84 memcpy(&local_address, hp->h_addr, sizeof(local_address));
85
86 numdaemons = 0;
87 maxdaemons = 20;
88 daemons = malloc(maxdaemons * sizeof(daemons[0]));
89 if (daemons == NULL) {
90 leavex(1, "Out of memory.");
91 }
92
93 if (explicit_host_arg) {
94 explicit_host = explicit_host_arg;
95 }
96 port = port_arg;
97 }
98
99 static void
100 add_daemon_addr(const struct sockaddr_storage *addr, uint16_t port_num)
101 {
102 const struct sockaddr_in *sin;
103
104 if (addr->ss_family != AF_INET) {
105 return;
106 }
107 sin = (const struct sockaddr_in *)addr;
108
109 assert(numdaemons <= maxdaemons);
110 if (numdaemons == maxdaemons) {
111 maxdaemons += 20;
112 daemons = realloc(daemons, maxdaemons * sizeof(daemons[0]));
113 if (daemons == NULL) {
114 leave(1, "realloc");
115 }
116 }
117
118 /*
119 * Note that we do *not* convert from network to host
120 * order since the port number we were sent *should*
121 * already be in network order.
122 *
123 * The result may be either the port number for the hunt
124 * socket or the port number for the stats socket... or the
125 * number of players connected and not a port number at all,
126 * depending on the packet type.
127 *
128 * For now at least it is ok to stuff it in here, because the
129 * usage of the various different packet types and the
130 * persistence of the results vs. the program exiting does not
131 * cause us to get confused. If the game is made more
132 * self-contained in the future we'll need to be more careful
133 * about this, especially if we make the caching of results
134 * less scattershot.
135 */
136 daemons[numdaemons] = *sin;
137 daemons[numdaemons].sin_port = port_num;
138 numdaemons++;
139 }
140
141 static bool
142 have_daemon_addr(const struct sockaddr_storage *addr)
143 {
144 unsigned j;
145 const struct sockaddr_in *sin;
146
147 if (addr->ss_family != AF_INET) {
148 return false;
149 }
150 sin = (const struct sockaddr_in *)addr;
151
152 for (j = 0; j < numdaemons; j++) {
153 if (sin->sin_addr.s_addr == daemons[j].sin_addr.s_addr) {
154 return true;
155 }
156 }
157 return false;
158 }
159
160 static int
161 getbroadcastaddrs(struct sockaddr_in **vector)
162 {
163 int vec_cnt;
164 struct ifaddrs *ifp, *ip;
165
166 *vector = NULL;
167 if (getifaddrs(&ifp) < 0)
168 return 0;
169
170 vec_cnt = 0;
171 for (ip = ifp; ip; ip = ip->ifa_next)
172 if ((ip->ifa_addr->sa_family == AF_INET) &&
173 (ip->ifa_flags & IFF_BROADCAST))
174 vec_cnt++;
175
176 *vector = malloc(vec_cnt * sizeof(struct sockaddr_in));
177
178 vec_cnt = 0;
179 for (ip = ifp; ip; ip = ip->ifa_next)
180 if ((ip->ifa_addr->sa_family == AF_INET) &&
181 (ip->ifa_flags & IFF_BROADCAST))
182 memcpy(&(*vector)[vec_cnt++], ip->ifa_broadaddr,
183 sizeof(struct sockaddr_in));
184
185 freeifaddrs(ifp);
186 return vec_cnt;
187 }
188
189 static void
190 send_messages(int contactsock, unsigned short msg)
191 {
192 struct sockaddr_in contactaddr;
193 struct hostent *hp;
194 uint16_t wiremsg;
195 int option;
196 int i;
197
198 contactaddr.sin_family = SOCK_FAMILY;
199 contactaddr.sin_port = htons(port);
200
201 if (explicit_host != NULL) { /* explicit host given */
202 hp = gethostbyname(explicit_host);
203 if (hp == NULL) {
204 leavex(1, "%s: Unknown host", explicit_host);
205 }
206 memcpy(&contactaddr.sin_addr, hp->h_addr,
207 sizeof(contactaddr.sin_addr));
208 wiremsg = htons(msg);
209 (void) sendto(contactsock, &wiremsg, sizeof(wiremsg), 0,
210 (struct sockaddr *)&contactaddr,
211 sizeof(contactaddr));
212 return;
213 }
214
215 if (!initial) {
216 /* favor host of previous session by contacting it first */
217 contactaddr.sin_addr = Daemon.sin_addr;
218
219 /* Must be playing! */
220 assert(msg == C_PLAYER);
221 wiremsg = htons(msg);
222
223 (void) sendto(contactsock, &wiremsg, sizeof(wiremsg), 0,
224 (struct sockaddr *)&contactaddr, sizeof(contactaddr));
225 }
226
227 if (initial)
228 brdc = getbroadcastaddrs(&brdv);
229
230 #ifdef SO_BROADCAST
231 /* Sun's will broadcast even though this option can't be set */
232 option = 1;
233 if (setsockopt(contactsock, SOL_SOCKET, SO_BROADCAST,
234 &option, sizeof option) < 0) {
235 leave(1, "setsockopt broadcast");
236 /* NOTREACHED */
237 }
238 #endif
239
240 /* send broadcast packets on all interfaces */
241 wiremsg = htons(msg);
242 for (i = 0; i < brdc; i++) {
243 contactaddr.sin_addr = brdv[i].sin_addr;
244 if (sendto(contactsock, &wiremsg, sizeof(wiremsg), 0,
245 (struct sockaddr *)&contactaddr,
246 sizeof(contactaddr)) < 0) {
247 leave(1, "sendto");
248 }
249 }
250 contactaddr.sin_addr = local_address;
251 if (sendto(contactsock, &wiremsg, sizeof(wiremsg), 0,
252 (struct sockaddr *)&contactaddr, sizeof(contactaddr)) < 0) {
253 leave(1, "sendto");
254 }
255 }
256
257 static void
258 get_responses(int contactsock)
259 {
260 struct pollfd set[1];
261 struct sockaddr_storage addr;
262 socklen_t addrlen;
263 int r;
264 uint16_t port_num;
265 ssize_t portlen;
266
267 /* forget all old responses */
268 numdaemons = 0;
269
270 errno = 0;
271 set[0].fd = contactsock;
272 set[0].events = POLLIN;
273 for (;;) {
274 r = poll(set, 1, 1000);
275 if (r < 0) {
276 if (errno == EINTR) {
277 continue;
278 }
279 leave(1, "poll");
280 }
281 if (r == 0) {
282 break;
283 }
284
285 addrlen = sizeof(addr);
286 portlen = recvfrom(contactsock, &port_num, sizeof(port_num), 0,
287 (struct sockaddr *)&addr, &addrlen);
288 if (portlen < 0) {
289 if (errno == EINTR) {
290 continue;
291 }
292 leave(1, "recvfrom");
293 }
294 if (portlen == 0) {
295 leavex(1, "recvfrom: Unexpected EOF");
296 }
297 if ((size_t)portlen != sizeof(port_num)) {
298 /* trash, ignore it */
299 continue;
300 }
301 if (have_daemon_addr(&addr)) {
302 /* this shouldn't happen */
303 continue;
304 }
305
306 add_daemon_addr(&addr, port_num);
307 }
308
309 initial = false;
310 }
311
312 void
313 serverlist_query(unsigned short msg)
314 {
315 int contactsock;
316
317 if (!initial && explicit_host != NULL) {
318 /* already did the work, no point doing it again */
319 return;
320 }
321
322 contactsock = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
323 if (contactsock < 0) {
324 leave(1, "socket system call failed");
325 }
326
327 send_messages(contactsock, msg);
328 get_responses(contactsock);
329
330 (void) close(contactsock);
331 }
332
333 unsigned
334 serverlist_num(void)
335 {
336 return numdaemons;
337 }
338
339 const struct sockaddr_storage *
340 serverlist_gethost(unsigned i, socklen_t *len_ret)
341 {
342 struct sockaddr_in *ret;
343
344 assert(i < numdaemons);
345 ret = &daemons[i];
346 *len_ret = sizeof(*ret);
347 return (struct sockaddr_storage *)ret;
348 }
349
350 unsigned short
351 serverlist_getresponse(unsigned i)
352 {
353 assert(i < numdaemons);
354 return daemons[i].sin_port;
355 }
356
357 #endif /* INTERNET */