home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / OPENSTEP / Networking / msend-3.3-I / msend.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-26  |  21.1 KB  |  895 lines

  1. /*
  2.  * msend.c
  3.  *
  4.  * Copyright (c) 1993 Zik Saleeba <zik@zikzak.apana.org.au>
  5.  * Copyright (c) 1993 Andrew Herbert <andrew@werple.apana.org.au>
  6.  * Copyright (c) 1992 Sun Microsystems, Inc. 
  7.  *
  8.  * This is a client implementation of the Message Send protocol
  9.  * defined in RFC1312. 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.  * Author: Geoff.Arnold@east.sun.com
  17.  * Modified by: David Barr <barr@darwin.psu.edu>
  18.  * Modified by: Andrew Herbert <andrew@werple.apana.org.au>
  19.  * Modified by: Zik Saleeba <zik@zikzak.apana.org.au>
  20.  */
  21.  
  22. /* $Id: msend.c,v 1.2 1997/10/26 08:41:14 lukeh Exp $ */
  23.  
  24. #include "common.h"
  25. #include "msend.h"
  26.  
  27. #ifdef MSEND_APP
  28. extern void mslog(char *message, ...);
  29. #define printf mslog
  30. #endif
  31.  
  32. char    *prog;            /* points to program name for messages */
  33. int    debug = 0;
  34. int    verbose = 0;
  35. char    *empty_arg = "";    /* used to encode an empty message part */
  36.  
  37. int    msg_len = 0;        /* the cumulative length of the message */
  38. char    *msg;            /* message assembly buffer */
  39.  
  40. #define INTERFACES 32        /* for broadcasting */
  41. int if_n = 0;
  42. struct sockaddr_in if_a[INTERFACES];
  43.  
  44.  
  45. void
  46. usage()
  47. {
  48.     fprintf(stderr, "Usage: %s [-t][-d][-v][-rN][-pN] recipient ['message']  - send message\n", prog);
  49.     fprintf(stderr, "   or: %s -lM           - show the last M messages (default 5)\n", prog);
  50.     fprintf(stderr, "   or: %s -c            - check for unread messages\n", prog);
  51.     fprintf(stderr, "   or: %s -u            - show unread messages\n", prog);
  52.     fprintf(stderr, "   or: %s -sN           - shorten buffer to N messages (default: 20)\n", prog);
  53.     fprintf(stderr, "   or: %s -eN           - expire old messages for all users (def. 20 msgs)\n", prog);
  54.     fprintf(stderr, "             -t    - use TCP (stream) connection - ignored for broadcasts\n");
  55.     fprintf(stderr, "             -g    - use UDP (dataGram) transmission (default)\n");
  56.     fprintf(stderr, "             -b    - broadcast to hosts on local network\n");
  57.     fprintf(stderr, "             -v    - verbose mode\n");
  58.     fprintf(stderr, "             -rN   - UDP retransmissions (default: 4)\n");
  59.     fprintf(stderr, "             -pN   - use port N (default: 18)\n");
  60.     fprintf(stderr, "             -d    - turn on debugging (implies -v)\n");
  61.     fprintf(stderr, "\nRecipient may have any of the following forms:\n");
  62.     fprintf(stderr, "     user           - send to user on the same machine\n");
  63.     fprintf(stderr, "     user@host      - directed to user at given host\n");
  64.     fprintf(stderr, "     user:term@host - user logged in to term on host\n");
  65.     fprintf(stderr, "     :term@host     - specified terminal on host\n");
  66.     fprintf(stderr, "     :all@host      - all terminals on given host\n");
  67.     fprintf(stderr, "     @host          - console at given host\n");
  68. }
  69.  
  70.  
  71. void collect_args(options, numargs, argc, argv)
  72.     char *(**options)[];
  73.     int *numargs;
  74.     int argc;
  75.     char *argv[];
  76. {
  77.     int argcount;
  78.     char *pos;
  79.     char *env_args;
  80.     int argccount;
  81.  
  82.     /* get the environment args */
  83.     env_args = (char *)getenv("MSENDOPTS");
  84.  
  85.     if (env_args != NULL) {
  86.         *numargs = 0;
  87.  
  88.         /* do a quick scan to approximately count the arguments */
  89.         argcount = 1;
  90.         for (pos = env_args; *pos != '\0'; pos++) {
  91.             if (*pos == ' ')
  92.                 argcount++;
  93.         }
  94.         *options = (char *(*)[])malloc(sizeof(char *) * (argcount+argc));
  95.  
  96.         /* ok, now do it properly */
  97.  
  98.         /* skip leading spaces */
  99.         pos = env_args;
  100.         while (*pos == ' ')
  101.             pos++;
  102.  
  103.         /* collect each arg */
  104.         while (*pos != '\0') {
  105.             (**options)[(*numargs)++] = pos;
  106.             while (*pos != ' ' && *pos != '\0')
  107.                 pos++;
  108.             if (*pos == ' ') {
  109.                 *pos++ = '\0';
  110.  
  111.                 /* ignore trailing spaces */
  112.                 while (*pos == ' ')
  113.                     pos++;
  114.             }
  115.         }
  116.         /* add the argv args */
  117.         for (argccount = 0; argccount < argc; argccount++)
  118.             (**options)[(*numargs)++] = argv[argccount];
  119.     }
  120.     else {
  121.         /* just use argv */
  122.         *options = (char *(*)[])argv;
  123.         *numargs = argc;
  124.     }
  125. }
  126.  
  127. #ifndef MSEND_APP
  128. int
  129. main(argc, argv)
  130.     int    argc;
  131.     char    *argv[];
  132. {
  133.     int    use_tcp = 0;
  134.     int    retries = 4;
  135.     short    port = 0;
  136.     int    broadcasting = 0;
  137.     int    shorten = 20;
  138.     int    doshorten = 0;
  139.  
  140.     char    *recipient;
  141.     char    *user;
  142.     char    *term;
  143.     char    *host;
  144.     char    *msg_text;
  145.  
  146.     struct sockaddr_in sin;
  147.     struct    servent *sp;
  148.     struct    hostent *hp;
  149.     char     local_name[MAXHOSTNAMELEN];
  150.     char    *(*options)[];
  151.     int    numargs;
  152.     int    argcount;
  153.  
  154.  
  155.     prog = *argv++;
  156.     argc--;
  157.     collect_args(&options, &numargs, argc, argv);
  158.  
  159.     /* process options:
  160.      * -d (debug)
  161.      * -t (use TCP)
  162.      * -b (broadcast)
  163.      * -g (dataGram)
  164.      * -rN (set retransmission count)
  165.      * -pN (use port N instead of 18)
  166.      * -lN (review messages)
  167.      * -u  (unread messages)
  168.      * -sN (shorten buffer)
  169.      * -eN (expire)
  170.      * -c  (check unread)
  171.      */
  172.  
  173.     argcount = 0;
  174.     while(numargs && *(*options)[argcount] == '-') {
  175.         (*options)[argcount]++;
  176.         switch (toupper(*(*options)[argcount])){
  177.             case 'D':
  178.                 debug++;
  179.                 verbose++;
  180.                 break;
  181.             case 'V':
  182.                 verbose++;
  183.                 break;
  184.             case 'T':
  185.                 use_tcp = 1;
  186.                 break;
  187.             case 'R':
  188.                 (*options)[argcount]++;
  189.                 retries = atoi((*options)[argcount]);
  190.                 break;
  191.             case 'P':
  192.                 (*options)[argcount]++;
  193.                 port = atoi((*options)[argcount]);
  194.                 break;
  195.             case 'B':
  196. #ifdef NO_BROADCAST
  197.                 fprintf(stderr, "Sorry, broadcast not available on this machine - not broadcasting.\n");
  198. #else
  199.                 broadcasting = 1;
  200.                 use_tcp = 0;
  201. #endif
  202.                 break;
  203.             case 'G':
  204.                 use_tcp = 0;
  205.                 break;
  206.             case 'L':
  207.                 (*options)[argcount]++;
  208.                 last_message(atoi((*options)[argcount]), shorten);
  209.                 exit(0);
  210.                 break;
  211.             case 'U':
  212.                 unread_message(shorten);
  213.                 exit(0);
  214.                 break;
  215.             case 'C':
  216.                 exit(check_unread());
  217.                 break;
  218.             case 'S':
  219.                 (*options)[argcount]++;
  220.                 shorten = atoi((*options)[argcount]);
  221.                 if (shorten <= 0)
  222.                     shorten = 20;
  223.                 doshorten = 1;
  224.                 break;
  225.             case 'E':
  226.                 (*options)[argcount]++;
  227.                 expire(atoi((*options)[argcount]));
  228.                 exit(0);
  229.                 break;
  230.             default:
  231.                 usage();
  232.                 exit(1);
  233.                 /*NOTREACHED*/
  234.         }
  235.         argcount++;
  236.         numargs--;
  237.     }
  238.  
  239.     /* shorten if we need to */
  240.     if (doshorten) {
  241.         makeshort(shorten);
  242.         exit(0);
  243.     }
  244.  
  245.     if((numargs < 1) || (numargs > 2)) {
  246.         usage();
  247.         exit(1);
  248.         /*NOTREACHED*/
  249.     }
  250.  
  251.     /*
  252.      * Rip apart the recipient field and set the user, term,
  253.      * and host pointers.
  254.      */
  255.     recipient = (*options)[argcount];
  256.  
  257.     msg_text = numargs == 2 ? (*options)[argcount+1] : NULL;
  258.  
  259.     if(debug) printf("recipient is '%s'\n", recipient);
  260.  
  261.     host = (char *)STRCHR(recipient, '@');
  262.     if(host == NULL)
  263.         host = empty_arg;
  264.     else
  265.         *host++ = '\0';
  266.  
  267.     term = (char *)STRCHR(recipient, ':');
  268.     if(term == NULL)
  269.         term = empty_arg;
  270.     else
  271.         *term++ = '\0';
  272.     if(!strcmp(term, "all"))    /* external form is "all" */
  273.         strcpy(term, "*");        /* protocol uses "*" */
  274.  
  275.     user = recipient;
  276.  
  277.     if(debug) printf("user = '%s', term='%s', host = '%s'\n",
  278.         user, term, host);
  279.  
  280.     if (host[0] == '\0') {
  281. #ifdef USE_SYSINFO
  282.         sysinfo(SI_HOSTNAME, local_name, sizeof(local_name));
  283. #else /* USE_SYSINFO */
  284.         gethostname(local_name, sizeof(local_name));
  285. #endif /* USE_SYSINFO */
  286.         host = local_name;
  287.     }
  288.  
  289.     sin.sin_family = AF_INET;
  290.  
  291.     /*
  292.      * compute the port to use: consult /etc/services, but if not
  293.      * found use 18 (from the RFC). the -pN option overrides
  294.      */
  295.  
  296.     if(port == 0) {
  297.         sp = getservbyname("message", (use_tcp ? "tcp" : "udp"));
  298.         if(sp)
  299.             sin.sin_port = sp->s_port;
  300.         else
  301.             sin.sin_port = htons(18);    /* from the RFC */
  302.     }
  303.     else
  304.         sin.sin_port = htons(port);
  305.  
  306.  
  307.     if(debug) printf("using port %d\n", htons(sin.sin_port));
  308.  
  309.     /*
  310.      * check to see if we're broadcasting. otherwise build an address for
  311.      * the designated host
  312.      */
  313.  
  314.     if(!broadcasting) {
  315.         hp = gethostbyname(host);
  316.         if(hp == NULL) {
  317.             /* XXX need to add stuff to handle dotted IP addresses */
  318.             fprintf(stderr, "%s: unknown host: %s\n", prog, host);
  319.             exit(2);
  320.         }
  321.         MEMCPY((char *)&sin.sin_addr, (char *)hp->h_addr, hp->h_length);
  322.     }
  323.  
  324.     /*
  325.      * now assemble the message. note that this procedure will only
  326.      * return if the assembly is successful
  327.      */
  328.     assemble_message(user, term, msg_text);
  329.  
  330.     /*
  331.      * if broadcast, invoke broadcast_msg
  332.      */
  333.     if(broadcasting) {
  334.         udp_msg(&sin, retries, 1);
  335.         exit(0);
  336.         /*NOTREACHED*/
  337.     }
  338.  
  339.  
  340.     /*
  341.      * if requested, attempt to send message via TCP
  342.      */
  343.     if(use_tcp)
  344.         tcp_msg(&sin);
  345.     /*
  346.      * If tcp_msg returns, it means that it was unable to bind
  347.      * to the port on the server. In this case, revert to UDP.
  348.      */
  349.     udp_msg(&sin, retries, 0);
  350.     exit(0);
  351. }
  352. #endif
  353.  
  354.  
  355. /*
  356.  * append a string to a message buffer, extra = 1 if we want
  357.  * to add a trailing null-terminator into the message as well.
  358.  * expands the buffer as necessary.
  359.  */
  360.  
  361. void 
  362. append_buffer(buffer, bufsize, msglen, addstr, extra)
  363.     char **buffer;
  364.     int *bufsize;
  365.     int *msglen;
  366.     char *addstr;
  367.     int extra;
  368. {
  369.     int addlen;
  370.  
  371.     /* expand the buffer */
  372.     addlen = strlen(addstr) + extra; 
  373.     if (*msglen + addlen > *bufsize) {
  374.         /* get more space */
  375.         *bufsize *= 2;
  376.         *buffer = realloc(*buffer, *bufsize);
  377.         if (*buffer == NULL) {
  378.             fprintf(stderr, "Out of memory.\n");
  379.             exit(1);
  380.         }
  381.     }
  382.  
  383.     strcpy(*buffer + *msglen, addstr); 
  384.     *msglen += addlen;
  385. }
  386.  
  387.  
  388. /*
  389.  * assemble_message assembles a complete message in the buffer
  390.  * msg and stores the length in msg_len. Note that, as defined
  391.  * by the RFC, the message buffer includes embedded nulls.
  392.  */
  393.  
  394. void
  395. assemble_message(user, term, msg_text)
  396.     char    *user;
  397.     char    *term;
  398.     char    *msg_text;
  399. {
  400.     char    *r;
  401.     char    linebuff[256];
  402.     char    *dp;
  403.     int    buflen;
  404.     FILE    *sigfile;
  405.     char    *signature;
  406.     char    *homedir;
  407.     struct passwd *pwd;
  408.  
  409.     /* make a buffer */
  410.     buflen = 1024;
  411.     msg = malloc(buflen);
  412.     if (msg == NULL) {
  413.         fprintf(stderr, "Out of memory.\n");
  414.         exit(1);
  415.     }
  416.  
  417.     /* make the message */
  418.     *msg = 'B';    /* per RFC */
  419.     msg_len = 1;
  420.  
  421.     filter(user);
  422.     append_buffer(&msg, &buflen, &msg_len, user, 1);
  423.     filter(term);
  424.     append_buffer(&msg, &buflen, &msg_len, term, 1);
  425.  
  426.     if (msg_text)
  427.         do_line(&msg, &buflen, &msg_len, msg_text);
  428.     else {
  429.         int is_tty = isatty(0);
  430.  
  431.         linebuff[sizeof(linebuff)-1] = '\0';
  432.         if (is_tty) {
  433.         puts("Enter your message (blank line to end):");
  434. #ifdef USE_READLINE
  435.         while ((r = readline("> ")) != NULL && *r != '\0') {
  436.             add_history(r);
  437.                 do_line(&msg, &buflen, &msg_len, r);
  438.         }
  439. #else
  440.         putchar('>');
  441.         putchar(' ');
  442.         /* eof or empty line terminates */
  443.         while(((r = fgets(linebuff, sizeof(linebuff)-1, stdin)) != NULL) &&
  444.             linebuff[1]) {
  445.                 do_line(&msg, &buflen, &msg_len, linebuff);
  446.             putchar('>'); putchar(' ');
  447.         }
  448. #endif
  449.         if (r == NULL)
  450.             /* we must have finished with ctrl-d */
  451.             putchar('\n');    /* newline for style */
  452.         }
  453.         else
  454.         while(fgets(linebuff, sizeof(linebuff)-1, stdin) != NULL)
  455.                 do_line(&msg, &buflen, &msg_len, linebuff);
  456.     }
  457.     
  458.     append_buffer(&msg, &buflen, &msg_len, "", 1);
  459.  
  460. #ifndef BUGGY_LOGNAME
  461.     dp = (char *)getlogin();
  462.     if (dp == NULL || *dp == '\0')
  463. #endif
  464.         {
  465.         struct passwd *me;
  466.         me = getpwuid(getuid());
  467.         if (me == NULL) {
  468.         fprintf(stderr, "You don't exist - go away!\n");
  469.         exit(1);
  470.         }
  471.         dp = me->pw_name;
  472.     }
  473.  
  474.     append_buffer(&msg, &buflen, &msg_len, dp, 1);
  475.     if(debug) printf("sender username is '%s'\n", (dp ? dp : empty_arg));
  476.     dp = (char *)ttyname(2); /* check standard error */
  477.     if (dp == NULL) {
  478.         append_buffer(&msg, &buflen, &msg_len, empty_arg, 1);
  479.     } else {
  480.         append_buffer(&msg, &buflen, &msg_len, dp, 1);
  481.     }
  482.     if(debug) printf("sender terminal is '%s'\n", (dp ? dp : empty_arg));
  483.     
  484.     sprintf(linebuff, "%ld", time(NULL));
  485.     append_buffer(&msg, &buflen, &msg_len, linebuff, 1);
  486.     if(debug) printf("cookie is '%s'\n", linebuff);
  487.  
  488.     /*
  489.      * Generate a signature from $HOME/.msgsig
  490.      */
  491.  
  492.     homedir = (char *)getenv("HOME");
  493.     if (homedir == NULL) {
  494.         pwd = (struct passwd *)getpwnam(dp);
  495.         homedir = pwd->pw_dir;
  496.     }
  497.     sprintf(linebuff, "%s/.msgsig", homedir);
  498.     signature = empty_arg;
  499.     sigfile = fopen(linebuff, "r");
  500.     if (sigfile != NULL) {
  501.         linebuff[sizeof(linebuff)-1] = '\0';
  502.         if (fgets(linebuff, sizeof(linebuff)-1, sigfile) != NULL) {
  503.             if (linebuff[strlen(linebuff)-1] == '\n')
  504.                 linebuff[strlen(linebuff)-1] = '\0';
  505.             filter(linebuff);
  506.             signature = linebuff;
  507.         }
  508.         fclose(sigfile);
  509.     }
  510.  
  511.     if(debug) printf("signature is '%s'\n", signature);
  512.     append_buffer(&msg, &buflen, &msg_len, signature, 1);
  513.  
  514.     if(debug) printf("total message length is %d\n", msg_len);
  515. }
  516.  
  517. void
  518. do_line(buffer, bufsize, msglen, linebuff)
  519.     char **buffer;
  520.     int *bufsize;
  521.     int *msglen;
  522.     char *linebuff;
  523. {
  524.     int    addlen;
  525.  
  526.     filter(linebuff);
  527.     addlen = strlen(linebuff);
  528.     if (addlen > 0 && linebuff[addlen-1] == '\n')
  529.         linebuff[addlen-1] = '\0'; /* minus the terminating LF */
  530.     append_buffer(buffer, bufsize, msglen, linebuff, 0);
  531.     append_buffer(buffer, bufsize, msglen, "\r\n", 0);
  532. }
  533.  
  534. /*
  535.  * tcp_msg(sp)
  536.  *      sp points at a sockaddr_in which contains the
  537.  *      port to be used.
  538.  *
  539.  * Send the assembled message using TCP. If the attempt is
  540.  * successful, the program exits with a status of 0. If a client-
  541.  * side error occurs (e.g. unable to open a socket) the program
  542.  * exits with a positive non-zero status. If it proves impossible
  543.  * to connect to the server, an error message is displayed and
  544.  * the procedure returns. This allows the caller to retry using
  545.  * UDP.
  546.  */
  547.      
  548. void
  549. tcp_msg (sp)
  550.     struct sockaddr_in *sp;
  551. {
  552.     int    s;
  553.     struct    sockaddr_in sin;
  554.     char    rcvbuf[256];
  555.     int    rval;
  556.  
  557.     if(debug) printf("invoked tcp_msg()\n");
  558.  
  559.     if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  560.         fprintf(stderr, "%s: unable to open socket.\n", prog);
  561.         perror("Reason");
  562.         exit(3);
  563.     }
  564.  
  565.     sin.sin_family = AF_INET;
  566.     sin.sin_addr.s_addr = htonl(INADDR_ANY);
  567.     sin.sin_port = htons(0);
  568.  
  569.     if(BIND(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
  570.         fprintf(stderr, "%s: unable to set local socket address.\n", prog);
  571.         perror("Reason");
  572.         exit(3);
  573.     }
  574.  
  575.     if(connect(s, (struct sockaddr *)sp, sizeof (*sp)) < 0) {
  576.         fprintf(stderr, "%s: unable to connect to TCP server.\n", 
  577.             prog);
  578.         perror("Reason");
  579.         return;
  580.         /*NOTREACHED*/
  581.     }
  582.  
  583.     if(verbose)
  584.         printf("sending message...\n");
  585.     if(write(s, msg, msg_len) < 0) {
  586.         fprintf(stderr, "%s: unable to send message.\n", prog);
  587.         perror("Reason");
  588.         if (close(s))
  589.             perror("close");
  590.         exit(3);
  591.     }
  592.         /*
  593.          * wait for reply
  594.          */
  595.     rval = read(s, rcvbuf, sizeof rcvbuf);
  596.     if (rval < 1) {
  597.         fprintf(stderr, "%s: no reply received.\n", prog);
  598.         perror("Reason");
  599.         if (close(s))
  600.             perror("close");
  601.         exit(3);
  602.     }
  603.     rcvbuf[(rval < sizeof(rcvbuf)) ? rval : sizeof(rcvbuf)-1] = 0;
  604.     if (debug) printf("reply:'%s'\n", rcvbuf);
  605.     if (rcvbuf[0] == '+') {
  606.         if (verbose) printf("message delivered to recipient (%s)\n",
  607.             rcvbuf+1);
  608.         exit(0);
  609.         /*NOTREACHED*/
  610.     }
  611.     else if (rcvbuf[0] == '-') {
  612.         printf("Message wasn't delivered - %s.\n", rcvbuf+1);
  613.         exit(1);
  614.         /*NOTREACHED*/
  615.     }
  616.     else {
  617.         printf("Message wasn't delivered.\n");
  618.         exit(2);
  619.         /*NOTREACHED*/
  620.     }
  621. }
  622.  
  623. /*
  624.  * udp_msg(sp, r)
  625.  *      sp points at a sockaddr_in which contains the
  626.  *      port to be used.
  627.  *      r is the retry count to be used.
  628.  *    broad is set if it's to be broadcast
  629.  *
  630.  * Send the assembled message to a specific destination using UDP.
  631.  * This procedure will never return - it always exits with an
  632.  * appropriate status code.
  633.  */
  634.  
  635. void
  636. udp_msg(sp, r, broad)
  637.     struct    sockaddr_in *sp;
  638.     int    r;
  639.     int    broad;
  640. {
  641.     int    s;
  642.     int    delivered = 0;
  643.     struct    sockaddr_in sin;
  644.     fd_set    ready;
  645.     struct    timeval to;
  646.     int    rval;
  647.     int    toutwait;
  648.     int    i;
  649.  
  650. if(debug) printf("invoked udp_msg(...,%d)\n", r);
  651.  
  652.     if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  653.         fprintf(stderr, "%s: unable to open socket.\n", prog);
  654.         perror("Reason");
  655.         exit(3);
  656.     }
  657.  
  658. #ifndef NO_BROADCAST
  659.     if (broad) {
  660.         i = 1;
  661.         if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &i, sizeof i) < 0) {
  662.             fprintf(stderr, "%s: unable to configure socket for broadcast.\n", prog);
  663.             perror("Reason");
  664.             exit(3);
  665.         }
  666.  
  667.         find_broadcast_addresses(s);
  668.     }
  669. #endif
  670.  
  671.     sin.sin_family = AF_INET;
  672.     sin.sin_addr.s_addr = htonl(INADDR_ANY);
  673.     sin.sin_port = htons(0);
  674.  
  675.     if(bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
  676.         fprintf(stderr, "%s: unable to set local socket address.\n", prog);
  677.         perror("Reason");
  678.         exit(3);
  679.     }
  680.     
  681.     /*
  682.      * the timeout wait starts at three seconds and backs off
  683.      * by two seconds each time we retry
  684.      */
  685.     toutwait = 3;
  686.  
  687.     /* send the message */
  688.     while(r--) {
  689.         if(verbose)
  690.             printf("sending message...\n");
  691.         if (broad) {
  692. #ifndef NO_BROADCAST
  693.             for (i = 0; i < if_n; i++){
  694.                 if_a[i].sin_port = sp->sin_port;
  695.                 if(verbose) printf("sending message to %s\n",
  696.                     inet_ntoa(if_a[i].sin_addr));
  697.                 if(sendto(s, msg, msg_len, 0, (struct sockaddr *)&(if_a[i]),
  698.                    sizeof if_a[i]) < 0) {
  699.                     fprintf(stderr, "%s: unable to send message.\n", prog);
  700.                     perror("Reason");
  701.                     exit(3);
  702.                 }
  703.             }
  704. #endif
  705.         }
  706.         else {
  707.             if(sendto(s, msg, msg_len, 0, (struct sockaddr *)sp, sizeof(*sp)) < 0) {
  708.                 fprintf(stderr, "%s: unable to send message.\n", prog);
  709.                 perror("Reason");
  710.                 exit(3);
  711.             }
  712.         }
  713.         if(r) {
  714.             /*
  715.              * wait for reply or timeout
  716.              */
  717.             to.tv_sec = toutwait;
  718.             to.tv_usec = 0;
  719.             FD_ZERO(&ready);
  720.             FD_SET(s, &ready);
  721.             rval = select(20, &ready, (fd_set *)0, (fd_set *)0, &to);
  722.             if(rval < 0)
  723.                  fprintf(stderr, "%s: interrupt\n", prog);
  724.             if(rval == 1) {
  725.                 delivered = 1;
  726.                 udp_decode_ack(s);
  727.                 break;
  728.             }
  729.             /*
  730.              * falling through, must be interrupt or timeout
  731.              */
  732.             toutwait += 2;
  733.         }
  734.     }
  735.     if (!delivered)
  736.         printf("Message unacknowledged - may not have been received.\n");
  737.     if(debug) printf("closing and exiting\n");
  738.     close(s);
  739.     exit(0);
  740.     /*NOTREACHED*/
  741. }
  742.  
  743.  
  744. #ifndef NO_BROADCAST
  745. /*
  746.  * find_broadcast_addresses
  747.  *
  748.  * This procedure is used by broadcast_msg to determine all of the
  749.  * network interfaces configred on the local system, so that the
  750.  * message can be broadcast over each of them. This logic is derived
  751.  * from the SunOS documentation, and has also been tested on SVR4:
  752.  * anyone porting this program to significantly different systems
  753.  * should check this area carefully.
  754.  *
  755.  * The procedure uses the SIOCGIFCONF ioctl to retrieve the
  756.  * interfaces. For each one, it retrieves the flags via SIOCGIFFLAGS.
  757.  * For a point-to-point interface, the peer address is fetched using
  758.  * SIOCGIFDSTADDR. For a broadcast inteface, the broadcast address
  759.  * is obtained using SIOCGIFBRDADDR. The addresses are accumulated
  760.  * in the array if_a, and if_n holds the count of addresses found.
  761.  */
  762.  
  763. void
  764. find_broadcast_addresses(s)
  765.     int s;
  766. {
  767.     struct ifconf ifc;
  768.     struct ifreq *ifr;
  769.     char buf[4096], *cp, *cplim;
  770.     int n;
  771.  
  772.     if_n = 0;
  773.  
  774.     ifc.ifc_len = sizeof buf;
  775.     ifc.ifc_buf = buf;
  776.  
  777.     if(ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
  778.         fprintf(stderr, "%s: SIOCGIFCONF failed.\n", prog);
  779.         perror("Reason");
  780.         exit(3);
  781.     }
  782.     ifr = ifc.ifc_req;
  783.     n = ifc.ifc_len/sizeof(struct ifreq);
  784.     if(debug)
  785.         printf("checking %d interfaces returned by SIOCGIFCONF...\n",
  786.             n);
  787.  
  788. #ifdef RTM_ADD
  789. #define max(a, b) (a > b ? a : b)
  790. #define size(p) max((p).sa_len, sizeof(p))
  791. #else
  792. #define size(p) (sizeof (p))
  793. #endif
  794.  
  795.     cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
  796.     for (cp = buf; cp < cplim;
  797.             cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
  798.         ifr = (struct ifreq *)cp;
  799.         if(ifr->ifr_addr.sa_family != AF_INET) {
  800.             continue;
  801.         }
  802.         if(ioctl(s, SIOCGIFFLAGS, (char *)ifr) < 0) {
  803.             perror("SIOCGIFFLAGS");
  804.             continue;
  805.         }
  806.         if((ifr->ifr_flags & IFF_UP) == 0 ||
  807.            (ifr->ifr_flags & IFF_LOOPBACK) ||
  808.            (ifr->ifr_flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
  809.             continue;
  810.         if(ifr->ifr_flags & IFF_POINTOPOINT) {
  811.             if(ioctl(s, SIOCGIFDSTADDR, (char *)ifr) < 0) {
  812.                 perror("SIOCGIFDSTADDR");
  813.                 continue;
  814.             }
  815.             MEMCPY((char *)&if_a[if_n++], (char *)&ifr->ifr_dstaddr,
  816.                 sizeof ifr->ifr_dstaddr);
  817.         } else if(ifr->ifr_flags & IFF_BROADCAST) {
  818.             if(ioctl(s, SIOCGIFBRDADDR, (char *)ifr) < 0) {
  819.                 perror("SIOCGIFBRDADDR");
  820.                 continue;
  821.             }
  822.             MEMCPY((char *)&if_a[if_n++], (char *)&ifr->ifr_broadaddr,
  823.                 sizeof ifr->ifr_broadaddr);
  824.         }
  825.     }
  826.  
  827.     if(debug)
  828.         printf("found %d interfaces\n", if_n);    
  829.     if(if_n == 0) {
  830.         fprintf(stderr, "%s: no applicable network interfaces\n", prog);
  831.         exit(3);
  832.         /*NOTREACHED*/
  833.     } 
  834. }
  835. #endif /* NO_BROADCAST */
  836.  
  837.  
  838. /* read & decode the server's acknowledgement */
  839. void
  840. udp_decode_ack(s)
  841.     int    s;
  842. {
  843.     struct    sockaddr_in from;
  844.     int    fromlen, rval;
  845.     char    rcvbuf[256];
  846.  
  847.     fromlen = sizeof(fromlen);
  848.     rval = recvfrom(s, rcvbuf, sizeof rcvbuf, 0,
  849.             (struct sockaddr *)&from, &fromlen);
  850.  
  851.     if (rval < 0) {
  852.         perror("recvfrom");
  853.         return;
  854.     }
  855.     rcvbuf[(rval < sizeof(rcvbuf)) ? rval : sizeof(rcvbuf)-1] = 0;
  856.  
  857.     if (rcvbuf[0] == '+') {
  858.         if (verbose)
  859.             printf("message delivered to recipient (%s)\n",
  860.                 rcvbuf+1);
  861.     }
  862.     else if (rcvbuf[0] == '-')
  863.         printf("Message wasn't delivered - %s.\n", rcvbuf+1);
  864.     else
  865.         printf("Unknown message acknowledgement (%s)\n", rcvbuf); 
  866. }
  867.  
  868.  
  869. /*
  870.  * As noted in the RFC, it is important to filter out control
  871.  * chracters and suchlike, since there may exist terminals
  872.  * which will behave in bizarre and security-violating ways
  873.  * if presented with certain control code sequences. The
  874.  * server will also be filtering messages, but it is incumbent
  875.  * upon a well-written client implementation to send only "clean"
  876.  * messages. After all, there may exist servers which will reject
  877.  * any message which does not filter cleanly.
  878.  *
  879.  * It is an open question as to how the filtering should be done.
  880.  * One approach might be to squeeze out any invalid characters
  881.  * silently. This would make debugging difficult. This implementation
  882.  * replaces all non-printable characters with '?' characters.
  883.  */
  884.  
  885. void
  886. filter(text)
  887. char *text;
  888. {
  889.     while (*text) {
  890.         if(!isprint(*text) && !isspace(*text))
  891.             *text = '?';
  892.         text++;
  893.     }
  894. }
  895.