--- netkit-rwho-0.17.orig/rwhod/rwhod.c
+++ netkit-rwho-0.17/rwhod/rwhod.c
@@ -29,6 +29,10 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
+
+ * Modified by Philippe Troin <phil@fifi.org> (added options & implemented
+ * them.
+
*/
char copyright[] =
@@ -47,6 +51,7 @@
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/file.h>
+#include <sys/types.h>
#include <net/if.h>
#include <netinet/in.h>
@@ -69,11 +74,13 @@
#include <arpa/inet.h>
#include <pwd.h>
#include <grp.h>
-
-#include "daemon.h"
+#include <time.h>
+#include <stdint.h>
#include "../version.h"
+typedef struct sockaddr_in SA;
+
#define ENDIAN LITTLE_ENDIAN
/*
@@ -95,7 +102,16 @@
static void broadcaster(void);
static int configure(int s);
static int verify(const char *name);
+#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2)
static int getloadavg(double ptr[3], int n);
+#endif
+
+/* This is the list of interface we want to listen on */
+struct wanted_neigh {
+ struct wanted_neigh *w_next;
+ char *w_ifname;
+ enum { W_USED_NOT, W_USED_ONCE, W_USED_MULTI } w_used;
+};
/*
* We communicate with each neighbor in
@@ -103,21 +119,30 @@
* started up. Neighbors are currently
* directly connected via a hardware interface.
*/
-struct neighbor {
+struct neighbor {
struct neighbor *n_next;
char *n_name; /* interface name */
- char *n_addr; /* who to send to */
+ SA *n_myaddr; /* My address on this i/f */
+ SA *n_mask; /* Netmask on this i/f */
+ SA *n_dstaddr; /* who to send to */
int n_addrlen; /* size of address */
int n_flags; /* should forward?, interface flags */
};
+static struct wanted_neigh *wanted_neigh;
static struct neighbor *neighbors;
static struct servent *sp;
static int sk;
-static int use_pointopoint = 0;
-static int use_broadcast = 0;
+static int use_pointopoint;
+static int use_broadcast;
static int need_init = 1;
-static int child_pid = 0;
+static int child_pid;
+static int use_forwarding;
+static int forwarded_packets;
+
+/* Max number of packets to forward between each alarm() tick.
+ If this number is exceeded, then the forwarding is switched off. */
+#define MAX_FWD_PACKETS (AL_INTERVAL)
#define WHDRSIZE (((caddr_t) &((struct whod *) 0)->wd_we) \
- ((caddr_t) 0))
@@ -126,24 +151,48 @@
static void termhandler(int);
static void sendpacket(struct whod *);
static void getboottime(struct whod *);
+static void forward(const SA *, const struct whod *, int cc);
+static void usage(void);
int
main(int argc, char *argv[])
{
+ struct wanted_neigh *wn;
+ int wn_dup;
struct sockaddr_in from;
- struct passwd *pw = 0;
+ struct passwd *pw;
struct stat st;
char path[64];
- char *user = NULL;
+ char *user = "rwhod";
int on = 1;
int opt;
+ time_t before;
if (getuid()) {
fprintf(stderr, "rwhod: not super user\n");
+ }
+ openlog("rwhod", LOG_PID, LOG_DAEMON);
+ sp = getservbyname("who", "udp");
+ if (sp == 0) {
+ fprintf(stderr, "rwhod: udp/who: unknown service\n");
+ exit(1);
+ }
+ if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ if (setsockopt(sk, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
+ syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
+ exit(1);
+ }
+ sine.sin_family = AF_INET;
+ sine.sin_port = sp->s_port;
+ if (bind(sk, (struct sockaddr *)&sine, sizeof(sine)) < 0) {
+ syslog(LOG_ERR, "bind: %m");
exit(1);
}
- while ((opt = getopt(argc, argv, "bpau:")) != EOF) {
+ while ((opt = getopt(argc, argv, "bpai:h?fu:")) != EOF) {
switch (opt) {
case 'b':
use_broadcast = 1;
@@ -155,31 +204,61 @@
use_broadcast = 1;
use_pointopoint = 1;
break;
+ case 'f':
+ use_forwarding = 1;
+ break;
+ case 'i':
+ wn_dup = 0;
+ for (wn = wanted_neigh; wn; wn = wn->w_next) {
+ if (strcmp(wn->w_ifname, optarg)== 0) {
+ wn_dup = 1;
+ break;
+ }
+ }
+ if (wn_dup) {
+ fprintf(stderr, "rwhod: warning: "
+ "duplicate interface %s in arguments\n",
+ optarg);
+ } else {
+ wn = malloc(sizeof(struct wanted_neigh));
+ if (wn == NULL) {
+ fprintf(stderr, "rwhod: out of memory\n");
+ exit(2);
+ }
+ wn->w_next = wanted_neigh;
+ wn->w_ifname = malloc(strlen(optarg)+1);
+ wn->w_used = W_USED_NOT;
+ if (wn->w_ifname == NULL) {
+ fprintf(stderr, "rwhod: out of memory\n");
+ exit(2);
+ }
+ strcpy(wn->w_ifname, optarg);
+ wanted_neigh = wn;
+ }
+ break;
case 'u':
user = optarg;
break;
case '?':
+ case 'h':
default:
- fprintf(stderr, "usage: rwhod [-bpa] [-u user]\n");
- exit(1);
- break;
+ usage();
}
}
if (optind<argc) {
- fprintf(stderr, "usage: rwhod [-bpa] [-u user]\n");
- exit(1);
+ usage();
}
- if (!use_pointopoint && !use_broadcast) {
+ if (!use_pointopoint && !use_broadcast && !wanted_neigh) {
/* use none is nonsensical; default to all */
use_pointopoint = 1;
use_broadcast = 1;
}
-
- sp = getservbyname("who", "udp");
- if (sp == 0) {
- fprintf(stderr, "rwhod: udp/who: unknown service\n");
+ if ((use_pointopoint || use_broadcast) && wanted_neigh) {
+ fprintf(stderr, "rwhod: cannot specify both -i and one of -b "
+ "-p -a\n");
exit(1);
}
+
#ifndef DEBUG
daemon(1, 0);
#endif
@@ -189,53 +268,20 @@
exit(1);
}
(void) signal(SIGHUP, huphandler);
- openlog("rwhod", LOG_PID, LOG_DAEMON);
-
- if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- syslog(LOG_ERR, "socket: %m");
- exit(1);
- }
- if (setsockopt(sk, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
- syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
- exit(1);
- }
- sine.sin_family = AF_INET;
- sine.sin_port = sp->s_port;
- if (bind(sk, (struct sockaddr *)&sine, sizeof(sine)) < 0) {
- syslog(LOG_ERR, "bind: %m");
- exit(1);
- }
(void) umask(022);
signal(SIGTERM, termhandler);
- child_pid = fork();
- if (child_pid < 0) {
- syslog(LOG_ERR, "fork: %m");
- exit(1);
- }
- if (child_pid == 0) {
- broadcaster();
- exit(0);
- }
/* We have to drop privs in two steps--first get the
* account info, then drop privs after chroot */
- if (user && (pw = getpwnam(user)) == NULL) {
+ if ((pw = getpwnam(user)) == NULL) {
syslog(LOG_ERR, "unknown user: %s", user);
exit(1);
}
- /* Chroot to the spool directory
- * (note this is already our $cwd) */
- if (chroot(_PATH_RWHODIR) < 0) {
- syslog(LOG_ERR, "chroot(%s): %m", _PATH_RWHODIR);
- kill(child_pid, SIGTERM);
- exit(1);
- }
-
/* Now drop privs */
- if (pw) {
+ if (pw->pw_uid) {
if (setgroups(1, &pw->pw_gid) < 0
|| setgid(pw->pw_gid) < 0
|| setuid(pw->pw_uid) < 0) {
@@ -244,10 +290,28 @@
}
}
+ if (!configure(sk))
+ exit(1);
+
+ child_pid = fork();
+ if (child_pid < 0) {
+ syslog(LOG_ERR, "fork: %m");
+ exit(1);
+ }
+ if (child_pid == 0) {
+ broadcaster();
+ exit(0);
+ }
+
+ before = 0;
for (;;) {
struct whod wd;
int cc, whod;
+#ifdef __GLIBC__
+ socklen_t len = sizeof(from);
+#else
size_t len = sizeof(from);
+#endif
memset(&wd, 0, sizeof(wd));
cc = recvfrom(sk, (char *)&wd, sizeof(struct whod), 0,
@@ -257,6 +321,8 @@
syslog(LOG_WARNING, "recv: %m");
continue;
}
+ if (cc < WHDRSIZE)
+ continue;
if (from.sin_port != sp->s_port) {
syslog(LOG_WARNING, "%d: bad from port",
ntohs(from.sin_port));
@@ -266,14 +332,24 @@
continue;
if (wd.wd_type != WHODTYPE_STATUS)
continue;
+
+ if (use_forwarding) {
+ time_t now = time(NULL);
+ if ((uintmax_t) (now - before) >= AL_INTERVAL) {
+ before = now;
+ forwarded_packets = 0;
+ }
+ forward(&from, &wd, cc);
+ }
+
/*
* Ensure null termination of the name within the packet.
* Otherwise we might overflow or read past the end.
*/
wd.wd_hostname[sizeof(wd.wd_hostname)-1] = 0;
if (!verify(wd.wd_hostname)) {
- syslog(LOG_WARNING, "malformed host name from %x",
- from.sin_addr);
+ syslog(LOG_WARNING, "malformed host name from %s",
+ inet_ntoa(from.sin_addr));
continue;
}
snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname);
@@ -306,7 +382,7 @@
}
#endif
wd.wd_recvtime = time(NULL);
- write(whod, (char *)&wd, cc);
+ write(whod, &wd, cc);
if (fstat(whod, &st) < 0 || st.st_size > cc)
ftruncate(whod, cc);
(void) close(whod);
@@ -345,9 +421,6 @@
size_t mynamelen;
struct whod mywd;
- if (!configure(sk))
- exit(1);
-
/*
* Establish host name as returned by system.
*/
@@ -357,7 +430,7 @@
}
if ((cp = index(myname, '.')) != NULL)
*cp = '\0';
- mynamelen = strlen(myname);
+ mynamelen = strlen(myname) + 1;
if (mynamelen > sizeof(mywd.wd_hostname))
mynamelen = sizeof(mywd.wd_hostname);
strncpy(mywd.wd_hostname, myname, mynamelen);
@@ -448,7 +521,9 @@
}
we = wd->wd_we;
for (i = 0; i < nutmps; i++) {
- if (stat(we->we_utmp.out_line, &stb) >= 0)
+ const char *p = we->we_utmp.out_line;
+
+ if (!strchr(p, ':') && stat(p, &stb) >= 0)
we->we_idle = htonl(now - stb.st_atime);
we++;
}
@@ -460,10 +535,10 @@
wd->wd_vers = WHODVERSION;
wd->wd_type = WHODTYPE_STATUS;
for (np = neighbors; np != NULL; np = np->n_next) {
- if (sendto(sk, (char *)wd, cc, 0,
- (struct sockaddr *) np->n_addr, np->n_addrlen) < 0)
+ if (sendto(sk, wd, cc, 0,
+ (struct sockaddr *) np->n_dstaddr, np->n_addrlen) < 0)
syslog(LOG_ERR, "sendto(%s): %m",
- inet_ntoa(((struct sockaddr_in *)np->n_addr)->sin_addr));
+ inet_ntoa(np->n_dstaddr->sin_addr));
}
if (nutmps && chdir(_PATH_RWHODIR)) {
@@ -472,6 +547,7 @@
}
}
+#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2)
/*
* Taken from:
*
@@ -518,6 +594,7 @@
fclose(fp);
return 0;
}
+#endif /* __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) */
void
@@ -566,7 +643,7 @@
exit(1);
}
(void) lseek(kmemf, (long)nl[NL_BOOTTIME].n_value, L_SET);
- (void) read(kmemf, (char *)&wd->wd_boottime,
+ (void) read(kmemf, &wd->wd_boottime,
sizeof (wd->wd_boottime));
wd->wd_boottime = htonl(wd->wd_boottime);
#endif
@@ -584,10 +661,11 @@
struct ifreq ifreq, *ifr;
struct sockaddr_in *sn;
register struct neighbor *np;
+ struct wanted_neigh *wn;
ifc.ifc_len = sizeof (buf);
ifc.ifc_buf = buf;
- if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
syslog(LOG_ERR, "ioctl (get interface configuration)");
return (0);
}
@@ -600,7 +678,11 @@
#endif
cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
for (cp = buf; cp < cplim;
+#ifdef linux
+ cp += sizeof(struct ifreq)) {
+#else
cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
+#endif
ifr = (struct ifreq *)cp;
for (np = neighbors; np != NULL; np = np->n_next)
if (np->n_name &&
@@ -614,63 +696,170 @@
continue;
np->n_name = malloc(strlen(ifr->ifr_name) + 1);
if (np->n_name == NULL) {
- free((char *)np);
+ free(np);
continue;
}
strcpy(np->n_name, ifr->ifr_name);
np->n_addrlen = sizeof (ifr->ifr_addr);
- np->n_addr = malloc(np->n_addrlen);
- if (np->n_addr == NULL) {
+
+ np->n_dstaddr = malloc(np->n_addrlen);
+ if (np->n_dstaddr == NULL) {
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ bzero(np->n_dstaddr, np->n_addrlen);
+
+ np->n_myaddr = malloc(np->n_addrlen);
+ if (np->n_myaddr == NULL) {
+ free(np->n_dstaddr);
free(np->n_name);
- free((char *)np);
+ free(np);
continue;
}
- bcopy((char *)&ifr->ifr_addr, np->n_addr, np->n_addrlen);
- if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ bzero(np->n_myaddr, np->n_addrlen);
+
+ np->n_mask = malloc(np->n_addrlen);
+ if (np->n_mask == NULL) {
+ free(np->n_myaddr);
+ free(np->n_dstaddr);
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ bzero(np->n_mask, np->n_addrlen);
+
+ /* Initialize both my address and destination address by
+ the interface address. The destination address will be
+ overwritten when the interface has IFF_BROADCAST or
+ IFF_POINTOPOINT. */
+ bcopy(&ifr->ifr_addr, np->n_dstaddr, np->n_addrlen);
+ bcopy(&ifr->ifr_addr, np->n_myaddr, np->n_addrlen);
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0) {
syslog(LOG_ERR, "ioctl (get interface flags)");
- free((char *)np);
+ free(np->n_myaddr);
+ free(np->n_dstaddr);
+ free(np->n_name);
+ free(np);
continue;
}
if ((ifreq.ifr_flags & IFF_UP) == 0 ||
- (ifreq.ifr_flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) {
- free((char *)np);
+ (ifreq.ifr_flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0 ||
+ (ifreq.ifr_flags & IFF_LOOPBACK) != 0) {
+ free(np->n_myaddr);
+ free(np->n_dstaddr);
+ free(np->n_name);
+ free(np);
continue;
}
+ if (wanted_neigh) {
+ int found = 0;
+ for (wn = wanted_neigh; wn; wn = wn->w_next)
+ if (strcmp(wn->w_ifname, ifreq.ifr_name)==0) {
+ found = 1;
+ break;
+ }
+ if (!found) {
+ free(np->n_mask);
+ free(np->n_myaddr);
+ free(np->n_dstaddr);
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ switch (wn->w_used) {
+ case W_USED_NOT:
+ wn->w_used = W_USED_ONCE;
+ break;
+ case W_USED_ONCE:
+ syslog(LOG_ERR,
+ "specified interface %s more than once",
+ wn->w_ifname);
+ wn->w_used = W_USED_MULTI;
+ break;
+ case W_USED_MULTI:
+ /* oh well... don't tell again... */
+ break;
+ default:
+ syslog(LOG_CRIT, "w_used=%d on %s",
+ wn->w_used, wn->w_ifname);
+ abort();
+ }
+ }
np->n_flags = ifreq.ifr_flags;
if (np->n_flags & IFF_POINTOPOINT) {
- if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
+ if (ioctl(s, SIOCGIFDSTADDR, &ifreq) < 0) {
syslog(LOG_ERR, "ioctl (get dstaddr)");
+ free(np->n_mask);
+ free(np->n_myaddr);
+ free(np->n_dstaddr);
+ free(np->n_name);
free(np);
continue;
}
- if (!use_pointopoint) {
+ if (!wanted_neigh && !use_pointopoint) {
+ free(np->n_mask);
+ free(np->n_myaddr);
+ free(np->n_dstaddr);
+ free(np->n_name);
free(np);
continue;
}
/* we assume addresses are all the same size */
- bcopy((char *)&ifreq.ifr_dstaddr,
- np->n_addr, np->n_addrlen);
+ bcopy(&ifreq.ifr_dstaddr, np->n_dstaddr, np->n_addrlen);
}
if (np->n_flags & IFF_BROADCAST) {
- if (ioctl(s, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ if (ioctl(s, SIOCGIFBRDADDR, &ifreq) < 0) {
syslog(LOG_ERR, "ioctl (get broadaddr)");
+ free(np->n_mask);
+ free(np->n_myaddr);
+ free(np->n_dstaddr);
+ free(np->n_name);
free(np);
continue;
}
- if (!use_broadcast) {
+ if (!wanted_neigh && !use_broadcast) {
+ free(np->n_mask);
+ free(np->n_myaddr);
+ free(np->n_dstaddr);
+ free(np->n_name);
free(np);
continue;
}
/* we assume addresses are all the same size */
- bcopy((char *)&ifreq.ifr_broadaddr,
- np->n_addr, np->n_addrlen);
+ bcopy(&ifreq.ifr_broadaddr, np->n_dstaddr, np->n_addrlen);
+
+ /* Get netmask */
+ if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0) {
+ syslog(LOG_ERR, "ioctl (get netmask)");
+ free(np->n_mask);
+ free(np->n_myaddr);
+ free(np->n_dstaddr);
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ bcopy((char*)&ifreq.ifr_netmask,
+ np->n_mask, np->n_addrlen);
}
/* gag, wish we could get rid of Internet dependencies */
- sn = (struct sockaddr_in *)np->n_addr;
+ sn = (SA *)np->n_dstaddr;
sn->sin_port = sp->s_port;
np->n_next = neighbors;
neighbors = np;
}
+
+ /* Check for unfound i/f */
+ for (wn = wanted_neigh; wn; wn = wn->w_next)
+ if (wn->w_used == W_USED_NOT)
+ syslog(LOG_WARNING, "didn't find interface %s",
+ wn->w_ifname);
+
+ /* Dump out used i/f */
+ for (np = neighbors; np; np = np->n_next)
+ syslog(LOG_INFO, "sending on interface %s", np->n_name);
+
return (1);
}
@@ -692,7 +881,7 @@
{
register struct whod *w = (struct whod *)buf;
register struct whoent *we;
- struct sockaddr_in *sn = (struct sockaddr_in *)to;
+ struct sockaddr_in *sn = (SA *)to;
char *interval();
printf("sendto %x.%d\n", ntohl(sn->sin_addr.s_addr), ntohs(sn->sin_port));
@@ -746,3 +935,63 @@
return (resbuf);
}
#endif
+
+/* Eventually forward the packet */
+static void
+forward(const SA *from, const struct whod *wd, int cc)
+{
+ struct neighbor *np;
+ int looped_back = 0;
+
+ /* Scan to see if the packet was sent by us */
+ for (np = neighbors; np != NULL; np = np->n_next)
+ if (from->sin_addr.s_addr ==
+ np->n_myaddr->sin_addr.s_addr) {
+ looped_back = 1;
+ break;
+ }
+
+ if (!looped_back) {
+ sigset_t saved_set;
+ sigset_t mask_set;
+
+ sigemptyset(&mask_set);
+ sigaddset(&mask_set, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask_set, &saved_set);
+
+ if (++forwarded_packets > MAX_FWD_PACKETS) {
+ syslog(LOG_ERR, "too many forward requests, "
+ "disabling forwarding");
+ use_forwarding = 0;
+ }
+
+ sigprocmask(SIG_SETMASK, &saved_set, NULL);
+
+ /* Re-broadcast packet on all interfaces... */
+ for (np = neighbors; np != NULL; np = np->n_next) {
+ /* .. but do not rebroadcast on the incoming interface */
+ if (((np->n_flags & IFF_BROADCAST) &&
+ (from->sin_addr.s_addr &
+ np->n_mask->sin_addr.s_addr) !=
+ (np->n_myaddr->sin_addr.s_addr &
+ np->n_mask->sin_addr.s_addr)) ||
+ ((np->n_flags & IFF_POINTOPOINT) &&
+ (from->sin_addr.s_addr) !=
+ np->n_dstaddr->sin_addr.s_addr)) {
+ if (sendto(sk, wd, cc, 0,
+ (struct sockaddr *)np->n_dstaddr,
+ np->n_addrlen) < 0)
+ syslog(LOG_ERR,
+ "forwarding sendto(%s): %m",
+ inet_ntoa(np->n_dstaddr->sin_addr));
+ }
+ }
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rwhod [-bpaf] [-i <ifname>] [-u user]...\n");
+ exit(1);
+}