Home | History | Annotate | Line # | Download | only in hunt
      1 /*	$NetBSD: server.c,v 1.9 2021/10/29 11:40:23 nia 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.9 2021/10/29 11:40:23 nia 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 struct sockaddr_in *daemons;
     61 static unsigned int numdaemons, maxdaemons;
     62 
     63 static struct sockaddr_in *broadcastaddrs;
     64 static int numbroadcastaddrs;
     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 = NULL;
     89 	if (reallocarr(&daemons, maxdaemons, sizeof(daemons[0])) != 0)
     90 		leavex(1, "Out of memory.");
     91 
     92 	if (explicit_host_arg) {
     93 		explicit_host = explicit_host_arg;
     94 	}
     95 	port = port_arg;
     96 }
     97 
     98 static void
     99 add_daemon_addr(const struct sockaddr_storage *addr, socklen_t addrlen,
    100 		uint16_t port_num)
    101 {
    102 	const struct sockaddr_in *sin;
    103 
    104 	if (addr->ss_family != AF_INET) {
    105 		return;
    106 	}
    107 	assert(addrlen == sizeof(struct sockaddr_in));
    108 	sin = (const struct sockaddr_in *)addr;
    109 
    110 	assert(numdaemons <= maxdaemons);
    111 	if (numdaemons == maxdaemons) {
    112 		maxdaemons += 20;
    113 		if (reallocarr(&daemons, maxdaemons, sizeof(daemons[0])) != 0)
    114 			leave(1, "realloc");
    115 	}
    116 
    117 	/*
    118 	 * Note that we do *not* convert from network to host
    119 	 * order since the port number we were sent *should*
    120 	 * already be in network order.
    121 	 *
    122 	 * The result may be either the port number for the hunt
    123 	 * socket or the port number for the stats socket... or the
    124 	 * number of players connected and not a port number at all,
    125 	 * depending on the packet type.
    126 	 *
    127 	 * For now at least it is ok to stuff it in here, because the
    128 	 * usage of the various different packet types and the
    129 	 * persistence of the results vs. the program exiting does not
    130 	 * cause us to get confused. If the game is made more
    131 	 * self-contained in the future we'll need to be more careful
    132 	 * about this, especially if we make the caching of results
    133 	 * less scattershot.
    134 	 */
    135 	daemons[numdaemons] = *sin;
    136 	daemons[numdaemons].sin_port = port_num;
    137 	numdaemons++;
    138 }
    139 
    140 static bool
    141 have_daemon_addr(const struct sockaddr_storage *addr, socklen_t addrlen)
    142 {
    143 	unsigned j;
    144 	const struct sockaddr_in *sin;
    145 
    146 	if (addr->ss_family != AF_INET) {
    147 		return false;
    148 	}
    149 	assert(addrlen == sizeof(struct sockaddr_in));
    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 void
    161 getbroadcastaddrs(void)
    162 {
    163 	unsigned num, i;
    164 	struct ifaddrs *ifp, *ip;
    165 
    166 	broadcastaddrs = NULL;
    167 	numbroadcastaddrs = 0;
    168 
    169 	if (getifaddrs(&ifp) < 0) {
    170 		return;
    171 	}
    172 
    173 	num = 0;
    174 	for (ip = ifp; ip; ip = ip->ifa_next) {
    175 		if ((ip->ifa_addr->sa_family == AF_INET) &&
    176 		    (ip->ifa_flags & IFF_BROADCAST)) {
    177 			num++;
    178 		}
    179 	}
    180 
    181 	broadcastaddrs = NULL;
    182 	if (reallocarr(&broadcastaddrs, num, sizeof(broadcastaddrs[0])) != 0)
    183 		leavex(1, "Out of memory");
    184 
    185 	i = 0;
    186 	for (ip = ifp; ip; ip = ip->ifa_next) {
    187 		if ((ip->ifa_addr->sa_family == AF_INET) &&
    188 		    (ip->ifa_flags & IFF_BROADCAST)) {
    189 			memcpy(&broadcastaddrs[i], ip->ifa_broadaddr,
    190 			       sizeof(broadcastaddrs[i]));
    191 			i++;
    192 		}
    193 	}
    194 	assert(i == num);
    195 	numbroadcastaddrs = num;
    196 
    197 	freeifaddrs(ifp);
    198 }
    199 
    200 static void
    201 send_messages(int contactsock, unsigned short msg)
    202 {
    203 	struct sockaddr_in contactaddr;
    204 	struct hostent *hp;
    205 	uint16_t wiremsg;
    206 	int option;
    207 	int i;
    208 
    209 	memset(&contactaddr, 0, sizeof(contactaddr));
    210 	contactaddr.sin_family = SOCK_FAMILY;
    211 	contactaddr.sin_port = htons(port);
    212 
    213 	if (explicit_host != NULL) {	/* explicit host given */
    214 		hp = gethostbyname(explicit_host);
    215 		if (hp == NULL) {
    216 			leavex(1, "%s: Unknown host", explicit_host);
    217 		}
    218 		memcpy(&contactaddr.sin_addr, hp->h_addr,
    219 		       sizeof(contactaddr.sin_addr));
    220 		wiremsg = htons(msg);
    221 		(void) sendto(contactsock, &wiremsg, sizeof(wiremsg), 0,
    222 			      (struct sockaddr *)&contactaddr,
    223 			      sizeof(contactaddr));
    224 		return;
    225 	}
    226 
    227 	if (!initial) {
    228 		/* favor host of previous session by contacting it first */
    229 		contactaddr.sin_addr = Daemon.sin_addr;
    230 
    231 		/* Must be playing! */
    232 		assert(msg == C_PLAYER);
    233 		wiremsg = htons(msg);
    234 
    235 		(void) sendto(contactsock, &wiremsg, sizeof(wiremsg), 0,
    236 		    (struct sockaddr *)&contactaddr, sizeof(contactaddr));
    237 	}
    238 
    239 	if (initial) {
    240 		getbroadcastaddrs();
    241 	}
    242 
    243 #ifdef SO_BROADCAST
    244 	/* Sun's will broadcast even though this option can't be set */
    245 	option = 1;
    246 	if (setsockopt(contactsock, SOL_SOCKET, SO_BROADCAST,
    247 	    &option, sizeof option) < 0) {
    248 		leave(1, "setsockopt broadcast");
    249 		/* NOTREACHED */
    250 	}
    251 #endif
    252 
    253 	/* send broadcast packets on all interfaces */
    254 	wiremsg = htons(msg);
    255 	for (i = 0; i < numbroadcastaddrs; i++) {
    256 		contactaddr.sin_addr = broadcastaddrs[i].sin_addr;
    257 		if (sendto(contactsock, &wiremsg, sizeof(wiremsg), 0,
    258 		    (struct sockaddr *)&contactaddr,
    259 		    sizeof(contactaddr)) < 0) {
    260 			leave(1, "sendto");
    261 		}
    262 	}
    263 	contactaddr.sin_addr = local_address;
    264 	if (sendto(contactsock, &wiremsg, sizeof(wiremsg), 0,
    265 	    (struct sockaddr *)&contactaddr, sizeof(contactaddr)) < 0) {
    266 		leave(1, "sendto");
    267 	}
    268 }
    269 
    270 static void
    271 get_responses(int contactsock)
    272 {
    273 	struct pollfd set[1];
    274 	struct sockaddr_storage addr;
    275 	socklen_t addrlen;
    276 	int r;
    277 	uint16_t port_num;
    278 	ssize_t portlen;
    279 
    280 	/* forget all old responses */
    281 	numdaemons = 0;
    282 
    283 	errno = 0;
    284 	set[0].fd = contactsock;
    285 	set[0].events = POLLIN;
    286 	for (;;) {
    287 		r = poll(set, 1, 1000);
    288 		if (r < 0) {
    289 			if (errno == EINTR) {
    290 				continue;
    291 			}
    292 			leave(1, "poll");
    293 		}
    294 		if (r == 0) {
    295 			break;
    296 		}
    297 
    298 		addrlen = sizeof(addr);
    299 		portlen = recvfrom(contactsock, &port_num, sizeof(port_num), 0,
    300 				   (struct sockaddr *)&addr, &addrlen);
    301 		if (portlen < 0) {
    302 			if (errno == EINTR) {
    303 				continue;
    304 			}
    305 			leave(1, "recvfrom");
    306 		}
    307 		if (portlen == 0) {
    308 			leavex(1, "recvfrom: Unexpected EOF");
    309 		}
    310 		if ((size_t)portlen != sizeof(port_num)) {
    311 			/* trash, ignore it */
    312 			continue;
    313 		}
    314 		if (have_daemon_addr(&addr, addrlen)) {
    315 			/* this shouldn't happen */
    316 			continue;
    317 		}
    318 
    319 		add_daemon_addr(&addr, addrlen, port_num);
    320 	}
    321 
    322 	initial = false;
    323 }
    324 
    325 void
    326 serverlist_query(unsigned short msg)
    327 {
    328 	int contactsock;
    329 
    330 	if (!initial && explicit_host != NULL) {
    331 		/* already did the work, no point doing it again */
    332 		return;
    333 	}
    334 
    335 	contactsock = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
    336 	if (contactsock < 0) {
    337 		leave(1, "socket system call failed");
    338 	}
    339 
    340 	send_messages(contactsock, msg);
    341 	get_responses(contactsock);
    342 
    343 	(void) close(contactsock);
    344 }
    345 
    346 unsigned
    347 serverlist_num(void)
    348 {
    349 	return numdaemons;
    350 }
    351 
    352 const struct sockaddr_storage *
    353 serverlist_gethost(unsigned i, socklen_t *len_ret)
    354 {
    355 	struct sockaddr_in *ret;
    356 
    357 	assert(i < numdaemons);
    358 	ret = &daemons[i];
    359 	*len_ret = sizeof(*ret);
    360 	return (struct sockaddr_storage *)ret;
    361 }
    362 
    363 unsigned short
    364 serverlist_getresponse(unsigned i)
    365 {
    366 	assert(i < numdaemons);
    367 	return daemons[i].sin_port;
    368 }
    369 
    370 #endif /* INTERNET */
    371