xref: /dragonfly/games/hunt/hunt/list.c (revision d9f85b33)
16693db17SSascha Wildner /*-
282a5c12eSMatthew Dillon  * Copyright 2001, David Leonard. All rights reserved.
382a5c12eSMatthew Dillon  * Redistribution and use in source and binary forms with or without
482a5c12eSMatthew Dillon  * modification are permitted provided that this notice is preserved.
582a5c12eSMatthew Dillon  * This software is provided ``as is'' without express or implied warranty.
682a5c12eSMatthew Dillon  *
782a5c12eSMatthew Dillon  * $OpenBSD: list.c,v 1.5 2007/09/04 22:39:31 hshoexer Exp $
882a5c12eSMatthew Dillon  */
982a5c12eSMatthew Dillon 
1082a5c12eSMatthew Dillon #include <stdio.h>
1182a5c12eSMatthew Dillon #include <stdlib.h>
1282a5c12eSMatthew Dillon #include <string.h>
1382a5c12eSMatthew Dillon #include <netdb.h>
1482a5c12eSMatthew Dillon #include <unistd.h>
1582a5c12eSMatthew Dillon #include <errno.h>
1682a5c12eSMatthew Dillon #include <err.h>
1782a5c12eSMatthew Dillon 
1882a5c12eSMatthew Dillon #include <sys/types.h>
1982a5c12eSMatthew Dillon #include <sys/socket.h>
2082a5c12eSMatthew Dillon #include <sys/sockio.h>
2182a5c12eSMatthew Dillon #include <sys/ioctl.h>
2282a5c12eSMatthew Dillon 
2382a5c12eSMatthew Dillon #include <netinet/in.h>
2482a5c12eSMatthew Dillon #include <net/if.h>
2582a5c12eSMatthew Dillon 
2682a5c12eSMatthew Dillon #include <arpa/inet.h>
2782a5c12eSMatthew Dillon 
2882a5c12eSMatthew Dillon #include "hunt.h"
2982a5c12eSMatthew Dillon #include "list.h"
3082a5c12eSMatthew Dillon 
3182a5c12eSMatthew Dillon /* Wait at most 5 seconds for a reply */
3282a5c12eSMatthew Dillon #define LIST_DELAY	5
3382a5c12eSMatthew Dillon 
3482a5c12eSMatthew Dillon struct driver *drivers = NULL;
3582a5c12eSMatthew Dillon int numdrivers = 0;
3682a5c12eSMatthew Dillon int maxdrivers = 0;
3782a5c12eSMatthew Dillon 
3882a5c12eSMatthew Dillon u_int16_t Server_port;
3982a5c12eSMatthew Dillon 
4082a5c12eSMatthew Dillon static int numprobes = 0;
4182a5c12eSMatthew Dillon static int probe_sock[64];
4282a5c12eSMatthew Dillon static struct timeval probe_timeout;
4382a5c12eSMatthew Dillon 
4482a5c12eSMatthew Dillon struct driver *
next_driver(void)456beb426bSSascha Wildner next_driver(void)
4682a5c12eSMatthew Dillon {
4782a5c12eSMatthew Dillon 
4882a5c12eSMatthew Dillon 	return next_driver_fd(-1);
4982a5c12eSMatthew Dillon }
5082a5c12eSMatthew Dillon 
5182a5c12eSMatthew Dillon struct driver *
next_driver_fd(int fd)526beb426bSSascha Wildner next_driver_fd(int fd)
5382a5c12eSMatthew Dillon {
5482a5c12eSMatthew Dillon 	fd_set	r;
5582a5c12eSMatthew Dillon 	int	maxfd = -1;
56435f923eSSascha Wildner 	int	i, s, ret;
57435f923eSSascha Wildner 	socklen_t len;
5882a5c12eSMatthew Dillon 	struct driver *driver;
5982a5c12eSMatthew Dillon 	u_int16_t resp;
6082a5c12eSMatthew Dillon 
6182a5c12eSMatthew Dillon 	if (fd == -1 && numprobes == 0)
6282a5c12eSMatthew Dillon 		return NULL;
6382a5c12eSMatthew Dillon 
6482a5c12eSMatthew Dillon     again:
6582a5c12eSMatthew Dillon 	FD_ZERO(&r);
6682a5c12eSMatthew Dillon 	if (fd != -1) {
6782a5c12eSMatthew Dillon 		FD_SET(fd, &r);
6882a5c12eSMatthew Dillon 		maxfd = fd;
6982a5c12eSMatthew Dillon 	}
7082a5c12eSMatthew Dillon 	for (i = 0; i < numprobes; i++) {
7182a5c12eSMatthew Dillon 		FD_SET(probe_sock[i], &r);
7282a5c12eSMatthew Dillon 		if (probe_sock[i] > maxfd)
7382a5c12eSMatthew Dillon 			maxfd = probe_sock[i];
7482a5c12eSMatthew Dillon 	}
7582a5c12eSMatthew Dillon 
7682a5c12eSMatthew Dillon 	probe_timeout.tv_sec = LIST_DELAY;
7782a5c12eSMatthew Dillon 	probe_timeout.tv_usec = 0;
7882a5c12eSMatthew Dillon 	ret = select(maxfd + 1, &r, NULL, NULL, &probe_timeout);
7982a5c12eSMatthew Dillon 
8082a5c12eSMatthew Dillon 	if (ret == -1) {
8182a5c12eSMatthew Dillon 		if (errno == EINTR)
8282a5c12eSMatthew Dillon 			goto again;
8382a5c12eSMatthew Dillon 		err(1, "select");
8482a5c12eSMatthew Dillon 	}
8582a5c12eSMatthew Dillon 
8682a5c12eSMatthew Dillon 	if (ret == 0) {
8782a5c12eSMatthew Dillon 		/* Timeout - close all sockets */
8882a5c12eSMatthew Dillon 		for (i = 0; i < numprobes; i++)
8982a5c12eSMatthew Dillon 			close(probe_sock[i]);
9082a5c12eSMatthew Dillon 		numprobes = 0;
9182a5c12eSMatthew Dillon 		return NULL;
9282a5c12eSMatthew Dillon 	}
9382a5c12eSMatthew Dillon 
9482a5c12eSMatthew Dillon 	if (fd != -1 && FD_ISSET(fd, &r))
9582a5c12eSMatthew Dillon 		/* Keypress. Return magic number */
9682a5c12eSMatthew Dillon 		return (struct driver *)-1;
9782a5c12eSMatthew Dillon 
9882a5c12eSMatthew Dillon 	for (i = 0; i < numprobes; i++)
9982a5c12eSMatthew Dillon 		/* Find the first ready socket */
10082a5c12eSMatthew Dillon 		if (FD_ISSET(probe_sock[i], &r))
10182a5c12eSMatthew Dillon 			break;
10282a5c12eSMatthew Dillon 
10382a5c12eSMatthew Dillon 	s = probe_sock[i];
10482a5c12eSMatthew Dillon 
10582a5c12eSMatthew Dillon 	if (numdrivers >= maxdrivers) {
10682a5c12eSMatthew Dillon 		if (maxdrivers) {
10782a5c12eSMatthew Dillon 			maxdrivers *= 2;
10882a5c12eSMatthew Dillon 			drivers = realloc(drivers, sizeof *driver * maxdrivers);
10982a5c12eSMatthew Dillon 		} else {
11082a5c12eSMatthew Dillon 			maxdrivers = 16;
11182a5c12eSMatthew Dillon 			drivers = calloc(sizeof *driver, maxdrivers);
11282a5c12eSMatthew Dillon 		}
11382a5c12eSMatthew Dillon 		if (drivers == NULL)
11482a5c12eSMatthew Dillon 			err(1, "malloc");
11582a5c12eSMatthew Dillon 	}
11682a5c12eSMatthew Dillon 	driver = &drivers[numdrivers];
11782a5c12eSMatthew Dillon 	len = sizeof driver->addr;
11882a5c12eSMatthew Dillon 	ret = recvfrom(s, &resp, sizeof resp, 0, &driver->addr, &len);
11982a5c12eSMatthew Dillon 	if (ret == -1)
12082a5c12eSMatthew Dillon 		goto again;
12182a5c12eSMatthew Dillon 	driver->response = ntohs(resp);
12282a5c12eSMatthew Dillon 
12382a5c12eSMatthew Dillon 	switch (driver->addr.sa_family) {
12482a5c12eSMatthew Dillon 	case AF_INET:
12582a5c12eSMatthew Dillon 	case AF_INET6:
12682a5c12eSMatthew Dillon 		((struct sockaddr_in *)&driver->addr)->sin_port =
12782a5c12eSMatthew Dillon 		    htons(driver->response);
12882a5c12eSMatthew Dillon 		break;
12982a5c12eSMatthew Dillon 	}
13082a5c12eSMatthew Dillon 	numdrivers++;
13182a5c12eSMatthew Dillon 	return driver;
13282a5c12eSMatthew Dillon }
13382a5c12eSMatthew Dillon 
13482a5c12eSMatthew Dillon /* Return the hostname for a driver. */
13582a5c12eSMatthew Dillon const char *
driver_name(struct driver * driver)1366beb426bSSascha Wildner driver_name(struct driver *driver)
13782a5c12eSMatthew Dillon {
13882a5c12eSMatthew Dillon 	const char *name;
13982a5c12eSMatthew Dillon 	static char buf[80];
14082a5c12eSMatthew Dillon 	struct hostent *hp;
14182a5c12eSMatthew Dillon 	struct sockaddr_in *sin;
14282a5c12eSMatthew Dillon 
14382a5c12eSMatthew Dillon 	name = NULL;
14482a5c12eSMatthew Dillon 
14582a5c12eSMatthew Dillon 	if (driver->addr.sa_family == AF_INET) {
14682a5c12eSMatthew Dillon 		sin = (struct sockaddr_in *)&driver->addr;
14715b85273SSascha Wildner 		hp = gethostbyaddr(&sin->sin_addr, sizeof sin->sin_addr,
14815b85273SSascha Wildner 		    AF_INET);
14982a5c12eSMatthew Dillon 		if (hp != NULL)
15082a5c12eSMatthew Dillon 			name = hp->h_name;
15182a5c12eSMatthew Dillon 		else {
15282a5c12eSMatthew Dillon 			name = inet_ntop(AF_INET, &sin->sin_addr,
15382a5c12eSMatthew Dillon 			    buf, sizeof buf);
15482a5c12eSMatthew Dillon 		}
15582a5c12eSMatthew Dillon 	}
15682a5c12eSMatthew Dillon 
15782a5c12eSMatthew Dillon 	return name;
15882a5c12eSMatthew Dillon }
15982a5c12eSMatthew Dillon 
16082a5c12eSMatthew Dillon static int
start_probe(struct sockaddr * addr,u_int16_t req)16182a5c12eSMatthew Dillon start_probe(struct sockaddr *addr, u_int16_t req)
16282a5c12eSMatthew Dillon {
16382a5c12eSMatthew Dillon 	u_int16_t msg;
16482a5c12eSMatthew Dillon 	int s;
16582a5c12eSMatthew Dillon 	int enable;
16682a5c12eSMatthew Dillon 
16782a5c12eSMatthew Dillon 	if (numprobes >= (int)(sizeof probe_sock / sizeof probe_sock[0])) {
16882a5c12eSMatthew Dillon 		/* Just ridiculous */
16982a5c12eSMatthew Dillon 		return -1;
17082a5c12eSMatthew Dillon 	}
17182a5c12eSMatthew Dillon 
17282a5c12eSMatthew Dillon 	s = socket(addr->sa_family, SOCK_DGRAM, 0);
17382a5c12eSMatthew Dillon 	if (s < 0) {
17482a5c12eSMatthew Dillon 		warn("socket");
17582a5c12eSMatthew Dillon 		return -1;
17682a5c12eSMatthew Dillon 	}
17782a5c12eSMatthew Dillon 
17882a5c12eSMatthew Dillon 	enable = 1;
17982a5c12eSMatthew Dillon 	setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof enable);
18082a5c12eSMatthew Dillon 
18182a5c12eSMatthew Dillon 	switch (addr->sa_family) {
18282a5c12eSMatthew Dillon 	case AF_INET:
18382a5c12eSMatthew Dillon 	case AF_INET6:
18482a5c12eSMatthew Dillon 		((struct sockaddr_in *)addr)->sin_port =
18582a5c12eSMatthew Dillon 		    htons(Server_port);
18682a5c12eSMatthew Dillon 		break;
18782a5c12eSMatthew Dillon 	}
18882a5c12eSMatthew Dillon 
18982a5c12eSMatthew Dillon 	msg = htons(req);
19082a5c12eSMatthew Dillon 	if (sendto(s, &msg, sizeof msg, 0, addr, addr->sa_len) == -1)
19182a5c12eSMatthew Dillon 		warn("sendto");
19282a5c12eSMatthew Dillon 	probe_sock[numprobes++] = s;
19382a5c12eSMatthew Dillon 
19482a5c12eSMatthew Dillon 	return 0;
19582a5c12eSMatthew Dillon }
19682a5c12eSMatthew Dillon 
19782a5c12eSMatthew Dillon void
probe_cleanup(void)1986beb426bSSascha Wildner probe_cleanup(void)
19982a5c12eSMatthew Dillon {
20082a5c12eSMatthew Dillon 	int i;
20182a5c12eSMatthew Dillon 
20282a5c12eSMatthew Dillon 	for (i = 0; i < numprobes; i++)
20382a5c12eSMatthew Dillon 		close(probe_sock[i]);
20482a5c12eSMatthew Dillon 	numprobes = 0;
20582a5c12eSMatthew Dillon }
20682a5c12eSMatthew Dillon 
20782a5c12eSMatthew Dillon /*
20882a5c12eSMatthew Dillon  * If we have no preferred host then send a broadcast message to everyone.
20982a5c12eSMatthew Dillon  * Otherwise, send the request message only to the preferred host.
21082a5c12eSMatthew Dillon  */
21182a5c12eSMatthew Dillon void
probe_drivers(u_int16_t req,char * preferred)2126beb426bSSascha Wildner probe_drivers(u_int16_t req, char *preferred)
21382a5c12eSMatthew Dillon {
21482a5c12eSMatthew Dillon 	struct sockaddr_in *target;
21582a5c12eSMatthew Dillon 	struct sockaddr_in localhost;
21682a5c12eSMatthew Dillon 	struct hostent *he;
21782a5c12eSMatthew Dillon         char *inbuf = NULL, *ninbuf;
21882a5c12eSMatthew Dillon         struct ifconf ifc;
21982a5c12eSMatthew Dillon         struct ifreq *ifr;
22082a5c12eSMatthew Dillon         int fd, inlen = 8192;
22182a5c12eSMatthew Dillon         int i, len;
22282a5c12eSMatthew Dillon 
22382a5c12eSMatthew Dillon 	numdrivers = 0;
22482a5c12eSMatthew Dillon 
22582a5c12eSMatthew Dillon 	probe_cleanup();
22682a5c12eSMatthew Dillon 
22782a5c12eSMatthew Dillon 	/* Send exclusively to a preferred host. */
22882a5c12eSMatthew Dillon 	if (preferred) {
22982a5c12eSMatthew Dillon 		struct sockaddr_in sin;
23082a5c12eSMatthew Dillon 
23182a5c12eSMatthew Dillon 		target = NULL;
23282a5c12eSMatthew Dillon 
23382a5c12eSMatthew Dillon 		if (!target) {
23482a5c12eSMatthew Dillon 			sin.sin_family = AF_INET;
23582a5c12eSMatthew Dillon 			sin.sin_len = sizeof sin;
23682a5c12eSMatthew Dillon 			if (inet_pton(AF_INET, preferred, &sin.sin_addr) == 1)
23782a5c12eSMatthew Dillon 				target = &sin;
23882a5c12eSMatthew Dillon 		}
23982a5c12eSMatthew Dillon 
24082a5c12eSMatthew Dillon 		if (!target && (he = gethostbyname(preferred)) != NULL) {
24182a5c12eSMatthew Dillon 			sin.sin_family = he->h_addrtype;
24282a5c12eSMatthew Dillon 			sin.sin_len = sizeof sin;
24382a5c12eSMatthew Dillon 			memcpy(&sin.sin_addr, he->h_addr, he->h_length);
24482a5c12eSMatthew Dillon 			target = &sin;
24582a5c12eSMatthew Dillon 		}
24682a5c12eSMatthew Dillon 
24782a5c12eSMatthew Dillon 		if (!target)
24882a5c12eSMatthew Dillon 			errx(1, "Bad hostname: %s", preferred);
24982a5c12eSMatthew Dillon 
25082a5c12eSMatthew Dillon 		start_probe((struct sockaddr *)target, req);
25182a5c12eSMatthew Dillon 		return;
25282a5c12eSMatthew Dillon 	}
25382a5c12eSMatthew Dillon 
25482a5c12eSMatthew Dillon 	/* Send a query to the local machine: */
25582a5c12eSMatthew Dillon 	localhost.sin_family = AF_INET;
25682a5c12eSMatthew Dillon 	localhost.sin_len = sizeof localhost;
25782a5c12eSMatthew Dillon 	localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
25882a5c12eSMatthew Dillon 	start_probe((struct sockaddr *)&localhost, req);
25982a5c12eSMatthew Dillon 
26082a5c12eSMatthew Dillon         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
26182a5c12eSMatthew Dillon                 err(1, "socket");
26282a5c12eSMatthew Dillon 
26382a5c12eSMatthew Dillon 	/* Find all attached networks: */
26482a5c12eSMatthew Dillon         while (1) {
26582a5c12eSMatthew Dillon                 ifc.ifc_len = inlen;
26682a5c12eSMatthew Dillon                 if ((ninbuf = realloc(inbuf, inlen)) == NULL)
26782a5c12eSMatthew Dillon 			err(1, "malloc");
26882a5c12eSMatthew Dillon                 ifc.ifc_buf = inbuf = ninbuf;
26982a5c12eSMatthew Dillon                 if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0)
27082a5c12eSMatthew Dillon                         err(1, "SIOCGIFCONF");
27182a5c12eSMatthew Dillon                 if (ifc.ifc_len + (int)sizeof(*ifr) < inlen)
27282a5c12eSMatthew Dillon                         break;
27382a5c12eSMatthew Dillon                 inlen *= 2;
27482a5c12eSMatthew Dillon         }
27582a5c12eSMatthew Dillon 
27682a5c12eSMatthew Dillon 	/* Send a request to every attached broadcast address: */
27782a5c12eSMatthew Dillon         ifr = ifc.ifc_req;
27882a5c12eSMatthew Dillon         for (i = 0; i < ifc.ifc_len;
27982a5c12eSMatthew Dillon              i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) {
28082a5c12eSMatthew Dillon                 len = sizeof(ifr->ifr_name) +
28182a5c12eSMatthew Dillon                       (ifr->ifr_addr.sa_len > sizeof(struct sockaddr) ?
28282a5c12eSMatthew Dillon                        ifr->ifr_addr.sa_len : sizeof(struct sockaddr));
28382a5c12eSMatthew Dillon 
28482a5c12eSMatthew Dillon 		if (ifr->ifr_addr.sa_family != AF_INET)
28582a5c12eSMatthew Dillon 			continue;
28682a5c12eSMatthew Dillon 
28782a5c12eSMatthew Dillon                 if (ioctl(fd, SIOCGIFFLAGS, (caddr_t)ifr) < 0) {
28882a5c12eSMatthew Dillon                         warn("%s: SIOCGIFFLAGS", ifr->ifr_name);
28982a5c12eSMatthew Dillon 			continue;
29082a5c12eSMatthew Dillon 		}
29182a5c12eSMatthew Dillon                 if ((ifr->ifr_flags & IFF_UP) == 0)
29282a5c12eSMatthew Dillon 			continue;
29382a5c12eSMatthew Dillon 		if ((ifr->ifr_flags & IFF_BROADCAST) != 0) {
29482a5c12eSMatthew Dillon 			if (ioctl(fd, SIOCGIFBRDADDR, (caddr_t)ifr) < 0)  {
29582a5c12eSMatthew Dillon 				warn("%s: SIOCGIFBRDADDR", ifr->ifr_name);
29682a5c12eSMatthew Dillon 				continue;
29782a5c12eSMatthew Dillon 			}
29882a5c12eSMatthew Dillon 			target = (struct sockaddr_in *)&ifr->ifr_dstaddr;
29982a5c12eSMatthew Dillon 		} else if ((ifr->ifr_flags & IFF_POINTOPOINT) != 0) {
30082a5c12eSMatthew Dillon 			if (ioctl(fd, SIOCGIFDSTADDR, (caddr_t)ifr) < 0)  {
30182a5c12eSMatthew Dillon 				warn("%s: SIOCGIFDSTADDR", ifr->ifr_name);
30282a5c12eSMatthew Dillon 				continue;
30382a5c12eSMatthew Dillon 			}
30482a5c12eSMatthew Dillon 			target = (struct sockaddr_in *)&ifr->ifr_broadaddr;
30582a5c12eSMatthew Dillon 		} else
30682a5c12eSMatthew Dillon 			continue;
30782a5c12eSMatthew Dillon 
30882a5c12eSMatthew Dillon 		start_probe((struct sockaddr *)target, req);
30982a5c12eSMatthew Dillon 	}
31082a5c12eSMatthew Dillon 	free(inbuf);
311*d9f85b33Szrj 	close(fd);
31282a5c12eSMatthew Dillon }
313