home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume18 / biff-comsat < prev    next >
Internet Message Format  |  1989-11-09  |  36KB

  1. From: rsalz@uunet.uu.net (Rich Salz)
  2. Newsgroups: comp.sources.unix
  3. Subject: v18i055:  Remote biff/comsat
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: koreth@ssyx.UCSC.EDU (Steven Grimm)
  7. Posting-number: Volume 18, Issue 55
  8. Archive-name: biff-comsat
  9.  
  10. This is a replacement for the standard BSD "biff" and "comsat" programs,
  11. which notify users when new mail arrives.  The old programs were very
  12. limited; they would only notify a user if mail arrived in his mailbox on
  13. the local host.  This version allows monitoring of other users' mailboxes,
  14. even on remote hosts.  They run under inetd.
  15.  
  16. ---
  17. These are my opinions, which you can probably ignore if you want to.
  18. Steven Grimm        Moderator, comp.{sources,binaries}.atari.st
  19. koreth@ssyx.ucsc.edu    uunet!ucbvax!ucscc!ssyx!koreth
  20.  
  21. #!/bin/sh
  22. # shar:    Shell Archiver  (v1.22)
  23. #
  24. #    Run the following text with /bin/sh to create:
  25. #      BUGS
  26. #      Makefile
  27. #      README
  28. #      biff.1
  29. #      biff.c
  30. #      comsat.8c
  31. #      comsat.c
  32. #      config.h
  33. #
  34. sed 's/^X//' << 'SHAR_EOF' > BUGS &&
  35. XBUGS REMAINING/ADDED:
  36. X---------------------
  37. Xusernames are limited to 64 characters.
  38. X
  39. Xif a user logs off then back on in between two remote rollcalls, he will
  40. Xstill get new mail notices requested during his previous login.  this may
  41. Xbe a feature.
  42. X
  43. Xcomsat should be split into multiple source files.
  44. X
  45. Xon extremely active systems, roll may never be called.
  46. X
  47. Xgack() should time out.
  48. X
  49. Xgetservbyname() appears to be really slow, at least on Suns.  Yellow Pages
  50. Xshould be outlawed.  YP is also responsible for the fact that biff and rbiff
  51. Xneed to have different names.
  52. X
  53. Xin biff.c, addusers() and delusers() should be combined.
  54. X
  55. X
  56. XBUGS FIXED FROM OLD VERSION:
  57. X----------------------------
  58. X2-line message bodies are handled correctly; the old comsat would print
  59. X"...more..." even if there wasn't anything more.
  60. X
  61. Xblank lines in the message body aren't shown.
  62. X
  63. Xsince biff is now setuid-root, Sunview users can turn biff on and off in
  64. Xspecific windows.
  65. SHAR_EOF
  66. chmod 0600 BUGS || echo "restore of BUGS fails"
  67. sed 's/^X//' << 'SHAR_EOF' > Makefile &&
  68. XCFLAGS = -O
  69. XBINDIR = /usr/ucb        # this is where biff gets installed
  70. XETCDIR = /usr/etc        # this is comsat's directory.
  71. XMANDIR = /usr/man
  72. X
  73. Xall: comsat biff
  74. X
  75. Xcomsat: comsat.c config.h
  76. X    $(CC) $(CFLAGS) comsat.c -o $@
  77. X
  78. X# Biff has to be setuid root, because it has to grab a secure internet port.
  79. X# There are no calls to gets() or insecure strcpy() or bcopy() calls.
  80. Xbiff: biff.c config.h
  81. X    $(CC) $(CFLAGS) biff.c -o $@
  82. X
  83. Xinstallprogs: all
  84. X    install -m 4755 -o root biff $(BINDIR)
  85. X    install -m 755 -o root comsat $(ETCDIR)
  86. X
  87. Xinstallman:
  88. X    install -m 644 -o root biff.1 $(MANDIR)/man1
  89. X    install -m 644 -o root comsat.8c $(MANDIR)/man8
  90. X
  91. Xinstall: installprogs installman
  92. X
  93. Xclean:
  94. X    /bin/rm -f biff comsat core
  95. SHAR_EOF
  96. chmod 0600 Makefile || echo "restore of Makefile fails"
  97. sed 's/^X//' << 'SHAR_EOF' > README &&
  98. XThis is a replacement for the standard BSD "biff" and "comsat"
  99. Xprograms, which notify users when new mail arrives.  The old programs
  100. Xwere very limited; they would only notify a user if mail arrived in his
  101. Xmailbox on the local host.  This version allows monitoring of other
  102. Xusers' mailboxes, even on remote hosts.
  103. X
  104. XTo install the programs, become root and do a make install.  That
  105. Xwill install the manual pages, /usr/ucb/biff, and /usr/etc/comsat.
  106. XYou will need to edit /etc/services and /etc/servers (or /etc/inetd.conf,
  107. Xon some systems).  Add a TCP biff service to /etc/services:
  108. X
  109. Xrbiff        522/tcp
  110. X
  111. Xand in servers or inetd.conf, comment out the line that causes
  112. Xinetd to run comsat.  In /etc/servers it looks like
  113. X
  114. Xcomsat  udp     /usr/etc/in.comsat
  115. X
  116. Xand in inetd.conf,
  117. X
  118. Xcomsat    dgram    udp    wait    root    /usr/etc/in.comsat    in.comsat
  119. X
  120. XAdd an entry to /etc/rc.local so that comsat starts at boot time:
  121. X
  122. Xif [ -f /usr/etc/comsat ]; then
  123. X    /usr/etc/comsat;    (echo -n ' comsat')    >/dev/console
  124. Xfi
  125. X
  126. XYou can remove /usr/etc/in.comsat if desired; it will no longer be
  127. Xcalled.  Restart inetd (on some systems, sending it a HUP signal will
  128. Xcause it to reload its database) and run /usr/etc/comsat.  You're
  129. Xall set!
  130. X
  131. XIf you're installing this on your system, drop me a line.  I'm interested
  132. Xto know how widely this catches on.  I'm not opposed to replacing biff
  133. Xwith this for the next version of BSD (or SunOS), but I'd like to be
  134. Xtold about it first.  Send me any suggestions or bug reports, too.
  135. XEnjoy!
  136. X
  137. X---
  138. XThese are my opinions, which you can probably ignore if you want to.
  139. XSteven Grimm        Moderator, comp.{sources,binaries}.atari.st
  140. Xkoreth@ssyx.ucsc.edu    uunet!ucbvax!ucscc!ssyx!koreth
  141. SHAR_EOF
  142. chmod 0600 README || echo "restore of README fails"
  143. sed 's/^X//' << 'SHAR_EOF' > biff.1 &&
  144. X.TH BIFF 1 "28 December 1988"
  145. X.SH NAME
  146. Xbiff \- watch for incoming mail
  147. X.SH SYNOPSIS
  148. X.B biff
  149. X[ y|n|a|d [ [ user ] [ @host ] ... ] ]
  150. X.SH DESCRIPTION
  151. X.B biff
  152. Xcontrols the mail arrival notification system.  With no arguments,
  153. X.B biff
  154. Xwill display the notification status for the current terminal.  The status
  155. Xis `y' if notification is allowed, or `n' if it is not.
  156. X.SH OPTIONS
  157. X.LP
  158. XThe first parameter can set the current terminal's notification status.  If
  159. Xit is `y' or `n', notification is enabled or disabled, respectively.
  160. X.LP
  161. XBy default, the owner of the tty will only be notified of new mail arriving
  162. Xin his own mailbox.  Additional parameters control monitoring of other
  163. Xmailboxes, optionally on remote machines.  If a username is specified without
  164. Xa hostname, it is assumed to be a user on the local host; if a hostname is
  165. Xspecified without a username, the tty owner's name is assumed.  To monitor
  166. Xanother account's mailbox, a user must be on a host listed in the remote host's
  167. X.B hosts.equiv
  168. Xfile and have the same account name as the remote account, or be listed in
  169. Xthe other account's
  170. X.B \&.rhosts
  171. Xfile.
  172. X.LP
  173. XIf the first parameter is `y' or `a', any additional parameters are added to
  174. Xthe list of other accounts being monitored.  If it is `n' or `d', they are
  175. Xremoved from the list.  The `a' and `d' options allow addition and deletion
  176. Xof other account names without affecting the terminal's notification state.
  177. X.SH "SEE ALSO"
  178. X.BR mail (1),
  179. X.BR comsat (8C)
  180. X.SH BUGS
  181. XThere should be a way of displaying notifications on regular terminals without
  182. Xdisturbing the user's screen display.  On Sun consoles (and possibly others),
  183. Xusers can select the windows in which they wish to enable notification.
  184. X.SH AUTHOR
  185. XSteven Grimm (koreth@ssyx.ucsc.edu)
  186. SHAR_EOF
  187. chmod 0600 biff.1 || echo "restore of biff.1 fails"
  188. sed 's/^X//' << 'SHAR_EOF' > biff.c &&
  189. X/*
  190. X** BIFF
  191. X**
  192. X** Monitor remote mailboxes.
  193. X*/
  194. X
  195. X#include <stdio.h>
  196. X#include <netdb.h>
  197. X#include <ctype.h>
  198. X#include <strings.h>
  199. X#include <sys/types.h>
  200. X#include <netinet/in.h>
  201. X#include <sys/socket.h>
  202. X#include <arpa/inet.h>
  203. X#include <sys/stat.h>
  204. X#include "config.h"
  205. X
  206. Xint tcp_port;
  207. X
  208. Xmain(argc, argv)
  209. Xchar **argv;
  210. X{
  211. X    char code, *tty, *ttyname();
  212. X    int fd, port;
  213. X    struct sockaddr_in in;
  214. X    struct servent *sent;
  215. X    struct stat st;
  216. X
  217. X    if (! isatty(1))
  218. X    {
  219. X        fprintf(stderr, "Can't biff to a pipe/socket\n");
  220. X        exit(-1);
  221. X    }
  222. X
  223. X    tty = ttyname(1);
  224. X    if (stat(tty, &st))
  225. X    {
  226. X        perror("statting tty");
  227. X        exit(-1);
  228. X    }
  229. X
  230. X    if (argc == 1)
  231. X    {
  232. X        printf("is %c\n", st.st_mode & S_IEXEC ? 'y' : 'n');
  233. X        exit(0);
  234. X    }
  235. X
  236. X/* you can't biff when su-ed to another account, unless it's to root */
  237. X    if (geteuid() && geteuid() != st.st_uid)
  238. X    {
  239. X        fprintf(stderr, "%s: Not owner\n", tty);
  240. X        exit(-1);
  241. X    }
  242. X
  243. X    switch(argv[1][0])
  244. X    {
  245. X        case 'y': case 'Y':
  246. X            st.st_mode |= S_IEXEC;
  247. X            chmod(tty, st.st_mode);
  248. X        case 'a': case 'A':
  249. X            if (argc > 2)
  250. X                addusers(argc-2, argv+2);
  251. X            break;
  252. X        case 'n': case 'N':
  253. X            st.st_mode &= ~S_IEXEC;
  254. X            chmod(tty, st.st_mode);
  255. X        case 'd': case 'D':
  256. X            if (argc > 2)
  257. X                delusers(argc-2, argv+2);
  258. X            break;
  259. X        default:
  260. X            fprintf(stderr,
  261. X                "Usage: %s [y|n|a|d [[user][@machine]...]]\n",
  262. X                    argv[0]);
  263. X            exit(-1);
  264. X    }
  265. X}
  266. X
  267. X/*
  268. X** Sort an argument list by machine name.  This makes the load on remote
  269. X** comsats (not to mention the network) a bit lighter, since we can easily
  270. X** service all the requests for each host individually.
  271. X*/
  272. Xsortargs(num, vec)
  273. Xint num;
  274. Xchar **vec;
  275. X{
  276. X    int mcomp();
  277. X
  278. X    qsort(vec, num, sizeof(char *), mcomp);
  279. X}
  280. X
  281. X/*
  282. X** Compare the hostnames of two user@host entries.
  283. X*/
  284. Xmcomp(a1, a2)
  285. Xchar *a1, *a2;
  286. X{
  287. X    a1 = index(a1, '@');
  288. X    a2 = index(a2, '@');
  289. X
  290. X    if (a1 == NULL)
  291. X        if (a2 == NULL)
  292. X            return 0;
  293. X        else
  294. X            return -1;
  295. X    if (a2 == NULL)
  296. X        return 1;
  297. X
  298. X    return(strcmp(a1, a2));
  299. X}
  300. X
  301. Xinit_tcp()
  302. X{
  303. X    struct servent *sent;
  304. X
  305. X    if ((sent = getservbyname("rbiff", "tcp")) != NULL)
  306. X        tcp_port = sent->s_port;
  307. X    else
  308. X        tcp_port = htons(TCP_PORT);
  309. X}
  310. X
  311. X/*
  312. X** Look up a host address, or translate numeric (a.b.c.d) notation to a
  313. X** valid inet address.  Puts the host address in *buf.  Returns 0 if
  314. X** the host wasn't found.
  315. X*/
  316. Xhaddr(host, buf)
  317. Xchar *host;
  318. Xunsigned long *buf;
  319. X{
  320. X    struct hostent *hent;
  321. X
  322. X    if (isdigit(host[0]))
  323. X    {
  324. X        *buf = inet_addr(host);
  325. X        return 1;
  326. X    }
  327. X    hent = gethostbyname(host);
  328. X    if (hent == NULL)
  329. X        return 0;
  330. X    bcopy(hent->h_addr, buf, hent->h_length);
  331. X    return 1;
  332. X}
  333. X
  334. Xint sockfd;
  335. X
  336. X/*
  337. X** Initiate a connection with a host.  Returns 0 on failure.
  338. X*/
  339. Xgetcon(hostname)
  340. Xchar *hostname;
  341. X{
  342. X    char    buf[10];
  343. X    struct    sockaddr_in in;
  344. X    int    port;
  345. X
  346. X    port = 1023;
  347. X    sockfd = rresvport(&port);
  348. X    if (sockfd < 0)
  349. X    {
  350. X        perror("rresvport");
  351. X        return 0;
  352. X    }
  353. X
  354. X    bzero(&in, sizeof(in));
  355. X    if (! haddr(hostname, &in.sin_addr.s_addr))
  356. X    {
  357. X        fprintf("%s: Host unknown\n", hostname);
  358. X        return 0;
  359. X    }
  360. X    in.sin_port = tcp_port;
  361. X    in.sin_family = AF_INET;
  362. X
  363. X    if (connect(sockfd, &in, sizeof(in)))
  364. X    {
  365. X        perror(hostname);
  366. X        return 0;
  367. X    }
  368. X
  369. X    sprintf(buf, "P%d", tcp_port);
  370. X    write(sockfd, buf, strlen(buf)+1);
  371. X    read(sockfd, buf, 1);
  372. X    return 1;
  373. X}
  374. X
  375. X/*
  376. X** Extract a hostname from a user@host string.  If there's no @host, use
  377. X** localhost.
  378. X*/
  379. Xchar *hname(str)
  380. Xchar *str;
  381. X{
  382. X    char *at;
  383. X
  384. X    at = index(str, '@');
  385. X    if (at == NULL)
  386. X        return("localhost");
  387. X    else
  388. X        return(at+1);
  389. X}
  390. X
  391. Xchar *addcodes[] = {
  392. X"",
  393. X"Out of memory",
  394. X"Already monitoring",
  395. X"Permission denied"
  396. X};
  397. X
  398. Xaddusers(num, vec)
  399. Xint num;
  400. Xchar **vec;
  401. X{
  402. X    int    i, npos;
  403. X    char    *curhost, *tmp, buf[130], *getlogin(), code;
  404. X
  405. X    sortargs(num, vec);
  406. X    curhost = vec[0];
  407. X    i = 0;
  408. X
  409. X    init_tcp();
  410. X
  411. X    sprintf(buf, "W%s", getlogin());
  412. X    npos = strlen(buf)+1;    /* position to place second name */
  413. X    
  414. X    while(i < num) {
  415. X        if (! getcon(hname(vec[i])))
  416. X        {
  417. X            fprintf(stderr, "Couldn't register %s\n", vec[i]);
  418. X            while (! mcomp(curhost, vec[i]))
  419. X                if (++i == num)
  420. X                    break;
  421. X            if (i < num)
  422. X                curhost = vec[i];
  423. X        }
  424. X        else
  425. X        {
  426. X            while (! mcomp(curhost, vec[i]))
  427. X            {
  428. X                if (vec[i][0] == '@')
  429. X                    strcpy(buf+npos, getlogin());
  430. X                else
  431. X                {
  432. X                    strncpy(buf+npos, vec[i], 64);
  433. X                    buf[npos+64] = '\0';
  434. X                    if ((tmp=index(buf+npos,'@')) != NULL)
  435. X                        *tmp = '\0';
  436. X                }
  437. X                write(sockfd, buf, npos+strlen(buf+npos)+1);
  438. X                read(sockfd, &code, 1);
  439. X                if (code)
  440. X                    fprintf(stderr, "%s: %s\n", vec[i],
  441. X                        addcodes[code]);
  442. X                if (++i == num)
  443. X                    break;
  444. X            }
  445. X            if (i < num)
  446. X                curhost = vec[i];
  447. X            close(sockfd);
  448. X        }
  449. X    }
  450. X}
  451. X
  452. X/*
  453. X** Delete users from watch lists.  This looks mostly identical to
  454. X** addusers(), but is just different enough to warrant its own
  455. X** routine.
  456. X*/
  457. Xdelusers(num, vec)
  458. Xint num;
  459. Xchar **vec;
  460. X{
  461. X    int    i, npos;
  462. X    char    *curhost, *tmp, buf[130], *getlogin(), code;
  463. X
  464. X    sortargs(num, vec);
  465. X    curhost = vec[0];
  466. X    i = 0;
  467. X
  468. X    init_tcp();
  469. X
  470. X    sprintf(buf, "D%s", getlogin());
  471. X    npos = strlen(buf)+1;    /* position to place second name */
  472. X    
  473. X    while(i < num) {
  474. X        if (! getcon(hname(vec[i])))
  475. X        {
  476. X            fprintf(stderr, "Couldn't delete %s\n", vec[i]);
  477. X            while (! mcomp(curhost, vec[i]))
  478. X                if (++i == num)
  479. X                    break;
  480. X            if (i < num)
  481. X                curhost = vec[i];
  482. X        }
  483. X        else
  484. X        {
  485. X            while (! mcomp(curhost, vec[i]))
  486. X            {
  487. X                if (vec[i][0] == '@')
  488. X                    buf[npos] = '\0';
  489. X                else
  490. X                {
  491. X                    strncpy(buf+npos, vec[i], 64);
  492. X                    buf[npos+64] = '\0';
  493. X                    if ((tmp=index(buf+npos,'@')) != NULL)
  494. X                        *tmp = '\0';
  495. X                }
  496. X                write(sockfd, buf, npos+strlen(buf+npos)+1);
  497. X                read(sockfd, &code, 1);
  498. X                if (!code)
  499. X                    printf("error deleting %s\n", vec[i]);
  500. X                if (++i == num)
  501. X                    break;
  502. X            }
  503. X            if (i < num)
  504. X                curhost = vec[i];
  505. X            close(sockfd);
  506. X        }
  507. X    }
  508. X}
  509. X
  510. SHAR_EOF
  511. chmod 0600 biff.c || echo "restore of biff.c fails"
  512. sed 's/^X//' << 'SHAR_EOF' > comsat.8c &&
  513. X.TH COMSAT 8C "28 December 1988"
  514. X.SH NAME
  515. Xcomsat \- mail notification daemon
  516. X.SH SYNOPSIS
  517. X/usr/etc/comsat
  518. X.SH DESCRIPTION
  519. X.B comsat
  520. Xwatches for datagrams on the UDP "biff" service port (usually 512) for messages
  521. Xof the form
  522. X.IR user @ mailbox-offset
  523. Xwhere
  524. X.I user
  525. Xis a valid username on the local host and
  526. X.I mailbox-offset
  527. Xis the byte offset into that user's mailbox of a new mail message.  It then
  528. Xdisplays some of the header lines and up to two lines of the message body to
  529. Xeveryone who is monitoring the mailbox in question (see below).
  530. X.LP
  531. XIf the owner of the mailbox is logged in and the owner-execute bit of his tty
  532. Xis set, he is automatically notified.  The
  533. X.BR biff (1)
  534. Xcommand can be used to set that bit easily, as well as to register requests
  535. Xto monitor other mailboxes.
  536. X.LP
  537. X.B comsat
  538. Xalso listens for stream connections on the TCP "biff" service port (522 by
  539. Xdefault).  Connections to that port must come from secure port numbers
  540. X(numbered less than 1024) or they will be closed immediately.  Once the
  541. Xconnection has been established, the remote program (usually a
  542. X.B comsat
  543. Xon another host, or
  544. X.BR biff )
  545. Xcan issue commands to
  546. X.BR comsat :
  547. Xadd a monitor, delete a monitor, verify that a user is logged on, or send
  548. Xa mail excerpt to a user on the local host.
  549. X.LP
  550. XA user must meet the authentication requirements enforced by
  551. X.BR ruserok (3)
  552. Xin order to monitor a mailbox other than his own.
  553. X.LP
  554. XWhen
  555. X.B comsat
  556. Xreceives a
  557. X.IR user @ offset
  558. Xdatagram, it looks through its list of monitors.  If anyone is monitoring the
  559. X.IR user 's
  560. Xmailbox,
  561. X.B comsat
  562. Xeither notifies him directly (if he is monitoring from the local host, is
  563. Xlogged on, and his tty has the owner-execute bit set) or initiates a
  564. Xconnection to the
  565. X.B comsat
  566. Xrunning on his host, and sends the mail excerpt to that host, which
  567. Xnotifies the monitor if he has his owner-execute bit set.
  568. X.LP
  569. X.B comsat
  570. Xwakes up on a regular basis and verifies that all the users monitoring
  571. Xmailboxes are still logged onto their respective hosts.  It deletes the
  572. Xabsent monitors from its monitor list.
  573. X.SH "SEE ALSO"
  574. X.BR biff (1),
  575. X.BR rcmd (3)
  576. X.SH BUGS
  577. XIf a user logs off then on in between two login checks, he will still
  578. Xbe notified of mail arriving at mailboxes monitored during his first
  579. Xlogin session.  This may be a feature.
  580. X.LP
  581. XOn extremely active systems, the login checks may never occur; a minimum
  582. Xnumber of seconds (120 in the default distribution, though this is
  583. Xconfigurable at each site) must pass since the last datagram or stream
  584. Xconnection before the check occurs.
  585. X.SH AUTHOR
  586. XSteven Grimm (koreth@ssyx.ucsc.edu)
  587. X
  588. SHAR_EOF
  589. chmod 0600 comsat.8c || echo "restore of comsat.8c fails"
  590. sed 's/^X//' << 'SHAR_EOF' > comsat.c &&
  591. X/*
  592. X** COMSAT
  593. X**
  594. X** A network version of the BSD biff server.
  595. X**
  596. X** Written by Steven Grimm (koreth@ssyx.ucsc.edu), 12-27-88.
  597. X*/
  598. X#include <sys/types.h>
  599. X#include <utmp.h>
  600. X#include <stdio.h>
  601. X#include <netdb.h>
  602. X#include <ctype.h>
  603. X#include <strings.h>
  604. X#include <signal.h>
  605. X#include <sys/file.h>
  606. X#include <sys/stat.h>
  607. X#include <sys/time.h>
  608. X#include <sys/ioctl.h>
  609. X#include <sys/socket.h>
  610. X#include <netinet/in.h>
  611. X#include <arpa/inet.h>
  612. X#ifndef MAXPATHLEN
  613. X#include <sys/param.h>
  614. X#endif
  615. X#include "config.h"
  616. X
  617. X#ifndef FD_SETSIZE    /* for 4.2BSD */
  618. X#define FD_SETSIZE      (sizeof(fd_set) * 8)
  619. X#define FD_SET(n, p)    (((fd_set *) (p))->fds_bits[0] |= (1 << ((n) % 32)))
  620. X#define FD_CLR(n, p)    (((fd_set *) (p))->fds_bits[0] &= ~(1 << ((n) % 32)))
  621. X#define FD_ISSET(n, p)  (((fd_set *) (p))->fds_bits[0] & (1 << ((n) % 32)))
  622. X#define FD_ZERO(p)      bzero((char *)(p), sizeof(*(p)))
  623. X#endif
  624. X
  625. Xextern    char *sys_errlist[];
  626. Xextern    int errno;
  627. X
  628. Xint    udp_fd, tcp_fd;
  629. Xstruct    sockaddr_in tcp_host[FD_SETSIZE];
  630. Xfd_set    readset;
  631. X
  632. X/*
  633. X** A linked list of these structures is used to keep track of remote users
  634. X** who are watching mailboxes on the local host.
  635. X*/
  636. Xstruct ruser {
  637. X    struct    sockaddr_in host;    /* watcher's host */
  638. X    char    watcher[64];        /* watcher's name */
  639. X    char    watchee[64];        /* watchee's name */
  640. X    int    local;            /* set if host==localhost */
  641. X    int    flag;            /* flag for temporary use */
  642. X    struct    ruser *next;        /* link to next entry */
  643. X};
  644. X
  645. Xstruct ruser *rlist = NULL;
  646. X
  647. X/*
  648. X** Add a remote user to the watch list.  Returns 0 on success, 1 on
  649. X** malloc failure, 2 if that user is repeating an existing request,
  650. X** or 3 if the remote user authentication fails.
  651. X*/
  652. Xr_add(host, watcher, watchee)
  653. Xstruct sockaddr_in *host;
  654. Xchar *watcher, *watchee;
  655. X{
  656. X    struct    ruser *entry;
  657. X    struct    hostent *hostent;
  658. X    int    ruok;
  659. X    char    hbuf[64];
  660. X
  661. X    for (entry = rlist; entry != NULL; entry = entry->next)
  662. X        if ((!strcmp(watcher, entry->watcher)) &&
  663. X                (!strcmp(watchee, entry->watchee)))
  664. X            return 2;
  665. X
  666. X    if ((hostent = gethostbyaddr(&host->sin_addr, sizeof(host->sin_addr),
  667. X                AF_INET)) == NULL)
  668. X        strcpy(hbuf, inet_ntoa(host->sin_addr));
  669. X    else
  670. X        strncpy(hbuf, hostent->h_name, 63);
  671. X    hbuf[63] = '\0';
  672. X    if (! host->sin_addr.s_addr)
  673. X        strcpy(hbuf, "localhost");
  674. X
  675. X    ruok = ruserok(hbuf, 0, watcher, watchee);
  676. X    setreuid(0,0);        /* make up for some Sun bugs */
  677. X    if (ruok)
  678. X        return 3;
  679. X
  680. X    entry = (struct ruser *)malloc(sizeof(struct ruser));
  681. X    if (entry == NULL)
  682. X        return 1;
  683. X
  684. X    entry->next = rlist;
  685. X    rlist = entry;
  686. X    strncpy(entry->watcher, watcher, 64);
  687. X    strncpy(entry->watchee, watchee, 64);
  688. X    bcopy(host, &entry->host, sizeof(struct sockaddr_in));
  689. X    entry->local = localhost(host);
  690. X
  691. X    return 0;
  692. X}
  693. X
  694. X/*
  695. X** Compare a host-username pair with the remote host and username in a
  696. X** watch list entry.  Returns 0 if they're the same.  Watche[er] may be empty
  697. X** in which case it's ignored; host is always significant.
  698. X*/
  699. Xr_cmp(host, watcher, watchee, entry)
  700. Xstruct sockaddr_in *host;
  701. Xchar *watcher, *watchee;
  702. Xstruct ruser *entry;
  703. X{
  704. X    if (watcher[0] && strcmp(watcher, entry->watcher))
  705. X        return 1;
  706. X    if (watchee[0] && strcmp(watchee, entry->watchee))
  707. X        return 1;
  708. X/* we have to check for localhost, since watchees registered without */
  709. X/* machine names will be watched by 'localhost', and checking for */
  710. X/* user@realmachinename will fail unless we do the special check. */
  711. X    if (entry->local)
  712. X    {
  713. X        if (!localhost(host))
  714. X            return 1;
  715. X    }
  716. X    else if (bcmp(&host->sin_addr, &entry->host.sin_addr,
  717. X            sizeof(host->sin_addr)))
  718. X        return 1;
  719. X    return 0;
  720. X}
  721. X
  722. X/*
  723. X** Delete a remote user from the watch list.
  724. X*/
  725. Xr_del(host, watcher, watchee)
  726. Xstruct sockaddr_in *host;
  727. Xchar *watcher, *watchee;
  728. X{
  729. X    struct ruser *cur, *tmp;
  730. X    int delflg = 0;
  731. X
  732. X#ifdef DEBUG
  733. X    printf("r_del(%s, %s, %s)\n", inet_ntoa(host->sin_addr.s_addr),
  734. X                    watcher, watchee);
  735. X#endif
  736. X
  737. X    while (rlist != NULL)
  738. X        if (! r_cmp(host, watcher, watchee, rlist))
  739. X        {
  740. X            tmp = rlist;
  741. X            rlist = rlist->next;
  742. X            free(tmp);
  743. X            delflg = 1;
  744. X        }
  745. X        else
  746. X            break;
  747. X
  748. X    if (rlist == NULL)
  749. X        return delflg;
  750. X
  751. X    cur = rlist;
  752. X
  753. X    while (cur->next != NULL)
  754. X        if (! r_cmp(host, watcher, watchee, rlist))
  755. X        {
  756. X            tmp = cur->next;
  757. X            cur->next = cur->next->next;
  758. X            free(tmp);
  759. X            delflg = 1;
  760. X        }
  761. X        else
  762. X            cur = cur->next;
  763. X
  764. X    return delflg;
  765. X}
  766. X
  767. X/*
  768. X** Open and bind the necessary sockets.  One socket listens for datagrams,
  769. X** the other for stream connection requests.
  770. X*/
  771. Xvoid
  772. Xgetsocks()
  773. X{
  774. X    struct servent *tcpsvc, *udpsvc;
  775. X    struct sockaddr_in addr;
  776. X
  777. X    tcpsvc = getservbyname("rbiff", "tcp");
  778. X
  779. X    tcp_fd = socket(PF_INET, SOCK_STREAM, 0);
  780. X    if (tcp_fd < 0)
  781. X        panic("tcp socket()");
  782. X
  783. X    udp_fd = socket(PF_INET, SOCK_DGRAM, 0);
  784. X    if (udp_fd < 0)
  785. X        panic("udp socket()");
  786. X
  787. X    bzero(&addr, sizeof(addr));
  788. X    addr.sin_family = AF_INET;
  789. X    addr.sin_addr.s_addr = INADDR_ANY;
  790. X
  791. X    if (tcpsvc == NULL)
  792. X        addr.sin_port = htons(TCP_PORT);
  793. X    else
  794. X        addr.sin_port = tcpsvc->s_port;
  795. X    if (bind(tcp_fd, &addr, sizeof(addr)) < 0)
  796. X        panic("tcp bind()");
  797. X
  798. X    udpsvc = getservbyname("biff", "udp");
  799. X
  800. X    if (udpsvc == NULL)
  801. X        addr.sin_port = htons(UDP_PORT);
  802. X    else
  803. X        addr.sin_port = udpsvc->s_port;
  804. X    if (bind(udp_fd, &addr, sizeof(addr)) < 0)
  805. X        panic("udp bind()");
  806. X
  807. X    if (listen(tcp_fd, 5) < 0)
  808. X        panic("listen (weird)");
  809. X
  810. X    if (fcntl(tcp_fd, F_SETFL, FIONBIO) == -1)
  811. X        panic("fcntl tcp_fd");
  812. X    if (fcntl(udp_fd, F_SETFL, FIONBIO) == -1)
  813. X        panic("fcntl udp_fd");
  814. X
  815. X    FD_ZERO(&readset);
  816. X    FD_SET(tcp_fd, &readset);
  817. X    FD_SET(udp_fd, &readset);
  818. X}
  819. X
  820. X/*
  821. X** Send a message to a user, once to each of his ttys that has the owner
  822. X** execute bit set.
  823. X*/
  824. Xvoid
  825. Xbiff(user, msg)
  826. Xchar *user, *msg;
  827. X{
  828. X    char filename[MAXPATHLEN];
  829. X    struct utmp uent;
  830. X    struct stat st;
  831. X    FILE *fp;
  832. X    int tty;
  833. X
  834. X    if ((fp = fopen(UTMP, "r")) == NULL)
  835. X        return;
  836. X
  837. X    while (! feof(fp))
  838. X    {
  839. X        if (! fread(&uent, sizeof(uent), 1, fp))
  840. X            break;
  841. X        if (strcmp(uent.ut_name, user))
  842. X            continue;
  843. X        sprintf(filename, "%s/%s", DEVDIR, uent.ut_line);
  844. X        if (stat(filename, &st) < 0)
  845. X            continue;
  846. X        if ((st.st_mode & S_IEXEC)
  847. X#ifndef DEBUG
  848. X            && !fork()
  849. X#endif
  850. X            )
  851. X        {
  852. X            tty = open(filename, O_RDWR);
  853. X            if (tty)
  854. X                write(tty, msg, strlen(msg));
  855. X#ifdef DEBUG
  856. X            close(tty);
  857. X#else
  858. X            exit(0);
  859. X#endif
  860. X        }
  861. X    }
  862. X    fclose(fp);
  863. X}
  864. X
  865. X/*
  866. X** Is a header line valid?  This routine finds out.  Returns 0 if the
  867. X** line shouldn't be included in the biff message.
  868. X*/
  869. Xhdrline(buf)
  870. Xchar *buf;
  871. X{
  872. X    int    i;
  873. X
  874. X    for (i = 0; i < Nfields; i++)
  875. X        if (!strncmp(buf, fields[i], strlen(fields[i])))
  876. X            return 1;
  877. X    return 0;
  878. X}
  879. X
  880. X/*
  881. X** Build a message to send out to everyone who's waiting for mail
  882. X** to a specific user.  Pass the username, offset in his mailbox of
  883. X** the new mail, and the address of a buffer at least 800 bytes long.
  884. X*/
  885. Xvoid
  886. Xbuildmsg(name, offset, buf)
  887. Xchar *name, *buf;
  888. Xint offset;
  889. X{
  890. X    struct    ruser *cur;
  891. X    struct    stat st;
  892. X    struct    timeval tvp[2];
  893. X    FILE    *fp;
  894. X    int    bpos, body;
  895. X    char    filename[MAXPATHLEN];
  896. X
  897. X    gethostname(filename, 79);
  898. X    filename[79] = '\0';
  899. X
  900. X    sprintf(buf, "\n\r\007New mail for %.20s@%.30s\007 has arrived:\n\r----\n\r",
  901. X        name, filename);
  902. X    bpos = strlen(buf);
  903. X
  904. X    sprintf(filename, "%s/%s", MAILDIR, name);
  905. X
  906. X    stat(filename, &st);
  907. X    if ((fp = fopen(filename, "r")) == NULL)
  908. X    {
  909. X        sprintf(buf+bpos, "(%s: %s)\n\r----\n\r", filename,
  910. X            sys_errlist[errno]);
  911. X        return;
  912. X    }
  913. X
  914. X    body = 0;    /* we are looking at the message header. */
  915. X
  916. X    fseek(fp, offset, 0);
  917. X
  918. X    while (! feof(fp))
  919. X    {
  920. X        if (fgets(buf+bpos, 780-bpos, fp) == NULL)
  921. X        {
  922. X            strcpy(buf+bpos, "----\n\r");
  923. X            break;
  924. X        }
  925. X        if (! body)
  926. X        {
  927. X            if (buf[bpos] == '\n')
  928. X            {
  929. X                bpos++;
  930. X                body++;
  931. X            }
  932. X            else if (hdrline(buf+bpos))
  933. X            {
  934. X                bpos += strlen(buf+bpos);
  935. X                buf[bpos++] = '\r';
  936. X            }
  937. X        }
  938. X        else
  939. X        {
  940. X            if (buf[bpos] == '\n')
  941. X                continue;
  942. X            if (++body == 4)
  943. X            {
  944. X                strcpy(buf+bpos, "...more...\n\r");
  945. X                break;
  946. X            }
  947. X            bpos += strlen(buf+bpos);
  948. X            buf[bpos++] = '\r';
  949. X        }
  950. X    }
  951. X    fclose(fp);
  952. X
  953. X/* login's "you have new mail" message won't work unless mtime > atime */
  954. X    bzero(tvp, sizeof(tvp));
  955. X    tvp[0].tv_sec = st.st_atime;
  956. X    tvp[1].tv_sec = st.st_mtime;
  957. X    utimes(filename, tvp);
  958. X}
  959. X
  960. X/*
  961. X** Determine whether or not a host address points to the local host.  Returns
  962. X** 0 if the host isn't local.  There's probably a much better way of doing
  963. X** this: first we see if the host is called "localhost", then see if its
  964. X** name is the same one that gethostname() returns, then see if gethostname()
  965. X** returns a machine name whose address is equal to the host address we're
  966. X** checking.  Yucko.
  967. X*/
  968. Xlocalhost(addr)
  969. Xstruct sockaddr_in *addr;
  970. X{
  971. X    struct    hostent *host;
  972. X    char    hname[64];
  973. X    int    i;
  974. X
  975. X    if (! addr->sin_addr.s_addr)
  976. X        return 1;
  977. X
  978. X    gethostname(hname, 64);
  979. X
  980. X    if ((host = gethostbyaddr(&addr->sin_addr, sizeof(addr->sin_addr),
  981. X            AF_INET)) != NULL)
  982. X    {
  983. X        if (! stricmp(host->h_name, "localhost"))
  984. X            return 1;
  985. X        if (! stricmp(host->h_name, hname))
  986. X            return 1;
  987. X    }
  988. X
  989. X    if ((host = gethostbyname(hname)) != NULL)
  990. X#ifndef h_addr
  991. X        if (!bcmp(host->h_addr,&addr->sin_addr,host->h_length))
  992. X            return 1;
  993. X#else /* if we have multiple host addresses, check all of 'em */
  994. X        for (i = 0; host->h_addr_list[i] != NULL; i++)
  995. X            if (! bcmp(host->h_addr_list[i], &addr->sin_addr,
  996. X                    sizeof(addr->sin_addr)))
  997. X                return 1;
  998. X#endif
  999. X    return 0;
  1000. X}
  1001. X
  1002. X/*
  1003. X** See if a user is logged in.  Return 1 if he is.
  1004. X*/
  1005. Xlogged_in(user)
  1006. Xchar *user;
  1007. X{
  1008. X    struct    utmp uent;
  1009. X    int    sawhim = 0;
  1010. X    FILE    *fp;
  1011. X
  1012. X    fp = fopen(UTMP, "r");
  1013. X    if (fp == NULL)
  1014. X        return 0;
  1015. X
  1016. X    while (! feof(fp))
  1017. X    {
  1018. X        if (! fread(&uent, sizeof(uent), 1, fp))
  1019. X            break;
  1020. X        if (strcmp(uent.ut_name, user))
  1021. X            continue;
  1022. X        sawhim++;
  1023. X        break;
  1024. X    }
  1025. X    fclose(fp);
  1026. X
  1027. X    return(sawhim);
  1028. X}
  1029. X
  1030. X/*
  1031. X** Open up a dialogue with a remote comsat, and send it a biff for someone.
  1032. X*/
  1033. Xvoid
  1034. Xrbiff(cur, buf)
  1035. Xstruct ruser *cur;
  1036. Xchar *buf;
  1037. X{
  1038. X    char    biffcmd[90];
  1039. X    int    fd, port, len;
  1040. X
  1041. X    sprintf(biffcmd, "B%s%c%d", cur->watcher, '\0', strlen(buf) + 1);
  1042. X
  1043. X/* connect to the remote, as specified in the rlist entry.  make sure we */
  1044. X/* are coming from a secure port else the other guy will hang up on us.  */
  1045. X    port = 1023;
  1046. X    fd = rresvport(&port);
  1047. X    if (fd < 0)
  1048. X        return;
  1049. X    if (connect(fd, &cur->host, sizeof(cur->host)))
  1050. X    {
  1051. X        close(fd);
  1052. X        return;
  1053. X    }
  1054. X
  1055. X    len = strlen(biffcmd);
  1056. X    len += strlen(biffcmd+len+1)+2;
  1057. X    write(fd, biffcmd, len);
  1058. X
  1059. X    if (len = gack(fd))
  1060. X    {
  1061. X        shutdown(fd, 2);
  1062. X        close(fd);
  1063. X        return;
  1064. X    }
  1065. X
  1066. X    write(fd, buf, strlen(buf)+1);
  1067. X
  1068. X    gack(fd);
  1069. X
  1070. X    shutdown(fd, 2);
  1071. X    close(fd);
  1072. X}
  1073. X
  1074. X/*
  1075. X** Handle an incoming datagram.  This should be a message of the form
  1076. X** "user@offset", where user is the user who has new mail and offset
  1077. X** is the byte offset into his mailbox of the new message.  If SECUREBIFF
  1078. X** is defined in config.h, the datagram must come from a reserved (<1024)
  1079. X** port on the local host.
  1080. X*/
  1081. Xvoid
  1082. Xudp()
  1083. X{
  1084. X    struct    ruser *cur;
  1085. X    struct    sockaddr_in from;
  1086. X    char    buf[800], user[64], *cp;
  1087. X    int    offset;
  1088. X
  1089. X    offset = sizeof(from);
  1090. X    if ((offset = recvfrom(udp_fd, buf, 800, 0, &from, &offset)) < 3)
  1091. X        return;
  1092. X    buf[offset] = '\0';
  1093. X
  1094. X#ifdef SECUREBIFF
  1095. X    if (ntohs(from.sin_port) >= IPPORT_RESERVED)
  1096. X        return;
  1097. X    if (! localhost(&from))
  1098. X        return;
  1099. X#endif
  1100. X
  1101. X    if ((cp = index(buf, '@')) == NULL)
  1102. X        return;
  1103. X    *cp++ = '\0';
  1104. X
  1105. X    strncpy(user, buf, 63);
  1106. X    user[63] = '\0';
  1107. X
  1108. X    if (! isdigit(*cp))
  1109. X        return;
  1110. X    offset = atoi(cp);
  1111. X
  1112. X    buildmsg(user, offset, buf);
  1113. X    biff(user, buf);
  1114. X
  1115. X    for (cur = rlist; cur != NULL; cur = cur->next)
  1116. X        if (! strcmp(cur->watchee, user))
  1117. X/* if the watcher is on the local host, just biff him. */
  1118. X            if (cur->local)
  1119. X            {
  1120. X/* if the watcher is watching himself, don't biff him again. */
  1121. X                if (strcmp(user, cur->watcher))
  1122. X                    biff(cur->watcher, buf);
  1123. X            }
  1124. X            else
  1125. X                rbiff(cur, buf);
  1126. X}
  1127. X
  1128. X/*
  1129. X** Handle a pending TCP connection.  If it's not coming from a secure port
  1130. X** on the remote host, close it immediately.
  1131. X*/
  1132. Xvoid
  1133. Xtcp()
  1134. X{
  1135. X    struct sockaddr_in host;
  1136. X    int len, tcp_fd_c;
  1137. X
  1138. X    len = sizeof(host);
  1139. X    tcp_fd_c = accept(tcp_fd, &host, &len);
  1140. X    if (tcp_fd_c < 0)
  1141. X        return;
  1142. X
  1143. X    if (ntohs(host.sin_port) >= IPPORT_RESERVED)
  1144. X    {
  1145. X        shutdown(tcp_fd_c, 2);
  1146. X        close(tcp_fd_c);
  1147. X        return;
  1148. X    }
  1149. X
  1150. X    len = 1;
  1151. X    setsockopt(tcp_fd_c, SOL_SOCKET, SO_KEEPALIVE, &len, sizeof(len));
  1152. X    fcntl(tcp_fd_c, F_SETFL, FIONBIO);
  1153. X
  1154. X    bcopy(&host, &tcp_host[tcp_fd_c], sizeof(host));
  1155. X    FD_SET(tcp_fd_c, &readset);
  1156. X}
  1157. X
  1158. X/*
  1159. X** Handle incoming data on a connected socket.  This should be in the form
  1160. X** of a single-character command byte followed by null-terminated parameters.
  1161. X** There probably isn't enough error checking here.
  1162. X*/
  1163. Xvoid
  1164. Xtcpc(fd)
  1165. Xint fd;
  1166. X{
  1167. X    char    buf[800], user[64];
  1168. X    int    nbytes;
  1169. X
  1170. X    ioctl(fd, FIONREAD, &nbytes);
  1171. X    if (! nbytes)        /* 0 bytes available means disconnect */
  1172. X    {
  1173. X        shutdown(fd, 2);
  1174. X        close(fd);
  1175. X        FD_CLR(fd, &readset);
  1176. X        return;
  1177. X    }
  1178. X
  1179. X    if (nbytes > sizeof(buf))
  1180. X        nbytes = sizeof(buf);
  1181. X
  1182. X    read(fd, buf, nbytes);
  1183. X    buf[nbytes] = '\0';
  1184. X
  1185. X    if (buf[0] == 'W')    /* Watch watcher\0watchee\0 */
  1186. X        ack(fd, r_add(&tcp_host[fd], buf+1, buf+strlen(buf) + 1));
  1187. X    else if (buf[0] == 'D')    /* Delete watcher\0[watchee]\0 */
  1188. X        ack(fd, r_del(&tcp_host[fd], buf+1, buf+strlen(buf) + 1));
  1189. X    else if (buf[0] == 'V') /* Verify watcher\0 */
  1190. X        ack(fd, logged_in(buf+1));
  1191. X    else if (buf[0] == 'P')    /* Port portnum\0 */
  1192. X    {
  1193. X        tcp_host[fd].sin_port = htons(atoi(buf+1));
  1194. X        ack(fd, 0);
  1195. X    }
  1196. X    else if (buf[0] == 'B')    /* Biff watcher\0length\0 */
  1197. X    {
  1198. X        char    user[64];
  1199. X        int    len, nread;
  1200. X
  1201. X        strcpy(user, buf+1);
  1202. X        len = atoi(buf+strlen(buf) + 1);
  1203. X        if (len > sizeof(buf))    /* don't wanna overflow our buffer */
  1204. X        {
  1205. X            ack(fd, 1);
  1206. X            return;
  1207. X        }
  1208. X
  1209. X        ack(fd, 0);    /* tell him to start sending */
  1210. X
  1211. X        nbytes = 0;
  1212. X        while (nbytes < len)
  1213. X        {
  1214. X            fd_set readbits;
  1215. X            struct timeval tout;
  1216. X
  1217. X            FD_ZERO(&readbits);
  1218. X            FD_SET(fd, &readbits);
  1219. X            timerclear(&tout);
  1220. X            tout.tv_sec = 20;    /* biff msg must arrive fast */
  1221. X
  1222. X            select(fd+1, &readbits, NULL, NULL, &tout);
  1223. X            nread = read(fd, buf+nbytes, len-nbytes);
  1224. X            if (nread < 1)    /* if timed out, we get EWOULDBLOCK */
  1225. X            {
  1226. X                close(fd);
  1227. X                FD_CLR(fd, &readset);
  1228. X                return;
  1229. X            }
  1230. X            nbytes += nread;
  1231. X        }
  1232. X        biff(user, buf);
  1233. X        ack(fd, 0);
  1234. X    }
  1235. X}
  1236. X
  1237. X/*
  1238. X** Send a byte out on a file descriptor.
  1239. X*/
  1240. Xack(fd, ch)
  1241. Xint fd, ch;
  1242. X{
  1243. X    char c;
  1244. X
  1245. X    c = (char) ch;
  1246. X    write(fd, &c, 1);
  1247. X}
  1248. X
  1249. X/*
  1250. X** Read a byte from a file descriptor.
  1251. X*/
  1252. Xgack(fd)
  1253. X{
  1254. X    char c;
  1255. X
  1256. X    read(fd, &c, 1);
  1257. X    return (int) c;
  1258. X}
  1259. X
  1260. X/*
  1261. X** Do a perror() and exit.
  1262. X*/
  1263. Xpanic(string)
  1264. Xchar *string;
  1265. X{
  1266. X    perror(string);
  1267. X    exit(-1);
  1268. X}
  1269. X
  1270. X/*
  1271. X** A custom bcopy whose behavior is known for overlapping regions
  1272. X*/
  1273. Xbcopy(src, des, len)
  1274. Xregister char *src, *des;
  1275. Xregister int len;
  1276. X{
  1277. X    while (len--)
  1278. X        *des++ = *src++;
  1279. X}
  1280. X
  1281. X/*
  1282. X** Case-insensitive compare.  This should use lookup tables for fast case
  1283. X** conversion.
  1284. X*/
  1285. Xstricmp(str1, str2)
  1286. Xregister char *str1, *str2;
  1287. X{
  1288. X    while (*str1 || *str2)
  1289. X    {
  1290. X        if ((islower(*str1) ? toupper(*str1) : *str1) !=
  1291. X            (islower(*str2) ? toupper(*str2) : *str2))
  1292. X            return 1;
  1293. X        str1++;
  1294. X        str2++;
  1295. X    }
  1296. X    return 0;
  1297. X}
  1298. X
  1299. X/*
  1300. X** See which watchers are still online, and delete the entries of those
  1301. X** who have logged off.  This probably scans through the list a lot more
  1302. X** times than is necessary.  Oh well.
  1303. X*/
  1304. Xvoid
  1305. Xrollcall()
  1306. X{
  1307. X    struct    ruser *cur, *user, *place;
  1308. X    int    port, fd, uflag;
  1309. X    char    buf[66], code;
  1310. X
  1311. X/* if there are no watchers, this is pretty easy. */
  1312. X
  1313. X    if (rlist == NULL)
  1314. X        return;
  1315. X
  1316. X/* first, see who's logged on locally.  while we're going through the linked */
  1317. X/* list, set all the remote-host entries' flag fields to 0 and all the local */
  1318. X/* ones to 1, to simplify things later. */
  1319. X
  1320. X    for (cur = rlist; cur != NULL; cur = cur->next)
  1321. X        if (cur->local)
  1322. X            if (logged_in(cur->watcher))
  1323. X                cur->flag = 1;
  1324. X            else
  1325. X                cur->flag = -1;
  1326. X        else
  1327. X            cur->flag = 0;
  1328. X
  1329. X/* now go through and look at the remote hosts.  each time we find a remote */
  1330. X/* host, we will connect to it then scan forward in the linked list so that */
  1331. X/* we only have to connect to each host once. */
  1332. X
  1333. X    buf[0] = 'V';
  1334. X
  1335. X    for (place = rlist; place != NULL; place = place->next)
  1336. X    {
  1337. X        if (place->flag)
  1338. X            continue;
  1339. X
  1340. X        port = 1023;
  1341. X        fd = rresvport(&port);
  1342. X        if (fd < 0)
  1343. X            break;
  1344. X
  1345. X/* if we can't connect to a host, toast all entries from that host. */
  1346. X
  1347. X        if (connect(fd, &place->host, sizeof(place->host)))
  1348. X        {
  1349. X            for (cur = place; cur != NULL; cur = cur->next)
  1350. X                if (! cur->flag)
  1351. X                    if (! r_cmp(&place->host, "", "", cur))
  1352. X                        cur->flag = -1;
  1353. X        }
  1354. X
  1355. X/* ask about each watcher on the current host. */
  1356. X
  1357. X        else for (user = place; user != NULL; user = user->next)
  1358. X        {
  1359. X            if (user->flag)
  1360. X                continue;
  1361. X            if (r_cmp(&place->host, "", "", user))
  1362. X                continue;
  1363. X            strcpy(buf+1, user->watcher);
  1364. X            write(fd, buf, strlen(buf)+1);
  1365. X            read(fd, &code, 1);
  1366. X            uflag = code ? 1 : -1;
  1367. X
  1368. X/* toast or keep all the watcher's entries. */
  1369. X
  1370. X            for (cur = user; cur != NULL; cur = cur->next)
  1371. X                if (!(cur->flag || r_cmp(&user->host,
  1372. X                        user->watcher, "", cur)))
  1373. X                    cur->flag = uflag;
  1374. X        }
  1375. X        close(fd);
  1376. X    }
  1377. X
  1378. X/* now go through and delete all the entries that are marked as toast. */
  1379. X
  1380. X    while (rlist != NULL)
  1381. X        if (rlist->flag == -1)
  1382. X        {
  1383. X            cur = rlist;
  1384. X            rlist = rlist->next;
  1385. X            free(cur);
  1386. X        }
  1387. X        else
  1388. X            break;
  1389. X
  1390. X    if (rlist == NULL)
  1391. X        return;
  1392. X
  1393. X    cur = rlist;
  1394. X    while (cur->next != NULL)
  1395. X        if (cur->next->flag == -1)
  1396. X        {
  1397. X            place = cur->next;
  1398. X            cur->next = cur->next->next;
  1399. X            free(place);
  1400. X        }
  1401. X        else
  1402. X            cur = cur->next;
  1403. X}
  1404. X
  1405. X#ifdef DEBUG
  1406. X/*
  1407. X** Dump the ruser list to stdout.
  1408. X*/
  1409. Xdumplist()
  1410. X{
  1411. X    struct ruser *cur;
  1412. X
  1413. X    if (rlist == NULL)
  1414. X        printf("No users in rlist\n");
  1415. X    else
  1416. X        for (cur = rlist; cur != NULL; cur = cur->next)
  1417. X            printf("%s@%s watching %s\n", cur->watcher,
  1418. X                inet_ntoa(cur->host.sin_addr.s_addr),
  1419. X                cur->watchee);
  1420. X}
  1421. X#endif
  1422. X
  1423. Xreap()
  1424. X{
  1425. X    wait(0);
  1426. X}
  1427. X
  1428. Xmain()
  1429. X{
  1430. X    int    fd;
  1431. X    fd_set    myread, other;
  1432. X    struct    timeval timer;
  1433. X
  1434. X    getsocks();
  1435. X
  1436. X    setreuid(0, 0);
  1437. X#ifndef DEBUG
  1438. X    if (fork())
  1439. X        exit(0);
  1440. X    fd = open("/dev/tty", O_RDWR);
  1441. X    ioctl(fd, TIOCNOTTY, 0);
  1442. X    close(fd);
  1443. X    close(0);
  1444. X    close(1);
  1445. X    close(2);
  1446. X#else
  1447. X    signal(SIGQUIT, dumplist);
  1448. X#endif
  1449. X
  1450. X    signal(SIGCHLD, reap);
  1451. X
  1452. X    FD_ZERO(&other);
  1453. X
  1454. X    while (1)
  1455. X    {
  1456. X        timerclear(&timer);
  1457. X        timer.tv_sec = ROLLCALL;
  1458. X        bcopy(&readset, &myread, sizeof(fd_set));
  1459. X
  1460. X        if (! select(FD_SETSIZE, &myread, &other, &other, &timer))
  1461. X            rollcall();
  1462. X        else
  1463. X        {
  1464. X            if (FD_ISSET(udp_fd, &myread))
  1465. X            {
  1466. X                udp();
  1467. X                FD_CLR(udp_fd, &myread);
  1468. X            }
  1469. X            if (FD_ISSET(tcp_fd, &myread))
  1470. X            {
  1471. X                tcp();
  1472. X                FD_CLR(tcp_fd, &myread);
  1473. X            }
  1474. X/* we have to do this in a loop; ffs() won't work for FD_SETSIZE > 32 */
  1475. X            for (fd = 0; fd < FD_SETSIZE; fd++)
  1476. X                if (FD_ISSET(fd, &myread))
  1477. X                    tcpc(fd);
  1478. X        }
  1479. X    }
  1480. X}
  1481. SHAR_EOF
  1482. chmod 0600 comsat.c || echo "restore of comsat.c fails"
  1483. sed 's/^X//' << 'SHAR_EOF' > config.h &&
  1484. X/*
  1485. X** Various pieces of configuration for comsat
  1486. X*/
  1487. X
  1488. X/*
  1489. X** If SECUREBIFF is defined, comsat will only accept datagrams from privileged
  1490. X** (<1024) port numbers.  This is to prevent users from sending "biff bombs"
  1491. X** to each other by sending messages to the server.  Note that /bin/mail may
  1492. X** have to be modified for this to work, as it does not normally use a secure
  1493. X** port to talk to comsat.
  1494. X*/
  1495. X/*#define SECUREBIFF*/
  1496. X
  1497. X/*
  1498. X** Set ROLLCALL to the number of seconds between "roll calls" -- a roll call
  1499. X** checks to see that all the remote users who are watching someone are still
  1500. X** logged in.
  1501. X*/
  1502. X#define ROLLCALL    120
  1503. X
  1504. X/*
  1505. X** These are the default port numbers to use, if we can't find the biff
  1506. X** entry in /etc/services.
  1507. X*/
  1508. X#define UDP_PORT    512
  1509. X#define TCP_PORT    522
  1510. X
  1511. X/*
  1512. X** Mail header fields we print in a mail notification.
  1513. X*/
  1514. Xchar *fields[] = {
  1515. X"From: ",
  1516. X"Subject: ",
  1517. X"To: ",
  1518. X"Date: "
  1519. X};
  1520. X
  1521. X/*
  1522. X** Directory where users' mailboxes are kept.
  1523. X*/
  1524. X#define MAILDIR        "/usr/spool/mail"
  1525. X
  1526. X/*
  1527. X** Location of utmp file.
  1528. X*/
  1529. X#define UTMP        "/etc/utmp"
  1530. X
  1531. X/*
  1532. X** Directory to search for tty devices.
  1533. X*/
  1534. X#define DEVDIR        "/dev"
  1535. X
  1536. X#define Nfields (sizeof(fields)/sizeof(fields[0]))
  1537. SHAR_EOF
  1538. chmod 0600 config.h || echo "restore of config.h fails"
  1539. exit 0
  1540.  
  1541. -- 
  1542. Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
  1543.  
  1544.  
  1545.