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