/*- * Copyright 2001, David Leonard. All rights reserved. * Redistribution and use in source and binary forms with or without * modification are permitted provided that this notice is preserved. * This software is provided ``as is'' without express or implied warranty. * * $OpenBSD: list.c,v 1.5 2007/09/04 22:39:31 hshoexer Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hunt.h" #include "list.h" /* Wait at most 5 seconds for a reply */ #define LIST_DELAY 5 struct driver *drivers = NULL; int numdrivers = 0; int maxdrivers = 0; u_int16_t Server_port; static int numprobes = 0; static int probe_sock[64]; static struct timeval probe_timeout; struct driver * next_driver(void) { return next_driver_fd(-1); } struct driver * next_driver_fd(int fd) { fd_set r; int maxfd = -1; int i, s, ret; socklen_t len; struct driver *driver; u_int16_t resp; if (fd == -1 && numprobes == 0) return NULL; again: FD_ZERO(&r); if (fd != -1) { FD_SET(fd, &r); maxfd = fd; } for (i = 0; i < numprobes; i++) { FD_SET(probe_sock[i], &r); if (probe_sock[i] > maxfd) maxfd = probe_sock[i]; } probe_timeout.tv_sec = LIST_DELAY; probe_timeout.tv_usec = 0; ret = select(maxfd + 1, &r, NULL, NULL, &probe_timeout); if (ret == -1) { if (errno == EINTR) goto again; err(1, "select"); } if (ret == 0) { /* Timeout - close all sockets */ for (i = 0; i < numprobes; i++) close(probe_sock[i]); numprobes = 0; return NULL; } if (fd != -1 && FD_ISSET(fd, &r)) /* Keypress. Return magic number */ return (struct driver *)-1; for (i = 0; i < numprobes; i++) /* Find the first ready socket */ if (FD_ISSET(probe_sock[i], &r)) break; s = probe_sock[i]; if (numdrivers >= maxdrivers) { if (maxdrivers) { maxdrivers *= 2; drivers = realloc(drivers, sizeof *driver * maxdrivers); } else { maxdrivers = 16; drivers = calloc(sizeof *driver, maxdrivers); } if (drivers == NULL) err(1, "malloc"); } driver = &drivers[numdrivers]; len = sizeof driver->addr; ret = recvfrom(s, &resp, sizeof resp, 0, &driver->addr, &len); if (ret == -1) goto again; driver->response = ntohs(resp); switch (driver->addr.sa_family) { case AF_INET: case AF_INET6: ((struct sockaddr_in *)&driver->addr)->sin_port = htons(driver->response); break; } numdrivers++; return driver; } /* Return the hostname for a driver. */ const char * driver_name(struct driver *driver) { const char *name; static char buf[80]; struct hostent *hp; struct sockaddr_in *sin; name = NULL; if (driver->addr.sa_family == AF_INET) { sin = (struct sockaddr_in *)&driver->addr; hp = gethostbyaddr(&sin->sin_addr, sizeof sin->sin_addr, AF_INET); if (hp != NULL) name = hp->h_name; else { name = inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof buf); } } return name; } static int start_probe(struct sockaddr *addr, u_int16_t req) { u_int16_t msg; int s; int enable; if (numprobes >= (int)(sizeof probe_sock / sizeof probe_sock[0])) { /* Just ridiculous */ return -1; } s = socket(addr->sa_family, SOCK_DGRAM, 0); if (s < 0) { warn("socket"); return -1; } enable = 1; setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof enable); switch (addr->sa_family) { case AF_INET: case AF_INET6: ((struct sockaddr_in *)addr)->sin_port = htons(Server_port); break; } msg = htons(req); if (sendto(s, &msg, sizeof msg, 0, addr, addr->sa_len) == -1) warn("sendto"); probe_sock[numprobes++] = s; return 0; } void probe_cleanup(void) { int i; for (i = 0; i < numprobes; i++) close(probe_sock[i]); numprobes = 0; } /* * If we have no preferred host then send a broadcast message to everyone. * Otherwise, send the request message only to the preferred host. */ void probe_drivers(u_int16_t req, char *preferred) { struct sockaddr_in *target; struct sockaddr_in localhost; struct hostent *he; char *inbuf = NULL, *ninbuf; struct ifconf ifc; struct ifreq *ifr; int fd, inlen = 8192; int i, len; numdrivers = 0; probe_cleanup(); /* Send exclusively to a preferred host. */ if (preferred) { struct sockaddr_in sin; target = NULL; if (!target) { sin.sin_family = AF_INET; sin.sin_len = sizeof sin; if (inet_pton(AF_INET, preferred, &sin.sin_addr) == 1) target = &sin; } if (!target && (he = gethostbyname(preferred)) != NULL) { sin.sin_family = he->h_addrtype; sin.sin_len = sizeof sin; memcpy(&sin.sin_addr, he->h_addr, he->h_length); target = &sin; } if (!target) errx(1, "Bad hostname: %s", preferred); start_probe((struct sockaddr *)target, req); return; } /* Send a query to the local machine: */ localhost.sin_family = AF_INET; localhost.sin_len = sizeof localhost; localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK); start_probe((struct sockaddr *)&localhost, req); if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) err(1, "socket"); /* Find all attached networks: */ while (1) { ifc.ifc_len = inlen; if ((ninbuf = realloc(inbuf, inlen)) == NULL) err(1, "malloc"); ifc.ifc_buf = inbuf = ninbuf; if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) err(1, "SIOCGIFCONF"); if (ifc.ifc_len + (int)sizeof(*ifr) < inlen) break; inlen *= 2; } /* Send a request to every attached broadcast address: */ ifr = ifc.ifc_req; for (i = 0; i < ifc.ifc_len; i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) { len = sizeof(ifr->ifr_name) + (ifr->ifr_addr.sa_len > sizeof(struct sockaddr) ? ifr->ifr_addr.sa_len : sizeof(struct sockaddr)); if (ifr->ifr_addr.sa_family != AF_INET) continue; if (ioctl(fd, SIOCGIFFLAGS, (caddr_t)ifr) < 0) { warn("%s: SIOCGIFFLAGS", ifr->ifr_name); continue; } if ((ifr->ifr_flags & IFF_UP) == 0) continue; if ((ifr->ifr_flags & IFF_BROADCAST) != 0) { if (ioctl(fd, SIOCGIFBRDADDR, (caddr_t)ifr) < 0) { warn("%s: SIOCGIFBRDADDR", ifr->ifr_name); continue; } target = (struct sockaddr_in *)&ifr->ifr_dstaddr; } else if ((ifr->ifr_flags & IFF_POINTOPOINT) != 0) { if (ioctl(fd, SIOCGIFDSTADDR, (caddr_t)ifr) < 0) { warn("%s: SIOCGIFDSTADDR", ifr->ifr_name); continue; } target = (struct sockaddr_in *)&ifr->ifr_broadaddr; } else continue; start_probe((struct sockaddr *)target, req); } free(inbuf); (void) close(fd); }