home *** CD-ROM | disk | FTP | other *** search
- From: rsalz@uunet.uu.net (Rich Salz)
- Newsgroups: comp.sources.unix
- Subject: v18i055: Remote biff/comsat
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: koreth@ssyx.UCSC.EDU (Steven Grimm)
- Posting-number: Volume 18, Issue 55
- Archive-name: biff-comsat
-
- This is a replacement for the standard BSD "biff" and "comsat" programs,
- which notify users when new mail arrives. The old programs were very
- limited; they would only notify a user if mail arrived in his mailbox on
- the local host. This version allows monitoring of other users' mailboxes,
- even on remote hosts. They run under inetd.
-
- ---
- These are my opinions, which you can probably ignore if you want to.
- Steven Grimm Moderator, comp.{sources,binaries}.atari.st
- koreth@ssyx.ucsc.edu uunet!ucbvax!ucscc!ssyx!koreth
-
- #!/bin/sh
- # shar: Shell Archiver (v1.22)
- #
- # Run the following text with /bin/sh to create:
- # BUGS
- # Makefile
- # README
- # biff.1
- # biff.c
- # comsat.8c
- # comsat.c
- # config.h
- #
- sed 's/^X//' << 'SHAR_EOF' > BUGS &&
- XBUGS REMAINING/ADDED:
- X---------------------
- Xusernames are limited to 64 characters.
- X
- Xif a user logs off then back on in between two remote rollcalls, he will
- Xstill get new mail notices requested during his previous login. this may
- Xbe a feature.
- X
- Xcomsat should be split into multiple source files.
- X
- Xon extremely active systems, roll may never be called.
- X
- Xgack() should time out.
- X
- Xgetservbyname() appears to be really slow, at least on Suns. Yellow Pages
- Xshould be outlawed. YP is also responsible for the fact that biff and rbiff
- Xneed to have different names.
- X
- Xin biff.c, addusers() and delusers() should be combined.
- X
- X
- XBUGS FIXED FROM OLD VERSION:
- X----------------------------
- X2-line message bodies are handled correctly; the old comsat would print
- X"...more..." even if there wasn't anything more.
- X
- Xblank lines in the message body aren't shown.
- X
- Xsince biff is now setuid-root, Sunview users can turn biff on and off in
- Xspecific windows.
- SHAR_EOF
- chmod 0600 BUGS || echo "restore of BUGS fails"
- sed 's/^X//' << 'SHAR_EOF' > Makefile &&
- XCFLAGS = -O
- XBINDIR = /usr/ucb # this is where biff gets installed
- XETCDIR = /usr/etc # this is comsat's directory.
- XMANDIR = /usr/man
- X
- Xall: comsat biff
- X
- Xcomsat: comsat.c config.h
- X $(CC) $(CFLAGS) comsat.c -o $@
- X
- X# Biff has to be setuid root, because it has to grab a secure internet port.
- X# There are no calls to gets() or insecure strcpy() or bcopy() calls.
- Xbiff: biff.c config.h
- X $(CC) $(CFLAGS) biff.c -o $@
- X
- Xinstallprogs: all
- X install -m 4755 -o root biff $(BINDIR)
- X install -m 755 -o root comsat $(ETCDIR)
- X
- Xinstallman:
- X install -m 644 -o root biff.1 $(MANDIR)/man1
- X install -m 644 -o root comsat.8c $(MANDIR)/man8
- X
- Xinstall: installprogs installman
- X
- Xclean:
- X /bin/rm -f biff comsat core
- SHAR_EOF
- chmod 0600 Makefile || echo "restore of Makefile fails"
- sed 's/^X//' << 'SHAR_EOF' > README &&
- XThis is a replacement for the standard BSD "biff" and "comsat"
- Xprograms, which notify users when new mail arrives. The old programs
- Xwere very limited; they would only notify a user if mail arrived in his
- Xmailbox on the local host. This version allows monitoring of other
- Xusers' mailboxes, even on remote hosts.
- X
- XTo install the programs, become root and do a make install. That
- Xwill install the manual pages, /usr/ucb/biff, and /usr/etc/comsat.
- XYou will need to edit /etc/services and /etc/servers (or /etc/inetd.conf,
- Xon some systems). Add a TCP biff service to /etc/services:
- X
- Xrbiff 522/tcp
- X
- Xand in servers or inetd.conf, comment out the line that causes
- Xinetd to run comsat. In /etc/servers it looks like
- X
- Xcomsat udp /usr/etc/in.comsat
- X
- Xand in inetd.conf,
- X
- Xcomsat dgram udp wait root /usr/etc/in.comsat in.comsat
- X
- XAdd an entry to /etc/rc.local so that comsat starts at boot time:
- X
- Xif [ -f /usr/etc/comsat ]; then
- X /usr/etc/comsat; (echo -n ' comsat') >/dev/console
- Xfi
- X
- XYou can remove /usr/etc/in.comsat if desired; it will no longer be
- Xcalled. Restart inetd (on some systems, sending it a HUP signal will
- Xcause it to reload its database) and run /usr/etc/comsat. You're
- Xall set!
- X
- XIf you're installing this on your system, drop me a line. I'm interested
- Xto know how widely this catches on. I'm not opposed to replacing biff
- Xwith this for the next version of BSD (or SunOS), but I'd like to be
- Xtold about it first. Send me any suggestions or bug reports, too.
- XEnjoy!
- X
- X---
- XThese are my opinions, which you can probably ignore if you want to.
- XSteven Grimm Moderator, comp.{sources,binaries}.atari.st
- Xkoreth@ssyx.ucsc.edu uunet!ucbvax!ucscc!ssyx!koreth
- SHAR_EOF
- chmod 0600 README || echo "restore of README fails"
- sed 's/^X//' << 'SHAR_EOF' > biff.1 &&
- X.TH BIFF 1 "28 December 1988"
- X.SH NAME
- Xbiff \- watch for incoming mail
- X.SH SYNOPSIS
- X.B biff
- X[ y|n|a|d [ [ user ] [ @host ] ... ] ]
- X.SH DESCRIPTION
- X.B biff
- Xcontrols the mail arrival notification system. With no arguments,
- X.B biff
- Xwill display the notification status for the current terminal. The status
- Xis `y' if notification is allowed, or `n' if it is not.
- X.SH OPTIONS
- X.LP
- XThe first parameter can set the current terminal's notification status. If
- Xit is `y' or `n', notification is enabled or disabled, respectively.
- X.LP
- XBy default, the owner of the tty will only be notified of new mail arriving
- Xin his own mailbox. Additional parameters control monitoring of other
- Xmailboxes, optionally on remote machines. If a username is specified without
- Xa hostname, it is assumed to be a user on the local host; if a hostname is
- Xspecified without a username, the tty owner's name is assumed. To monitor
- Xanother account's mailbox, a user must be on a host listed in the remote host's
- X.B hosts.equiv
- Xfile and have the same account name as the remote account, or be listed in
- Xthe other account's
- X.B \&.rhosts
- Xfile.
- X.LP
- XIf the first parameter is `y' or `a', any additional parameters are added to
- Xthe list of other accounts being monitored. If it is `n' or `d', they are
- Xremoved from the list. The `a' and `d' options allow addition and deletion
- Xof other account names without affecting the terminal's notification state.
- X.SH "SEE ALSO"
- X.BR mail (1),
- X.BR comsat (8C)
- X.SH BUGS
- XThere should be a way of displaying notifications on regular terminals without
- Xdisturbing the user's screen display. On Sun consoles (and possibly others),
- Xusers can select the windows in which they wish to enable notification.
- X.SH AUTHOR
- XSteven Grimm (koreth@ssyx.ucsc.edu)
- SHAR_EOF
- chmod 0600 biff.1 || echo "restore of biff.1 fails"
- sed 's/^X//' << 'SHAR_EOF' > biff.c &&
- X/*
- X** BIFF
- X**
- X** Monitor remote mailboxes.
- X*/
- X
- X#include <stdio.h>
- X#include <netdb.h>
- X#include <ctype.h>
- X#include <strings.h>
- X#include <sys/types.h>
- X#include <netinet/in.h>
- X#include <sys/socket.h>
- X#include <arpa/inet.h>
- X#include <sys/stat.h>
- X#include "config.h"
- X
- Xint tcp_port;
- X
- Xmain(argc, argv)
- Xchar **argv;
- X{
- X char code, *tty, *ttyname();
- X int fd, port;
- X struct sockaddr_in in;
- X struct servent *sent;
- X struct stat st;
- X
- X if (! isatty(1))
- X {
- X fprintf(stderr, "Can't biff to a pipe/socket\n");
- X exit(-1);
- X }
- X
- X tty = ttyname(1);
- X if (stat(tty, &st))
- X {
- X perror("statting tty");
- X exit(-1);
- X }
- X
- X if (argc == 1)
- X {
- X printf("is %c\n", st.st_mode & S_IEXEC ? 'y' : 'n');
- X exit(0);
- X }
- X
- X/* you can't biff when su-ed to another account, unless it's to root */
- X if (geteuid() && geteuid() != st.st_uid)
- X {
- X fprintf(stderr, "%s: Not owner\n", tty);
- X exit(-1);
- X }
- X
- X switch(argv[1][0])
- X {
- X case 'y': case 'Y':
- X st.st_mode |= S_IEXEC;
- X chmod(tty, st.st_mode);
- X case 'a': case 'A':
- X if (argc > 2)
- X addusers(argc-2, argv+2);
- X break;
- X case 'n': case 'N':
- X st.st_mode &= ~S_IEXEC;
- X chmod(tty, st.st_mode);
- X case 'd': case 'D':
- X if (argc > 2)
- X delusers(argc-2, argv+2);
- X break;
- X default:
- X fprintf(stderr,
- X "Usage: %s [y|n|a|d [[user][@machine]...]]\n",
- X argv[0]);
- X exit(-1);
- X }
- X}
- X
- X/*
- X** Sort an argument list by machine name. This makes the load on remote
- X** comsats (not to mention the network) a bit lighter, since we can easily
- X** service all the requests for each host individually.
- X*/
- Xsortargs(num, vec)
- Xint num;
- Xchar **vec;
- X{
- X int mcomp();
- X
- X qsort(vec, num, sizeof(char *), mcomp);
- X}
- X
- X/*
- X** Compare the hostnames of two user@host entries.
- X*/
- Xmcomp(a1, a2)
- Xchar *a1, *a2;
- X{
- X a1 = index(a1, '@');
- X a2 = index(a2, '@');
- X
- X if (a1 == NULL)
- X if (a2 == NULL)
- X return 0;
- X else
- X return -1;
- X if (a2 == NULL)
- X return 1;
- X
- X return(strcmp(a1, a2));
- X}
- X
- Xinit_tcp()
- X{
- X struct servent *sent;
- X
- X if ((sent = getservbyname("rbiff", "tcp")) != NULL)
- X tcp_port = sent->s_port;
- X else
- X tcp_port = htons(TCP_PORT);
- X}
- X
- X/*
- X** Look up a host address, or translate numeric (a.b.c.d) notation to a
- X** valid inet address. Puts the host address in *buf. Returns 0 if
- X** the host wasn't found.
- X*/
- Xhaddr(host, buf)
- Xchar *host;
- Xunsigned long *buf;
- X{
- X struct hostent *hent;
- X
- X if (isdigit(host[0]))
- X {
- X *buf = inet_addr(host);
- X return 1;
- X }
- X hent = gethostbyname(host);
- X if (hent == NULL)
- X return 0;
- X bcopy(hent->h_addr, buf, hent->h_length);
- X return 1;
- X}
- X
- Xint sockfd;
- X
- X/*
- X** Initiate a connection with a host. Returns 0 on failure.
- X*/
- Xgetcon(hostname)
- Xchar *hostname;
- X{
- X char buf[10];
- X struct sockaddr_in in;
- X int port;
- X
- X port = 1023;
- X sockfd = rresvport(&port);
- X if (sockfd < 0)
- X {
- X perror("rresvport");
- X return 0;
- X }
- X
- X bzero(&in, sizeof(in));
- X if (! haddr(hostname, &in.sin_addr.s_addr))
- X {
- X fprintf("%s: Host unknown\n", hostname);
- X return 0;
- X }
- X in.sin_port = tcp_port;
- X in.sin_family = AF_INET;
- X
- X if (connect(sockfd, &in, sizeof(in)))
- X {
- X perror(hostname);
- X return 0;
- X }
- X
- X sprintf(buf, "P%d", tcp_port);
- X write(sockfd, buf, strlen(buf)+1);
- X read(sockfd, buf, 1);
- X return 1;
- X}
- X
- X/*
- X** Extract a hostname from a user@host string. If there's no @host, use
- X** localhost.
- X*/
- Xchar *hname(str)
- Xchar *str;
- X{
- X char *at;
- X
- X at = index(str, '@');
- X if (at == NULL)
- X return("localhost");
- X else
- X return(at+1);
- X}
- X
- Xchar *addcodes[] = {
- X"",
- X"Out of memory",
- X"Already monitoring",
- X"Permission denied"
- X};
- X
- Xaddusers(num, vec)
- Xint num;
- Xchar **vec;
- X{
- X int i, npos;
- X char *curhost, *tmp, buf[130], *getlogin(), code;
- X
- X sortargs(num, vec);
- X curhost = vec[0];
- X i = 0;
- X
- X init_tcp();
- X
- X sprintf(buf, "W%s", getlogin());
- X npos = strlen(buf)+1; /* position to place second name */
- X
- X while(i < num) {
- X if (! getcon(hname(vec[i])))
- X {
- X fprintf(stderr, "Couldn't register %s\n", vec[i]);
- X while (! mcomp(curhost, vec[i]))
- X if (++i == num)
- X break;
- X if (i < num)
- X curhost = vec[i];
- X }
- X else
- X {
- X while (! mcomp(curhost, vec[i]))
- X {
- X if (vec[i][0] == '@')
- X strcpy(buf+npos, getlogin());
- X else
- X {
- X strncpy(buf+npos, vec[i], 64);
- X buf[npos+64] = '\0';
- X if ((tmp=index(buf+npos,'@')) != NULL)
- X *tmp = '\0';
- X }
- X write(sockfd, buf, npos+strlen(buf+npos)+1);
- X read(sockfd, &code, 1);
- X if (code)
- X fprintf(stderr, "%s: %s\n", vec[i],
- X addcodes[code]);
- X if (++i == num)
- X break;
- X }
- X if (i < num)
- X curhost = vec[i];
- X close(sockfd);
- X }
- X }
- X}
- X
- X/*
- X** Delete users from watch lists. This looks mostly identical to
- X** addusers(), but is just different enough to warrant its own
- X** routine.
- X*/
- Xdelusers(num, vec)
- Xint num;
- Xchar **vec;
- X{
- X int i, npos;
- X char *curhost, *tmp, buf[130], *getlogin(), code;
- X
- X sortargs(num, vec);
- X curhost = vec[0];
- X i = 0;
- X
- X init_tcp();
- X
- X sprintf(buf, "D%s", getlogin());
- X npos = strlen(buf)+1; /* position to place second name */
- X
- X while(i < num) {
- X if (! getcon(hname(vec[i])))
- X {
- X fprintf(stderr, "Couldn't delete %s\n", vec[i]);
- X while (! mcomp(curhost, vec[i]))
- X if (++i == num)
- X break;
- X if (i < num)
- X curhost = vec[i];
- X }
- X else
- X {
- X while (! mcomp(curhost, vec[i]))
- X {
- X if (vec[i][0] == '@')
- X buf[npos] = '\0';
- X else
- X {
- X strncpy(buf+npos, vec[i], 64);
- X buf[npos+64] = '\0';
- X if ((tmp=index(buf+npos,'@')) != NULL)
- X *tmp = '\0';
- X }
- X write(sockfd, buf, npos+strlen(buf+npos)+1);
- X read(sockfd, &code, 1);
- X if (!code)
- X printf("error deleting %s\n", vec[i]);
- X if (++i == num)
- X break;
- X }
- X if (i < num)
- X curhost = vec[i];
- X close(sockfd);
- X }
- X }
- X}
- X
- SHAR_EOF
- chmod 0600 biff.c || echo "restore of biff.c fails"
- sed 's/^X//' << 'SHAR_EOF' > comsat.8c &&
- X.TH COMSAT 8C "28 December 1988"
- X.SH NAME
- Xcomsat \- mail notification daemon
- X.SH SYNOPSIS
- X/usr/etc/comsat
- X.SH DESCRIPTION
- X.B comsat
- Xwatches for datagrams on the UDP "biff" service port (usually 512) for messages
- Xof the form
- X.IR user @ mailbox-offset
- Xwhere
- X.I user
- Xis a valid username on the local host and
- X.I mailbox-offset
- Xis the byte offset into that user's mailbox of a new mail message. It then
- Xdisplays some of the header lines and up to two lines of the message body to
- Xeveryone who is monitoring the mailbox in question (see below).
- X.LP
- XIf the owner of the mailbox is logged in and the owner-execute bit of his tty
- Xis set, he is automatically notified. The
- X.BR biff (1)
- Xcommand can be used to set that bit easily, as well as to register requests
- Xto monitor other mailboxes.
- X.LP
- X.B comsat
- Xalso listens for stream connections on the TCP "biff" service port (522 by
- Xdefault). Connections to that port must come from secure port numbers
- X(numbered less than 1024) or they will be closed immediately. Once the
- Xconnection has been established, the remote program (usually a
- X.B comsat
- Xon another host, or
- X.BR biff )
- Xcan issue commands to
- X.BR comsat :
- Xadd a monitor, delete a monitor, verify that a user is logged on, or send
- Xa mail excerpt to a user on the local host.
- X.LP
- XA user must meet the authentication requirements enforced by
- X.BR ruserok (3)
- Xin order to monitor a mailbox other than his own.
- X.LP
- XWhen
- X.B comsat
- Xreceives a
- X.IR user @ offset
- Xdatagram, it looks through its list of monitors. If anyone is monitoring the
- X.IR user 's
- Xmailbox,
- X.B comsat
- Xeither notifies him directly (if he is monitoring from the local host, is
- Xlogged on, and his tty has the owner-execute bit set) or initiates a
- Xconnection to the
- X.B comsat
- Xrunning on his host, and sends the mail excerpt to that host, which
- Xnotifies the monitor if he has his owner-execute bit set.
- X.LP
- X.B comsat
- Xwakes up on a regular basis and verifies that all the users monitoring
- Xmailboxes are still logged onto their respective hosts. It deletes the
- Xabsent monitors from its monitor list.
- X.SH "SEE ALSO"
- X.BR biff (1),
- X.BR rcmd (3)
- X.SH BUGS
- XIf a user logs off then on in between two login checks, he will still
- Xbe notified of mail arriving at mailboxes monitored during his first
- Xlogin session. This may be a feature.
- X.LP
- XOn extremely active systems, the login checks may never occur; a minimum
- Xnumber of seconds (120 in the default distribution, though this is
- Xconfigurable at each site) must pass since the last datagram or stream
- Xconnection before the check occurs.
- X.SH AUTHOR
- XSteven Grimm (koreth@ssyx.ucsc.edu)
- X
- SHAR_EOF
- chmod 0600 comsat.8c || echo "restore of comsat.8c fails"
- sed 's/^X//' << 'SHAR_EOF' > comsat.c &&
- X/*
- X** COMSAT
- X**
- X** A network version of the BSD biff server.
- X**
- X** Written by Steven Grimm (koreth@ssyx.ucsc.edu), 12-27-88.
- X*/
- X#include <sys/types.h>
- X#include <utmp.h>
- X#include <stdio.h>
- X#include <netdb.h>
- X#include <ctype.h>
- X#include <strings.h>
- X#include <signal.h>
- X#include <sys/file.h>
- X#include <sys/stat.h>
- X#include <sys/time.h>
- X#include <sys/ioctl.h>
- X#include <sys/socket.h>
- X#include <netinet/in.h>
- X#include <arpa/inet.h>
- X#ifndef MAXPATHLEN
- X#include <sys/param.h>
- X#endif
- X#include "config.h"
- X
- X#ifndef FD_SETSIZE /* for 4.2BSD */
- X#define FD_SETSIZE (sizeof(fd_set) * 8)
- X#define FD_SET(n, p) (((fd_set *) (p))->fds_bits[0] |= (1 << ((n) % 32)))
- X#define FD_CLR(n, p) (((fd_set *) (p))->fds_bits[0] &= ~(1 << ((n) % 32)))
- X#define FD_ISSET(n, p) (((fd_set *) (p))->fds_bits[0] & (1 << ((n) % 32)))
- X#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
- X#endif
- X
- Xextern char *sys_errlist[];
- Xextern int errno;
- X
- Xint udp_fd, tcp_fd;
- Xstruct sockaddr_in tcp_host[FD_SETSIZE];
- Xfd_set readset;
- X
- X/*
- X** A linked list of these structures is used to keep track of remote users
- X** who are watching mailboxes on the local host.
- X*/
- Xstruct ruser {
- X struct sockaddr_in host; /* watcher's host */
- X char watcher[64]; /* watcher's name */
- X char watchee[64]; /* watchee's name */
- X int local; /* set if host==localhost */
- X int flag; /* flag for temporary use */
- X struct ruser *next; /* link to next entry */
- X};
- X
- Xstruct ruser *rlist = NULL;
- X
- X/*
- X** Add a remote user to the watch list. Returns 0 on success, 1 on
- X** malloc failure, 2 if that user is repeating an existing request,
- X** or 3 if the remote user authentication fails.
- X*/
- Xr_add(host, watcher, watchee)
- Xstruct sockaddr_in *host;
- Xchar *watcher, *watchee;
- X{
- X struct ruser *entry;
- X struct hostent *hostent;
- X int ruok;
- X char hbuf[64];
- X
- X for (entry = rlist; entry != NULL; entry = entry->next)
- X if ((!strcmp(watcher, entry->watcher)) &&
- X (!strcmp(watchee, entry->watchee)))
- X return 2;
- X
- X if ((hostent = gethostbyaddr(&host->sin_addr, sizeof(host->sin_addr),
- X AF_INET)) == NULL)
- X strcpy(hbuf, inet_ntoa(host->sin_addr));
- X else
- X strncpy(hbuf, hostent->h_name, 63);
- X hbuf[63] = '\0';
- X if (! host->sin_addr.s_addr)
- X strcpy(hbuf, "localhost");
- X
- X ruok = ruserok(hbuf, 0, watcher, watchee);
- X setreuid(0,0); /* make up for some Sun bugs */
- X if (ruok)
- X return 3;
- X
- X entry = (struct ruser *)malloc(sizeof(struct ruser));
- X if (entry == NULL)
- X return 1;
- X
- X entry->next = rlist;
- X rlist = entry;
- X strncpy(entry->watcher, watcher, 64);
- X strncpy(entry->watchee, watchee, 64);
- X bcopy(host, &entry->host, sizeof(struct sockaddr_in));
- X entry->local = localhost(host);
- X
- X return 0;
- X}
- X
- X/*
- X** Compare a host-username pair with the remote host and username in a
- X** watch list entry. Returns 0 if they're the same. Watche[er] may be empty
- X** in which case it's ignored; host is always significant.
- X*/
- Xr_cmp(host, watcher, watchee, entry)
- Xstruct sockaddr_in *host;
- Xchar *watcher, *watchee;
- Xstruct ruser *entry;
- X{
- X if (watcher[0] && strcmp(watcher, entry->watcher))
- X return 1;
- X if (watchee[0] && strcmp(watchee, entry->watchee))
- X return 1;
- X/* we have to check for localhost, since watchees registered without */
- X/* machine names will be watched by 'localhost', and checking for */
- X/* user@realmachinename will fail unless we do the special check. */
- X if (entry->local)
- X {
- X if (!localhost(host))
- X return 1;
- X }
- X else if (bcmp(&host->sin_addr, &entry->host.sin_addr,
- X sizeof(host->sin_addr)))
- X return 1;
- X return 0;
- X}
- X
- X/*
- X** Delete a remote user from the watch list.
- X*/
- Xr_del(host, watcher, watchee)
- Xstruct sockaddr_in *host;
- Xchar *watcher, *watchee;
- X{
- X struct ruser *cur, *tmp;
- X int delflg = 0;
- X
- X#ifdef DEBUG
- X printf("r_del(%s, %s, %s)\n", inet_ntoa(host->sin_addr.s_addr),
- X watcher, watchee);
- X#endif
- X
- X while (rlist != NULL)
- X if (! r_cmp(host, watcher, watchee, rlist))
- X {
- X tmp = rlist;
- X rlist = rlist->next;
- X free(tmp);
- X delflg = 1;
- X }
- X else
- X break;
- X
- X if (rlist == NULL)
- X return delflg;
- X
- X cur = rlist;
- X
- X while (cur->next != NULL)
- X if (! r_cmp(host, watcher, watchee, rlist))
- X {
- X tmp = cur->next;
- X cur->next = cur->next->next;
- X free(tmp);
- X delflg = 1;
- X }
- X else
- X cur = cur->next;
- X
- X return delflg;
- X}
- X
- X/*
- X** Open and bind the necessary sockets. One socket listens for datagrams,
- X** the other for stream connection requests.
- X*/
- Xvoid
- Xgetsocks()
- X{
- X struct servent *tcpsvc, *udpsvc;
- X struct sockaddr_in addr;
- X
- X tcpsvc = getservbyname("rbiff", "tcp");
- X
- X tcp_fd = socket(PF_INET, SOCK_STREAM, 0);
- X if (tcp_fd < 0)
- X panic("tcp socket()");
- X
- X udp_fd = socket(PF_INET, SOCK_DGRAM, 0);
- X if (udp_fd < 0)
- X panic("udp socket()");
- X
- X bzero(&addr, sizeof(addr));
- X addr.sin_family = AF_INET;
- X addr.sin_addr.s_addr = INADDR_ANY;
- X
- X if (tcpsvc == NULL)
- X addr.sin_port = htons(TCP_PORT);
- X else
- X addr.sin_port = tcpsvc->s_port;
- X if (bind(tcp_fd, &addr, sizeof(addr)) < 0)
- X panic("tcp bind()");
- X
- X udpsvc = getservbyname("biff", "udp");
- X
- X if (udpsvc == NULL)
- X addr.sin_port = htons(UDP_PORT);
- X else
- X addr.sin_port = udpsvc->s_port;
- X if (bind(udp_fd, &addr, sizeof(addr)) < 0)
- X panic("udp bind()");
- X
- X if (listen(tcp_fd, 5) < 0)
- X panic("listen (weird)");
- X
- X if (fcntl(tcp_fd, F_SETFL, FIONBIO) == -1)
- X panic("fcntl tcp_fd");
- X if (fcntl(udp_fd, F_SETFL, FIONBIO) == -1)
- X panic("fcntl udp_fd");
- X
- X FD_ZERO(&readset);
- X FD_SET(tcp_fd, &readset);
- X FD_SET(udp_fd, &readset);
- X}
- X
- X/*
- X** Send a message to a user, once to each of his ttys that has the owner
- X** execute bit set.
- X*/
- Xvoid
- Xbiff(user, msg)
- Xchar *user, *msg;
- X{
- X char filename[MAXPATHLEN];
- X struct utmp uent;
- X struct stat st;
- X FILE *fp;
- X int tty;
- X
- X if ((fp = fopen(UTMP, "r")) == NULL)
- X return;
- X
- X while (! feof(fp))
- X {
- X if (! fread(&uent, sizeof(uent), 1, fp))
- X break;
- X if (strcmp(uent.ut_name, user))
- X continue;
- X sprintf(filename, "%s/%s", DEVDIR, uent.ut_line);
- X if (stat(filename, &st) < 0)
- X continue;
- X if ((st.st_mode & S_IEXEC)
- X#ifndef DEBUG
- X && !fork()
- X#endif
- X )
- X {
- X tty = open(filename, O_RDWR);
- X if (tty)
- X write(tty, msg, strlen(msg));
- X#ifdef DEBUG
- X close(tty);
- X#else
- X exit(0);
- X#endif
- X }
- X }
- X fclose(fp);
- X}
- X
- X/*
- X** Is a header line valid? This routine finds out. Returns 0 if the
- X** line shouldn't be included in the biff message.
- X*/
- Xhdrline(buf)
- Xchar *buf;
- X{
- X int i;
- X
- X for (i = 0; i < Nfields; i++)
- X if (!strncmp(buf, fields[i], strlen(fields[i])))
- X return 1;
- X return 0;
- X}
- X
- X/*
- X** Build a message to send out to everyone who's waiting for mail
- X** to a specific user. Pass the username, offset in his mailbox of
- X** the new mail, and the address of a buffer at least 800 bytes long.
- X*/
- Xvoid
- Xbuildmsg(name, offset, buf)
- Xchar *name, *buf;
- Xint offset;
- X{
- X struct ruser *cur;
- X struct stat st;
- X struct timeval tvp[2];
- X FILE *fp;
- X int bpos, body;
- X char filename[MAXPATHLEN];
- X
- X gethostname(filename, 79);
- X filename[79] = '\0';
- X
- X sprintf(buf, "\n\r\007New mail for %.20s@%.30s\007 has arrived:\n\r----\n\r",
- X name, filename);
- X bpos = strlen(buf);
- X
- X sprintf(filename, "%s/%s", MAILDIR, name);
- X
- X stat(filename, &st);
- X if ((fp = fopen(filename, "r")) == NULL)
- X {
- X sprintf(buf+bpos, "(%s: %s)\n\r----\n\r", filename,
- X sys_errlist[errno]);
- X return;
- X }
- X
- X body = 0; /* we are looking at the message header. */
- X
- X fseek(fp, offset, 0);
- X
- X while (! feof(fp))
- X {
- X if (fgets(buf+bpos, 780-bpos, fp) == NULL)
- X {
- X strcpy(buf+bpos, "----\n\r");
- X break;
- X }
- X if (! body)
- X {
- X if (buf[bpos] == '\n')
- X {
- X bpos++;
- X body++;
- X }
- X else if (hdrline(buf+bpos))
- X {
- X bpos += strlen(buf+bpos);
- X buf[bpos++] = '\r';
- X }
- X }
- X else
- X {
- X if (buf[bpos] == '\n')
- X continue;
- X if (++body == 4)
- X {
- X strcpy(buf+bpos, "...more...\n\r");
- X break;
- X }
- X bpos += strlen(buf+bpos);
- X buf[bpos++] = '\r';
- X }
- X }
- X fclose(fp);
- X
- X/* login's "you have new mail" message won't work unless mtime > atime */
- X bzero(tvp, sizeof(tvp));
- X tvp[0].tv_sec = st.st_atime;
- X tvp[1].tv_sec = st.st_mtime;
- X utimes(filename, tvp);
- X}
- X
- X/*
- X** Determine whether or not a host address points to the local host. Returns
- X** 0 if the host isn't local. There's probably a much better way of doing
- X** this: first we see if the host is called "localhost", then see if its
- X** name is the same one that gethostname() returns, then see if gethostname()
- X** returns a machine name whose address is equal to the host address we're
- X** checking. Yucko.
- X*/
- Xlocalhost(addr)
- Xstruct sockaddr_in *addr;
- X{
- X struct hostent *host;
- X char hname[64];
- X int i;
- X
- X if (! addr->sin_addr.s_addr)
- X return 1;
- X
- X gethostname(hname, 64);
- X
- X if ((host = gethostbyaddr(&addr->sin_addr, sizeof(addr->sin_addr),
- X AF_INET)) != NULL)
- X {
- X if (! stricmp(host->h_name, "localhost"))
- X return 1;
- X if (! stricmp(host->h_name, hname))
- X return 1;
- X }
- X
- X if ((host = gethostbyname(hname)) != NULL)
- X#ifndef h_addr
- X if (!bcmp(host->h_addr,&addr->sin_addr,host->h_length))
- X return 1;
- X#else /* if we have multiple host addresses, check all of 'em */
- X for (i = 0; host->h_addr_list[i] != NULL; i++)
- X if (! bcmp(host->h_addr_list[i], &addr->sin_addr,
- X sizeof(addr->sin_addr)))
- X return 1;
- X#endif
- X return 0;
- X}
- X
- X/*
- X** See if a user is logged in. Return 1 if he is.
- X*/
- Xlogged_in(user)
- Xchar *user;
- X{
- X struct utmp uent;
- X int sawhim = 0;
- X FILE *fp;
- X
- X fp = fopen(UTMP, "r");
- X if (fp == NULL)
- X return 0;
- X
- X while (! feof(fp))
- X {
- X if (! fread(&uent, sizeof(uent), 1, fp))
- X break;
- X if (strcmp(uent.ut_name, user))
- X continue;
- X sawhim++;
- X break;
- X }
- X fclose(fp);
- X
- X return(sawhim);
- X}
- X
- X/*
- X** Open up a dialogue with a remote comsat, and send it a biff for someone.
- X*/
- Xvoid
- Xrbiff(cur, buf)
- Xstruct ruser *cur;
- Xchar *buf;
- X{
- X char biffcmd[90];
- X int fd, port, len;
- X
- X sprintf(biffcmd, "B%s%c%d", cur->watcher, '\0', strlen(buf) + 1);
- X
- X/* connect to the remote, as specified in the rlist entry. make sure we */
- X/* are coming from a secure port else the other guy will hang up on us. */
- X port = 1023;
- X fd = rresvport(&port);
- X if (fd < 0)
- X return;
- X if (connect(fd, &cur->host, sizeof(cur->host)))
- X {
- X close(fd);
- X return;
- X }
- X
- X len = strlen(biffcmd);
- X len += strlen(biffcmd+len+1)+2;
- X write(fd, biffcmd, len);
- X
- X if (len = gack(fd))
- X {
- X shutdown(fd, 2);
- X close(fd);
- X return;
- X }
- X
- X write(fd, buf, strlen(buf)+1);
- X
- X gack(fd);
- X
- X shutdown(fd, 2);
- X close(fd);
- X}
- X
- X/*
- X** Handle an incoming datagram. This should be a message of the form
- X** "user@offset", where user is the user who has new mail and offset
- X** is the byte offset into his mailbox of the new message. If SECUREBIFF
- X** is defined in config.h, the datagram must come from a reserved (<1024)
- X** port on the local host.
- X*/
- Xvoid
- Xudp()
- X{
- X struct ruser *cur;
- X struct sockaddr_in from;
- X char buf[800], user[64], *cp;
- X int offset;
- X
- X offset = sizeof(from);
- X if ((offset = recvfrom(udp_fd, buf, 800, 0, &from, &offset)) < 3)
- X return;
- X buf[offset] = '\0';
- X
- X#ifdef SECUREBIFF
- X if (ntohs(from.sin_port) >= IPPORT_RESERVED)
- X return;
- X if (! localhost(&from))
- X return;
- X#endif
- X
- X if ((cp = index(buf, '@')) == NULL)
- X return;
- X *cp++ = '\0';
- X
- X strncpy(user, buf, 63);
- X user[63] = '\0';
- X
- X if (! isdigit(*cp))
- X return;
- X offset = atoi(cp);
- X
- X buildmsg(user, offset, buf);
- X biff(user, buf);
- X
- X for (cur = rlist; cur != NULL; cur = cur->next)
- X if (! strcmp(cur->watchee, user))
- X/* if the watcher is on the local host, just biff him. */
- X if (cur->local)
- X {
- X/* if the watcher is watching himself, don't biff him again. */
- X if (strcmp(user, cur->watcher))
- X biff(cur->watcher, buf);
- X }
- X else
- X rbiff(cur, buf);
- X}
- X
- X/*
- X** Handle a pending TCP connection. If it's not coming from a secure port
- X** on the remote host, close it immediately.
- X*/
- Xvoid
- Xtcp()
- X{
- X struct sockaddr_in host;
- X int len, tcp_fd_c;
- X
- X len = sizeof(host);
- X tcp_fd_c = accept(tcp_fd, &host, &len);
- X if (tcp_fd_c < 0)
- X return;
- X
- X if (ntohs(host.sin_port) >= IPPORT_RESERVED)
- X {
- X shutdown(tcp_fd_c, 2);
- X close(tcp_fd_c);
- X return;
- X }
- X
- X len = 1;
- X setsockopt(tcp_fd_c, SOL_SOCKET, SO_KEEPALIVE, &len, sizeof(len));
- X fcntl(tcp_fd_c, F_SETFL, FIONBIO);
- X
- X bcopy(&host, &tcp_host[tcp_fd_c], sizeof(host));
- X FD_SET(tcp_fd_c, &readset);
- X}
- X
- X/*
- X** Handle incoming data on a connected socket. This should be in the form
- X** of a single-character command byte followed by null-terminated parameters.
- X** There probably isn't enough error checking here.
- X*/
- Xvoid
- Xtcpc(fd)
- Xint fd;
- X{
- X char buf[800], user[64];
- X int nbytes;
- X
- X ioctl(fd, FIONREAD, &nbytes);
- X if (! nbytes) /* 0 bytes available means disconnect */
- X {
- X shutdown(fd, 2);
- X close(fd);
- X FD_CLR(fd, &readset);
- X return;
- X }
- X
- X if (nbytes > sizeof(buf))
- X nbytes = sizeof(buf);
- X
- X read(fd, buf, nbytes);
- X buf[nbytes] = '\0';
- X
- X if (buf[0] == 'W') /* Watch watcher\0watchee\0 */
- X ack(fd, r_add(&tcp_host[fd], buf+1, buf+strlen(buf) + 1));
- X else if (buf[0] == 'D') /* Delete watcher\0[watchee]\0 */
- X ack(fd, r_del(&tcp_host[fd], buf+1, buf+strlen(buf) + 1));
- X else if (buf[0] == 'V') /* Verify watcher\0 */
- X ack(fd, logged_in(buf+1));
- X else if (buf[0] == 'P') /* Port portnum\0 */
- X {
- X tcp_host[fd].sin_port = htons(atoi(buf+1));
- X ack(fd, 0);
- X }
- X else if (buf[0] == 'B') /* Biff watcher\0length\0 */
- X {
- X char user[64];
- X int len, nread;
- X
- X strcpy(user, buf+1);
- X len = atoi(buf+strlen(buf) + 1);
- X if (len > sizeof(buf)) /* don't wanna overflow our buffer */
- X {
- X ack(fd, 1);
- X return;
- X }
- X
- X ack(fd, 0); /* tell him to start sending */
- X
- X nbytes = 0;
- X while (nbytes < len)
- X {
- X fd_set readbits;
- X struct timeval tout;
- X
- X FD_ZERO(&readbits);
- X FD_SET(fd, &readbits);
- X timerclear(&tout);
- X tout.tv_sec = 20; /* biff msg must arrive fast */
- X
- X select(fd+1, &readbits, NULL, NULL, &tout);
- X nread = read(fd, buf+nbytes, len-nbytes);
- X if (nread < 1) /* if timed out, we get EWOULDBLOCK */
- X {
- X close(fd);
- X FD_CLR(fd, &readset);
- X return;
- X }
- X nbytes += nread;
- X }
- X biff(user, buf);
- X ack(fd, 0);
- X }
- X}
- X
- X/*
- X** Send a byte out on a file descriptor.
- X*/
- Xack(fd, ch)
- Xint fd, ch;
- X{
- X char c;
- X
- X c = (char) ch;
- X write(fd, &c, 1);
- X}
- X
- X/*
- X** Read a byte from a file descriptor.
- X*/
- Xgack(fd)
- X{
- X char c;
- X
- X read(fd, &c, 1);
- X return (int) c;
- X}
- X
- X/*
- X** Do a perror() and exit.
- X*/
- Xpanic(string)
- Xchar *string;
- X{
- X perror(string);
- X exit(-1);
- X}
- X
- X/*
- X** A custom bcopy whose behavior is known for overlapping regions
- X*/
- Xbcopy(src, des, len)
- Xregister char *src, *des;
- Xregister int len;
- X{
- X while (len--)
- X *des++ = *src++;
- X}
- X
- X/*
- X** Case-insensitive compare. This should use lookup tables for fast case
- X** conversion.
- X*/
- Xstricmp(str1, str2)
- Xregister char *str1, *str2;
- X{
- X while (*str1 || *str2)
- X {
- X if ((islower(*str1) ? toupper(*str1) : *str1) !=
- X (islower(*str2) ? toupper(*str2) : *str2))
- X return 1;
- X str1++;
- X str2++;
- X }
- X return 0;
- X}
- X
- X/*
- X** See which watchers are still online, and delete the entries of those
- X** who have logged off. This probably scans through the list a lot more
- X** times than is necessary. Oh well.
- X*/
- Xvoid
- Xrollcall()
- X{
- X struct ruser *cur, *user, *place;
- X int port, fd, uflag;
- X char buf[66], code;
- X
- X/* if there are no watchers, this is pretty easy. */
- X
- X if (rlist == NULL)
- X return;
- X
- X/* first, see who's logged on locally. while we're going through the linked */
- X/* list, set all the remote-host entries' flag fields to 0 and all the local */
- X/* ones to 1, to simplify things later. */
- X
- X for (cur = rlist; cur != NULL; cur = cur->next)
- X if (cur->local)
- X if (logged_in(cur->watcher))
- X cur->flag = 1;
- X else
- X cur->flag = -1;
- X else
- X cur->flag = 0;
- X
- X/* now go through and look at the remote hosts. each time we find a remote */
- X/* host, we will connect to it then scan forward in the linked list so that */
- X/* we only have to connect to each host once. */
- X
- X buf[0] = 'V';
- X
- X for (place = rlist; place != NULL; place = place->next)
- X {
- X if (place->flag)
- X continue;
- X
- X port = 1023;
- X fd = rresvport(&port);
- X if (fd < 0)
- X break;
- X
- X/* if we can't connect to a host, toast all entries from that host. */
- X
- X if (connect(fd, &place->host, sizeof(place->host)))
- X {
- X for (cur = place; cur != NULL; cur = cur->next)
- X if (! cur->flag)
- X if (! r_cmp(&place->host, "", "", cur))
- X cur->flag = -1;
- X }
- X
- X/* ask about each watcher on the current host. */
- X
- X else for (user = place; user != NULL; user = user->next)
- X {
- X if (user->flag)
- X continue;
- X if (r_cmp(&place->host, "", "", user))
- X continue;
- X strcpy(buf+1, user->watcher);
- X write(fd, buf, strlen(buf)+1);
- X read(fd, &code, 1);
- X uflag = code ? 1 : -1;
- X
- X/* toast or keep all the watcher's entries. */
- X
- X for (cur = user; cur != NULL; cur = cur->next)
- X if (!(cur->flag || r_cmp(&user->host,
- X user->watcher, "", cur)))
- X cur->flag = uflag;
- X }
- X close(fd);
- X }
- X
- X/* now go through and delete all the entries that are marked as toast. */
- X
- X while (rlist != NULL)
- X if (rlist->flag == -1)
- X {
- X cur = rlist;
- X rlist = rlist->next;
- X free(cur);
- X }
- X else
- X break;
- X
- X if (rlist == NULL)
- X return;
- X
- X cur = rlist;
- X while (cur->next != NULL)
- X if (cur->next->flag == -1)
- X {
- X place = cur->next;
- X cur->next = cur->next->next;
- X free(place);
- X }
- X else
- X cur = cur->next;
- X}
- X
- X#ifdef DEBUG
- X/*
- X** Dump the ruser list to stdout.
- X*/
- Xdumplist()
- X{
- X struct ruser *cur;
- X
- X if (rlist == NULL)
- X printf("No users in rlist\n");
- X else
- X for (cur = rlist; cur != NULL; cur = cur->next)
- X printf("%s@%s watching %s\n", cur->watcher,
- X inet_ntoa(cur->host.sin_addr.s_addr),
- X cur->watchee);
- X}
- X#endif
- X
- Xreap()
- X{
- X wait(0);
- X}
- X
- Xmain()
- X{
- X int fd;
- X fd_set myread, other;
- X struct timeval timer;
- X
- X getsocks();
- X
- X setreuid(0, 0);
- X#ifndef DEBUG
- X if (fork())
- X exit(0);
- X fd = open("/dev/tty", O_RDWR);
- X ioctl(fd, TIOCNOTTY, 0);
- X close(fd);
- X close(0);
- X close(1);
- X close(2);
- X#else
- X signal(SIGQUIT, dumplist);
- X#endif
- X
- X signal(SIGCHLD, reap);
- X
- X FD_ZERO(&other);
- X
- X while (1)
- X {
- X timerclear(&timer);
- X timer.tv_sec = ROLLCALL;
- X bcopy(&readset, &myread, sizeof(fd_set));
- X
- X if (! select(FD_SETSIZE, &myread, &other, &other, &timer))
- X rollcall();
- X else
- X {
- X if (FD_ISSET(udp_fd, &myread))
- X {
- X udp();
- X FD_CLR(udp_fd, &myread);
- X }
- X if (FD_ISSET(tcp_fd, &myread))
- X {
- X tcp();
- X FD_CLR(tcp_fd, &myread);
- X }
- X/* we have to do this in a loop; ffs() won't work for FD_SETSIZE > 32 */
- X for (fd = 0; fd < FD_SETSIZE; fd++)
- X if (FD_ISSET(fd, &myread))
- X tcpc(fd);
- X }
- X }
- X}
- SHAR_EOF
- chmod 0600 comsat.c || echo "restore of comsat.c fails"
- sed 's/^X//' << 'SHAR_EOF' > config.h &&
- X/*
- X** Various pieces of configuration for comsat
- X*/
- X
- X/*
- X** If SECUREBIFF is defined, comsat will only accept datagrams from privileged
- X** (<1024) port numbers. This is to prevent users from sending "biff bombs"
- X** to each other by sending messages to the server. Note that /bin/mail may
- X** have to be modified for this to work, as it does not normally use a secure
- X** port to talk to comsat.
- X*/
- X/*#define SECUREBIFF*/
- X
- X/*
- X** Set ROLLCALL to the number of seconds between "roll calls" -- a roll call
- X** checks to see that all the remote users who are watching someone are still
- X** logged in.
- X*/
- X#define ROLLCALL 120
- X
- X/*
- X** These are the default port numbers to use, if we can't find the biff
- X** entry in /etc/services.
- X*/
- X#define UDP_PORT 512
- X#define TCP_PORT 522
- X
- X/*
- X** Mail header fields we print in a mail notification.
- X*/
- Xchar *fields[] = {
- X"From: ",
- X"Subject: ",
- X"To: ",
- X"Date: "
- X};
- X
- X/*
- X** Directory where users' mailboxes are kept.
- X*/
- X#define MAILDIR "/usr/spool/mail"
- X
- X/*
- X** Location of utmp file.
- X*/
- X#define UTMP "/etc/utmp"
- X
- X/*
- X** Directory to search for tty devices.
- X*/
- X#define DEVDIR "/dev"
- X
- X#define Nfields (sizeof(fields)/sizeof(fields[0]))
- SHAR_EOF
- chmod 0600 config.h || echo "restore of config.h fails"
- exit 0
-
- --
- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
-
-
-