home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1996 May / PCW596.iso / wtest / clico / sunsoft / pcnfs51 / src / rfcmsg.taz / rfcmsg / msgserv.c < prev   
Encoding:
C/C++ Source or Header  |  1994-08-27  |  13.9 KB  |  752 lines

  1. /* RE_SID: @(%)/usr/re/builds/pcnfs/unix/rfcmsg/SCCS/s.msgserv.c 1.9 93/12/09 11:51:45 SMI */
  2. /* @(#)msgserv.c    1.9 12/9/93 */
  3. /*
  4.  * msgserv.c
  5.  *
  6.  * Copyright (c) 1991-1993 Sun Microsystems, Inc. 
  7.  *
  8.  * This is a server implementation of the Message Send protocol
  9.  * defined in RFCxxxx. This implementation may be freely
  10.  * copied, modified, and redistributed, provided that this
  11.  * comment and the Sun Microsystems copyright are retained.
  12.  * Anyone installing, modifying, or documenting this
  13.  * software is advised to read the section in the RFC which
  14.  * deals with security issues.
  15.  */
  16. #include <sys/types.h>
  17. #include <sys/time.h>
  18. #include <sys/stat.h>
  19. #include <utmp.h>
  20. #ifdef SVR4
  21. #include <sys/select.h>
  22. #include <sys/resource.h>
  23. #endif
  24. #include <sys/socket.h>
  25. #ifndef SVR4
  26. #include <sys/ttycom.h>
  27. #endif
  28. #include <sys/wait.h>
  29. #include <sys/sockio.h>
  30. #include <netinet/in.h>
  31. #include <net/if.h>
  32. #include <arpa/inet.h>
  33. #include <netdb.h>
  34. #include <stdio.h>
  35. #include <fcntl.h>
  36. #include <string.h>
  37. #include <signal.h>
  38. #include <ctype.h>
  39. #include <time.h>
  40. #include <memory.h>
  41.  
  42. char *prog;
  43. int debug = 0;
  44. int verbose = 0;
  45. int use_console = 0;    /* XXX currently unused */
  46. char *empty_arg = "";
  47.  
  48. char * recipient;
  49. char * recip_term;
  50. char * sender;
  51. char * sender_term;
  52. char * msg_text;
  53. char * cookie;
  54. char * signature;
  55.  
  56. char console[] = "/dev/console";
  57. /* utmp globals */
  58. struct utmp utmp;
  59. FILE *utmp_file;    /* stream for utmp, also non-0 indicates valid entry */
  60.  
  61.  
  62. /*
  63.  * types for all procedures
  64.  */
  65. void usage();
  66. void handle_udp();
  67. void handle_tcp();
  68. int main();
  69. #ifdef SVR4
  70. void reaper();
  71. #else
  72. int reaper();
  73. #endif
  74. void udp_ack();
  75. void ack();
  76. void nak();
  77. char *deliver();
  78. int rip_apart_message();
  79. int check_cache();
  80. void filter();
  81. char *do_cmd();
  82. char * first_utmp_entry();
  83. void next_utmp_entry();
  84. char *send_to_term();
  85.  
  86.  
  87. void
  88. usage()
  89. {
  90.     fprintf(stderr, "usage: %s [-d][-pN]\n", prog);
  91. /* XXX    fprintf(stderr, "usage: %s [-d][-pN][-c]\n", prog); */
  92.     fprintf(stderr, "  -d    - turn on debugging\n");
  93.     fprintf(stderr, "  -pN   - use port N (default: 18)\n");
  94. /* XXX    fprintf(stderr, "  -c    - ignore terminal and use console\n"); */
  95.  
  96. }
  97.  
  98. int
  99. main(argc, argv)
  100. int argc;
  101. char *argv[];
  102. {
  103.  
  104.     short port = 0;
  105.     int tcpsock1;
  106.     int tcpsock2;
  107.     int udpsock;
  108.     int i;
  109.     fd_set ready;
  110.     struct sockaddr_in sin;
  111.     struct servent *sp;
  112.  
  113. #ifdef SVR4
  114.     int nfds;
  115.     struct rlimit rlp;
  116.  
  117.     if (getrlimit(RLIMIT_NOFILE, &rlp) && debug)
  118.         perror("getrlimit");
  119.     nfds = rlp.rlim_cur;
  120. #else
  121.     int nfds = getdtablesize();
  122. #endif
  123.  
  124.  
  125.     prog = *argv++;
  126.     argc--;
  127.  
  128.     /* process options:
  129.      * -d (debug)
  130.      * -pN (use port N instead of 18)
  131.      */
  132.  
  133.     while(argc && *argv[0] == '-') {
  134.         (*argv)++;
  135.         switch (toupper(*argv[0])){
  136.             case 'D':
  137.                 debug++;
  138.                 verbose++;
  139.                 break;
  140.             case 'C':
  141.                 use_console++;
  142.                 break;
  143.             case 'P':
  144.                 (*argv)++;
  145.                 port = atoi(*argv);
  146.                 break;
  147.             default:
  148.                 usage();
  149.                 exit(1);
  150.                 /*NOTREACHED*/
  151.         }
  152.         argv++;
  153.         argc--;
  154.     }
  155.     if(argc != 0) {
  156.         usage();
  157.         exit(1);
  158.         /*NOTREACHED*/
  159.     }
  160.  
  161.     if(!debug) {
  162.         if(fork())
  163.             exit(0);
  164.         for(i = nfds-1; i >= 0; --i)
  165.             close(i);
  166.         (void)open("/dev/null", O_RDWR);
  167.         dup2(0, 1);
  168.         dup2(0, 2);
  169. #ifdef SVR4
  170.         setsid();
  171. #else
  172. /* NB - setsid() also works in SunOS but maybe not other BSD-derived code */
  173.         i = open("/dev/tty", O_RDWR);
  174.         if(i >= 0){
  175.             ioctl(i, TIOCNOTTY, 0);
  176.             close(i);
  177.         }
  178. #endif
  179. /* XXX todo - add code to use SYSLOG if we're not in debug mode */
  180.     }
  181.  
  182. #ifdef SVR4
  183.     /*
  184.      *  use sigset to get BSD semantics
  185.      */
  186.     sigset(SIGCHLD, reaper);
  187. #else !SVR4
  188.     signal(SIGCHLD, reaper);
  189. #endif SVR4
  190.  
  191.     sin.sin_family = AF_INET;
  192.  
  193.     /*
  194.      * compute the port to use: consult /etc/services, but if not
  195.      * found use 18 (from the RFC). the -pN option overrides
  196.      */
  197.  
  198.     if(port == 0) {
  199.         sp = getservbyname("message", "udp");
  200.         if(sp)
  201.             sin.sin_port = sp->s_port;
  202.         else
  203.             sin.sin_port = htons(18);    /* from the RFC */
  204.     }
  205.     else
  206.         sin.sin_port = htons(port);
  207.  
  208.     sin.sin_addr.s_addr = INADDR_ANY;
  209.  
  210.     if(debug) printf("%s: using port %d\n", prog, htons(sin.sin_port));
  211.  
  212.  
  213.  
  214.     tcpsock1 = socket(AF_INET, SOCK_STREAM, 0);
  215.     if(bind(tcpsock1, (struct sockaddr *)&sin, sizeof sin) < 0) {
  216.         if(debug) perror("bind (TCP)");
  217.         exit(99); /* XXX */
  218.     }
  219.     listen(tcpsock1, 5);
  220.  
  221.     udpsock = socket(AF_INET, SOCK_DGRAM, 0);
  222.     if(bind(udpsock, (struct sockaddr *)&sin, sizeof sin) < 0) {
  223.         if(debug) perror("bind (UDP)");
  224.         exit(99); /* XXX */
  225.     }
  226.  
  227.     if(debug) printf("entering main loop...\n");
  228.     while(1) {
  229.         FD_ZERO(&ready);
  230.         FD_SET(udpsock, &ready);
  231.         FD_SET(tcpsock1, &ready);
  232.         i = select(nfds, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
  233.         if(debug) printf("----------------------------------------------------------\nselect returned %d\n", i);
  234.         if(i <= 0) {
  235.             if(debug)perror("select");
  236.             continue;
  237.         }
  238.         if(FD_ISSET(udpsock, &ready))
  239.             handle_udp(udpsock);
  240.         else if(FD_ISSET(tcpsock1, &ready)) {
  241.             tcpsock2 = accept(tcpsock1, (struct sockaddr *)0,
  242.                 (int *)0);
  243.  
  244.             if(debug)
  245.                 printf("forking....\n");
  246.  
  247.             if(fork() == 0) {
  248.                 close(tcpsock1);
  249.                 close(udpsock);
  250.                 handle_tcp(tcpsock2);
  251.                 exit(0);
  252.             }
  253.         }
  254.     }
  255. }
  256.  
  257. #define CACHE_ENTRIES 32
  258.  
  259. struct mc_entry {
  260.     struct sockaddr_in mc_addr;
  261.     char mc_cookie[48];
  262. };
  263.  
  264. int mc_used = 0;
  265. int mc_next = 0;
  266. struct mc_entry mcache[CACHE_ENTRIES];
  267.  
  268. int
  269. check_cache(cookie, addrp)
  270. char *cookie;
  271. struct sockaddr_in *addrp;
  272. {
  273.     int i;
  274.     if(mc_used) {
  275.         for (i = 0; i < mc_used; i++) {
  276.             if(!strcmp(cookie, mcache[i].mc_cookie) &&
  277.                !memcmp((char *)addrp, (char *)&mcache[i].mc_addr,
  278.                 sizeof(*addrp)))
  279.                 return(1);
  280.         }
  281.     }
  282.     memcpy((char *)&mcache[mc_next].mc_addr, (char *)addrp, sizeof(*addrp));
  283.     strcpy(mcache[mc_next].mc_cookie, cookie);
  284.  
  285.     mc_next++;
  286.     if(mc_next > mc_used) mc_used = mc_next;
  287.     mc_next = mc_next % CACHE_ENTRIES;
  288.     return(0);
  289. }
  290.  
  291. void
  292. handle_udp(s)
  293. int s;
  294. {
  295.     char buff[1024];
  296.     int buflen;
  297.     struct sockaddr_in from;
  298.     int fromlen;
  299.     char *txt;
  300.  
  301.     fromlen = sizeof (from);
  302.  
  303.     if(debug) printf("%s: udp msg received\n", prog);
  304.  
  305.     buflen = recvfrom(s, buff, 1024, 0,
  306.          (struct sockaddr *)&from,  &fromlen);
  307.  
  308.     if(buflen < 0) {
  309.         perror("recvfrom");
  310.         return;
  311.     }
  312.     if(rip_apart_message(buff, buflen)) {
  313.         fprintf(stderr, "%s: malformed message\n", prog);
  314.         return;
  315.     }
  316.     if(check_cache(cookie, &from)) {
  317.         if(debug) printf("duplicate message\n");
  318.         return;
  319.     }
  320.  
  321.     if(debug)
  322.         printf("forking....\n");
  323.  
  324.     if(!debug) {
  325.         if(fork() != 0)
  326.             return;
  327.     }
  328.  
  329.     if((txt = deliver()) == NULL && *recipient) {
  330.         udp_ack(s, &from, "OK");
  331.     }
  332.     if(!debug)
  333.         exit(0);
  334. }
  335.  
  336. void
  337. handle_tcp(s)
  338. int s;
  339. {
  340.     char buff[1024];
  341.     int buflen;
  342.     struct sockaddr_in peer;
  343.     int peerlen;
  344.     char *txt;
  345.  
  346.     if(debug) printf("%s: tcp msg received\n", prog);
  347.  
  348.     peerlen = sizeof peer;
  349.     if(getpeername(s, (struct sockaddr *)&peer, &peerlen) < 0) {
  350.         perror("getpeername");
  351.         exit(99);
  352.     }
  353.  
  354.     buflen = read(s, buff, 1024);
  355.     if(buflen < 0) {
  356.         perror("read");
  357.         nak(s, "Read error");
  358.         return;
  359.     }
  360.     if(rip_apart_message(buff, buflen)) {
  361.         fprintf(stderr, "%s: malformed message\n", prog);
  362.         nak(s, "Message format error");
  363.         return;
  364.     }
  365.     if((txt = deliver()) != NULL) {
  366. #ifdef notyet
  367.         nak(s, txt);
  368.         return;
  369. #endif
  370.     }
  371.     ack(s, "OK");
  372. }
  373.  
  374.  
  375. /* Note the type difference here */
  376.  
  377. #ifdef SVR4
  378. void
  379. reaper()
  380. {
  381.     int i, j;
  382.     i = wait(&j);
  383.     return;
  384. }
  385. #else
  386. int
  387. reaper()
  388. {
  389.     union wait status;
  390.     while(wait3(&status, WNOHANG, 0) > 0) continue;
  391.     return(0);
  392. }
  393. #endif
  394.  
  395. void udp_ack(s, to, msg)
  396. int s;
  397. struct sockaddr_in *to;
  398. char *msg;
  399. {
  400.     char buff[128];
  401.     if(debug)
  402.         printf("sending ack\n");
  403.     sprintf(buff, "+%s", msg);
  404.     (void)sendto(s, buff, strlen(buff) + 1, 0,
  405.         (struct sockaddr *)to, sizeof (*to));
  406. }
  407.  
  408. void ack(s, msg)
  409. int s;
  410. char *msg;
  411. {
  412.     char buff[128];
  413.     if(debug)
  414.         printf("sending ack\n");
  415.     sprintf(buff, "+%s", msg);
  416.     (void)write(s, buff, strlen(buff) + 1);
  417. }
  418.  
  419. void nak(s, msg)
  420. int s;
  421. char *msg;
  422. {
  423.     char buff[128];
  424.     if(debug)
  425.         printf("sending nak\n");
  426.     sprintf(buff, "-%s", msg);
  427.     (void)write(s, buff, strlen(buff) + 1);
  428. }
  429.  
  430. int
  431. rip_apart_message(buff, buflen)
  432. char *buff;
  433. int buflen;
  434. {
  435.     char *cp1;
  436.     char *cp2;
  437.     char *lim;
  438.  
  439.  
  440.     recipient = NULL;
  441.     recip_term = NULL;
  442.     sender = NULL;
  443.     sender_term = NULL;
  444.     msg_text = NULL;
  445.     cookie = NULL;
  446.     signature = NULL;
  447.  
  448.     if(buff[0] != 'B')
  449.         return(1);
  450.  
  451.     lim = &buff[buflen];
  452.  
  453.     cp1 = buff;
  454.     cp1++;                /* point at recipient */
  455.  
  456. /*
  457.  * Gather up recipient
  458.  */
  459.     cp2 = cp1;
  460.     while (*cp2 && cp2 < lim)
  461.         cp2++;
  462.     if(cp2 >= lim) return(1);    /* over-length */
  463.     recipient = cp1;
  464.     cp1 = cp2;
  465.     cp1++;
  466.     if(debug)
  467.         printf("recipient   = '%s'\n", recipient);
  468.  
  469.  
  470.  
  471. /*
  472.  * Gather up recip_term
  473.  */
  474.     cp2 = cp1;
  475.     while (*cp2 && cp2 < lim)
  476.         cp2++;
  477.     if(cp2 >= lim) return(1);    /* over-length */
  478.     recip_term = cp1;
  479.  
  480.     /* toss preceding "/dev/" if any */
  481.     if (strncmp(recip_term, "/dev/", strlen("/dev/")) == 0)
  482.         recip_term += strlen("/dev/");
  483.     cp1 = cp2;
  484.     cp1++;
  485.     if(debug)
  486.         printf("recip_term  = '%s'\n", recip_term);
  487.  
  488.  
  489. /*
  490.  * Gather up msg_text
  491.  */
  492.     cp2 = cp1;
  493.     while (*cp2 && cp2 < lim)
  494.         cp2++;
  495.     if(cp2 >= lim) return(1);    /* over-length */
  496.     msg_text = cp1;
  497.     cp1 = cp2;
  498.     cp1++;
  499.     if(debug)
  500.         printf("msg_text    = '%s'\n", msg_text);
  501.  
  502.  
  503. /*
  504.  * Gather up sender
  505.  */
  506.     cp2 = cp1;
  507.     while (*cp2 && cp2 < lim)
  508.         cp2++;
  509.     if(cp2 >= lim) return(1);    /* over-length */
  510.     sender = cp1;
  511.     cp1 = cp2;
  512.     cp1++;
  513.     if(debug)
  514.         printf("sender      = '%s'\n", sender);
  515.  
  516.  
  517. /*
  518.  * Gather up sender_term
  519.  */
  520.     cp2 = cp1;
  521.     while (*cp2 && cp2 < lim)
  522.         cp2++;
  523.     if(cp2 >= lim) return(1);    /* over-length */
  524.     sender_term = cp1;
  525.     cp1 = cp2;
  526.     cp1++;
  527.     if(debug)
  528.         printf("sender_term = '%s'\n", sender_term);
  529.  
  530.  
  531. /*
  532.  * Gather up cookie
  533.  */
  534.     cp2 = cp1;
  535.     while (*cp2 && cp2 < lim)
  536.         cp2++;
  537.     if(cp2 >= lim) return(1);    /* over-length */
  538.     cookie = cp1;
  539.     cp1 = cp2;
  540.     cp1++;
  541.     if(debug)
  542.         printf("cookie      = '%s'\n", cookie);
  543.  
  544. /*
  545.  * Gather up signature
  546.  */
  547.     cp2 = cp1;
  548.     while (*cp2 && cp2 < lim)
  549.         cp2++;
  550.     if(cp2 >= lim) return(1);    /* over-length */
  551.     signature = cp1;
  552.     cp1 = cp2;
  553.     cp1++;
  554.     if(debug)
  555.         printf("signature   = '%s'\n", signature);
  556.  
  557.     return(0);
  558. }
  559.  
  560.  
  561. /*
  562.  * delivers the message; returns NULL if OK, otherwise
  563.  * a string describing the problem
  564.  */    
  565. char *deliver()
  566. {
  567.     char *cp;
  568.     int only_one;
  569.     char *retval;
  570.     int user_seen = 0, msgs_sent = 0;
  571.  
  572.     while(cp = strchr(msg_text, '\015'))
  573.         *cp = ' ';
  574.  
  575.     filter(msg_text);
  576.     if(debug) printf("delivering message....\n");
  577.  
  578.     /* set only_one to false only if recip_term is "*" */
  579.     only_one = 1;
  580.     if (strcmp(recip_term, "*") == 0) {
  581.         only_one = 0;
  582.     }
  583.  
  584.     /* go through utmp entries, sending to appropriate ones */
  585.     if (retval = first_utmp_entry())
  586.         return(retval);
  587.  
  588.     do {
  589.         if (debug) printf("evaluating utmp entry %s %s...\n",
  590.             utmp.ut_name, utmp.ut_line);
  591.         /* check for wrong recipient */
  592.         if (*recipient) {
  593.             if (strncmp(recipient, utmp.ut_name,
  594.                     sizeof(utmp.ut_name))) {
  595.                 continue;
  596.             }
  597.  
  598.             user_seen++;
  599.         }
  600.         else {
  601.             /* no recipient; if no term force console */
  602.             if (*recip_term == '\0') {
  603.                 if (strncmp("console", utmp.ut_line,
  604.                         sizeof(utmp.ut_line))) {
  605.                     /* nope, wrong term */
  606.                     continue;
  607.                 }
  608.             }
  609.         }
  610.  
  611.         /* check for wrong term */
  612.         if (*recip_term) {
  613.             /* specific term or "*" */
  614.             if (strcmp(recip_term, "*")) {
  615.                 /* specific term */
  616.                 if (strncmp(recip_term, utmp.ut_line,
  617.                         sizeof(utmp.ut_line))) {
  618.                     /* nope, wrong term */
  619.                     continue;
  620.                 }
  621.             }
  622.         }
  623.  
  624.         /* passed all tests, send it */
  625.         msgs_sent++;
  626.         retval = send_to_term(utmp.ut_line);
  627.  
  628.         /* see if once is enough */
  629.         if (only_one && (retval == NULL))
  630.             break;
  631.  
  632.     } while (next_utmp_entry(), utmp_file);    /* keep going if more entries */
  633.  
  634.     /*
  635.      *  check if user matched entry in /etc/utmp, but no message
  636.      *  sent.  Send the message to the console rather than not
  637.      *  displaying message at all.
  638.      */
  639.     if (user_seen && (msgs_sent == 0))
  640.         retval = send_to_term ("console");
  641.  
  642.     return(NULL);
  643. }
  644.  
  645. /*
  646.  * first_utmp_entry
  647.  *
  648.  * opens utmp, calls next_utmp_entry and returns NULL if open is successful;
  649.  * returns error string if open fails.
  650.  */
  651. char *
  652. first_utmp_entry()
  653. {
  654.     memset((char *) &utmp, 0, sizeof(utmp));
  655.     utmp_file = fopen("/etc/utmp", "r");
  656.     if(utmp_file == NULL) {
  657.         perror("fopen /etc/utmp");
  658.         return("unable to open /etc/utmp\n");
  659.     }
  660.     next_utmp_entry();
  661.     return NULL;
  662. }
  663.  
  664. /*
  665.  * next_utmp_entry
  666.  *
  667.  * sets up next utmp entry with utmp_ok != 0,
  668.  * closes file and utmp_ok == 0 if none.
  669.  */
  670. void
  671. next_utmp_entry()
  672. {
  673.     while (fread((char *) &utmp, sizeof(utmp), 1, utmp_file) == 1) {
  674.         if (*utmp.ut_line && *utmp.ut_name) {
  675.             return;        /* utmp_file is non-NULL */
  676.         }
  677.     }
  678.     /* if we get here, we're at eof; close & zero utmp_file */
  679.     (void) fclose(utmp_file);
  680.     utmp_file = NULL;    /* indicates no more entries */
  681. }
  682.  
  683. char *
  684. send_to_term(term)
  685.     char *term;
  686. {
  687.     /*
  688.      * write to specific terminal if any, else console
  689.      */
  690.     char device[32];
  691.     struct stat statbuf;
  692.     int rc;
  693.     FILE *stream;
  694.     time_t aclock;
  695.  
  696.     sprintf(device, "/dev/%s", term);
  697.     if (stat(device, &statbuf)) {
  698.         perror("stat");
  699.         return("unable to stat %s", device);
  700.     }
  701.     
  702.     if (! (statbuf.st_mode & (S_IWGRP | S_IWOTH))) {
  703.         if (debug) printf("won't write to %s because mode is %o\n",
  704.             device, statbuf.st_mode);
  705.         return("won't write because of mode");
  706.     }
  707.  
  708.     stream = fopen(device, "w");
  709.     if(stream == NULL) {
  710.         perror("fopen");
  711.         return("unable to write to %s", device);
  712.     }
  713.     time(&aclock);
  714.     if(debug) printf("writing to %s\n", device);
  715.     fprintf(stream,
  716.         "\7\n---------\nConsole message from %s on %s%s---------\n",
  717.         sender, asctime(localtime(&aclock)), msg_text);
  718.     rc = fclose(stream);
  719.     if (rc) {
  720.         perror("fclose");
  721.         return("unable to close %s", device);
  722.     }
  723.     if(debug) printf("delivery successful\n");
  724.     return NULL;
  725. }
  726.  
  727. /*
  728.  * As noted in the RFC, it is important to filter out control
  729.  * chracters and suchlike, since there may exist terminals
  730.  * which will behave in bizarre and security-violating ways
  731.  * if presented with certain control code sequences. The
  732.  * client may also be filtering messages, but it is incumbent
  733.  * upon a well-written server implementation not to rely on being
  734.  * sent only "clean" messages.
  735.  *
  736.  * It is an open question as to how the filtering should be done.
  737.  * One approach might be to squeeze out any invalid characters
  738.  * silently. This would make debugging difficult. This implementation
  739.  * replaces all non-printable characters with '?' characters.
  740.  */
  741.  
  742. void
  743. filter(text)
  744. char *text;
  745. {
  746.     while (*text) {
  747.         if(!isprint(*text) && !isspace(*text))
  748.             *text = '?';
  749.         text++;
  750.     }
  751. }
  752.