source: trunk/athena/bin/lert/lert.c @ 14857

Revision 14857, 10.9 KB checked in by zacheiss, 24 years ago (diff)
Redo argument parsing, and add a -server option for connecting to a server other than the one specified by the hesiod sloc entry for lert.
RevLine 
[14069]1/* Copyright 1994, 1999 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
[7780]15
[14069]16/* This is the client for the lert system. */
[7780]17
[14857]18static const char rcsid[] = "$Id: lert.c,v 1.10 2000-06-19 17:41:48 zacheiss Exp $";
[7780]19
20#include <sys/types.h>
[14073]21#include <sys/socket.h>
[7780]22#include <sys/stat.h>
[14073]23#include <sys/time.h>
24
25#include <errno.h>
[7780]26#include <fcntl.h>
27#include <netdb.h>
[14073]28#include <netinet/in.h>
[7780]29#include <pwd.h>
[14073]30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34
35#include <des.h>
36#include <hesiod.h>
37#include <krb.h>
38
[7780]39#include "lert.h"
40
[14857]41#define argis(a, b) (!strcmp(*arg + 1, a) || !strcmp(*arg + 1, b))
42
[7780]43static struct timeval timeout = { LERT_TIMEOUT, 0 };
44static int error_messages = TRUE;
45
[14069]46static void bombout(int mess);
47
[14857]48static void usage(char *pname)
[7780]49{
[10066]50  fprintf(stderr,
[14857]51          "Usage: %s [-zephyr|-z] [-mail|-m] [-no|-n] [-quiet|-q] [-server|-s server]\n", pname);
[7780]52}
53
[14857]54static char *lert_says(int no_more, char *server)
[7780]55{
[14073]56  void *hes_context = NULL;
57  char *lert_host, *message;
[7780]58  struct hostent *hp;
[14073]59  struct servent *sp;
[7780]60  struct sockaddr_in sin, lsin;
[14073]61  char *cp, *srealm, *sinst;
62  KTEXT_ST authent;
[7780]63  CREDENTIALS cred;
[14073]64  Key_schedule sched;
65  int plen, packetsize, s, i, tries, status, gotit;
66  unsigned char *packet;
67  fd_set readfds;
[7780]68  MSG_DAT msg_data;
69
[14069]70  /* Find out where lert lives. (Note the presumption that there is
71   * only one lert!)
[7780]72   */
[14073]73  lert_host = NULL;
[14857]74  if (server)
75    lert_host = server;
76  else if (hesiod_init(&hes_context) == 0)
[14069]77    {
[14073]78      char **slocs = hesiod_resolve(hes_context, LERT_SERVER, LERT_TYPE);
79      if (slocs)
80        {
81          lert_host = strdup(slocs[0]);
82          if (!lert_host)
83            bombout(ERR_MEMORY);
84          hesiod_free_list(hes_context, slocs);
85        }
[14069]86    }
[14073]87  if (!lert_host)
88    {
89      lert_host = strdup(LERT_HOME);
90      if (!lert_host)
91        bombout(ERR_MEMORY);
92    }
[7780]93
[14073]94  hp = gethostbyname(lert_host);
95  if (hp == NULL)
96    bombout(ERR_HOSTNAME);
97
98  memset(&sin, 0, sizeof(sin));
99  sin.sin_family = hp->h_addrtype;
100  memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
101
102  /* Find out what port lert is on. */
103  sin.sin_port = htons(LERT_PORT);
104  sp = getservbyname(LERT_SERVED, LERT_PROTO);
105  if (sp)
106    sin.sin_port = sp->s_port;
107  else if (hes_context)
108    {
109      sp = hesiod_getservbyname(hes_context, LERT_SERVED, LERT_PROTO);
110      sin.sin_port = sp->s_port;
111      hesiod_free_servent(hes_context, sp);
112    }
113   
114  if (hes_context)
115    hesiod_end(hes_context);
116
[14069]117  /* Find out what Kerberos realm the server is in and get a ticket
118   * for it.
[7780]119   */
[14073]120  cp = krb_realmofhost(lert_host);
[14069]121  if (cp == NULL)
122    bombout(ERR_KERB_REALM);
[14073]123  srealm = strdup(cp);
124  if (!srealm)
125    bombout(ERR_MEMORY);
[7780]126
[14069]127  /* Resolve hostname for service principal. */
[14073]128  cp = krb_get_phost(lert_host);
[14069]129  if (cp == NULL)
130    bombout(ERR_KERB_PHOST);
[14073]131  sinst = strdup(cp);
132  if (!sinst)
133    bombout(ERR_MEMORY);
[7780]134
[14073]135  status = krb_mk_req(&authent, LERT_SERVICE, sinst, srealm, no_more);
[14069]136  if (status != KSUCCESS)
137    bombout(ERR_KERB_AUTH);
[7780]138
[14073]139  /* Get the session key now... we'll need it later. */
140  status = krb_get_cred(LERT_SERVICE, sinst, srealm, &cred);
141  if (status != KSUCCESS)
142    bombout(ERR_KERB_CRED);
143  des_key_sched(cred.session, sched);
[7780]144
[14073]145  free(srealm);
146  free(sinst);
[7780]147
[14069]148  /* Lert's basic protocol:
[14073]149   * Client sends version, one byte query code, and authentication.
[14069]150   */
[10378]151  plen = LERT_LENGTH + sizeof(int) + authent.length;
[14073]152  packet = malloc(plen);
153  if (!packet)
154    bombout(ERR_MEMORY);
155  packet[0] = LERT_VERSION;
156  packet[1] = no_more;
157  memcpy(packet + LERT_LENGTH, &authent, sizeof(int) + authent.length);
[7780]158
159  s = socket(AF_INET, SOCK_DGRAM, 0);
[14069]160  if (s < 0)
161    bombout(ERR_SOCKET);
162  if (connect(s, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0)
163    bombout(ERR_CONNECT);
[14073]164  /* Get my address, for krb_rd_priv. */
165  memset(&lsin, 0, sizeof(lsin));
166  i = sizeof(lsin);
167  if (getsockname(s, (struct sockaddr *)&lsin, &i) < 0)
168    bombout(LERT_NO_SOCK);
169
[7780]170  gotit = 0;
[14073]171  for (tries = 0; tries < RETRIES && !gotit; tries++)
[14069]172    {
173      if (send(s, packet, plen, 0) < 0)
174        bombout(ERR_SEND);
175      FD_ZERO(&readfds);
176      FD_SET(s, &readfds);
[14073]177      gotit = select(s + 1, &readfds, NULL, NULL, &timeout) == 1;
178    }
179  free(packet);
180  if (tries == 0)
181    bombout(ERR_TIMEOUT);
[7780]182
[14073]183  /* Read the response. */
184  packetsize = 2048;
185  packet = malloc(packetsize);
186  if (!packet)
187    bombout(ERR_MEMORY);
188  plen = 0;
189  while (1)
190    {
191      int nread;
192
193      nread = recv(s, packet + plen, packetsize - plen, 0);
194      if (nread < 0)
[14069]195        bombout(ERR_RCV);
[14073]196      plen += nread;
[7780]197
[14073]198      if (nread < packetsize - plen)
199        break;
200
201      packetsize *= 2;
202      packet = realloc(packet, packetsize);
203      if (!packet)
204        bombout(ERR_MEMORY);
[14069]205    }
206
[14073]207  /* Now close the socket. */
[14069]208  shutdown(s, 2);
[7780]209  close(s);
210
[14073]211  status = krb_rd_priv(packet, plen, sched, cred.session,
[14069]212                       &sin, &lsin, &msg_data);
213  if (status)
214    bombout(ERR_SERVER);
[7780]215
[14069]216  if (msg_data.app_length == 0)
[14073]217    return NULL;
[7780]218
[14069]219  /* At this point, we have a packet.  Check it out:
220   * [0] LERT_VERSION
221   * [1] code response
222   * [2 on] data...
223   */
[7780]224
[14069]225  if (msg_data.app_data[0] != LERT_VERSION)
226    bombout(ERR_VERSION);
[14073]227
[14069]228  if (msg_data.app_data[1] == LERT_MSG)
229    {
230      /* We have a message from the server. */
[14073]231      message = malloc(msg_data.app_length - LERT_CHECK + 1);
232      if (!message)
233        bombout(ERR_MEMORY);
234      memcpy(message, msg_data.app_data + LERT_CHECK,
[7780]235           msg_data.app_length - LERT_CHECK);
[14073]236      message[msg_data.app_length - LERT_CHECK] = '\0';
[14069]237    }
238  else
[14073]239    message = NULL;
240  free(packet);
241
242  return message;
[7780]243}
244
[14069]245/* General error reporting */
246static void bombout(int mess)
[7780]247{
[14069]248  if (error_messages)
249    {
250      fprintf(stderr, "lert: ");
251      switch(mess)
252        {
253        case ERR_KERB_CRED:
254          fprintf(stderr, "Error getting kerberos credentials.\n");
255          break;
256        case ERR_KERB_AUTH:
257          fprintf(stderr, "Error getting kerberos authentication.\n");
258          fprintf(stderr, "Are your tickets valid?\n");
259          break;
260        case ERR_TIMEOUT:
261          fprintf(stderr, "Timed out waiting for response from server.\n");
262          break;
263        case ERR_SERVER:
264          fprintf(stderr, "Unable to contact server.\n");
265          break;
266        case ERR_SERVED:
267          fprintf(stderr, "Bad string from server.\n");
268          break;
269        case ERR_SEND:
270          fprintf(stderr, "Error in send: %s\n", strerror(errno));
271          break;
272        case ERR_RCV:
273          fprintf(stderr, "Error in recv: %s\n", strerror(errno));
274          break;
275        case ERR_USER:
276          fprintf(stderr, "Could not get your name to send messages\n");
277          break;
278        case NO_PROCS:
279          fprintf(stderr, "Error running child processes: %s\n",
280                  strerror(errno));
281          break;
282        case ERR_MEMORY:
283          fprintf(stderr, "Out of memory\n");
284          break;
[14073]285        case ERR_FILE:
286          fprintf(stderr, "Could not read message file\n");
287          break;
[14069]288        default:
289          fprintf(stderr, "A problem (%d) occurred when checking the "
290                  "database\n", mess);
291          break;
292        }
293      fprintf(stderr, "Please try again later.\n");
294      fprintf(stderr,
295              "If this problem persists, please report it to a consultant.\n");
[7780]296    }
[14069]297  exit(1);
[7780]298}
299
[14073]300/* Send a message from lert via zephyr. */
301static void zephyr_message(char *user, char *header, char *message)
[7780]302{
[14073]303  FILE *p;
304  char *cmd;
[7780]305
[14073]306  cmd = malloc(11 + strlen(user));
307  if (!cmd)
308    bombout(ERR_MEMORY);
309  sprintf(cmd, "zwrite -q %s", user);
[7780]310
[14073]311  p = popen(cmd, "w");
312  if (!p)
313    bombout(NO_PROCS);
314  fputs("@bold{", p);
315  fputs(header, p);
316  fputs("}", p);
317  fputs(message, p);
318  pclose(p);
319}
[14069]320
[14073]321/* Send a message from lert via mail. */
322static void mail_message(char *user, char *subject,
323                         char *header, char *message)
324{
325  FILE *p;
326  char *cmd;
[14069]327
[14073]328  cmd = malloc(20 + strlen(subject) + strlen(user));
329  if (!cmd)
[14069]330    bombout(ERR_MEMORY);
[14073]331  sprintf(cmd, "mhmail %s -subject \"%s\"", user, subject);
332
333  p = popen(cmd, "w");
334  if (!p)
335    bombout(NO_PROCS);
336  fputs(header, p);
337  fputs(message, p);
338  pclose(p);
[7780]339}
340
[14073]341/* Print a message from lert to stdout. */
342static void cat_message(char *header, char *message)
[7780]343{
[14073]344  fputs(header, stdout);
345  fputs(message, stdout);
346}         
[7780]347
[14073]348/* Read a file from the lert directory. */
349static char *read_file(char *name)
350{
351  struct stat st;
352  int fd;
353  char *buf;
[7780]354
[14073]355  fd = open(name, O_RDONLY);
356  if (fd < 0 || fstat(fd, &st) < 0)
357    bombout(ERR_FILE);
[7780]358
[14073]359  buf = malloc(st.st_size + 1);
360  if (!buf)
361    bombout(ERR_MEMORY);
362
363  if (read(fd, buf, st.st_size) < st.st_size)
364    bombout(ERR_FILE);
365  buf[st.st_size] = '\0';
366  close(fd);
367
368  return buf;
369}
370
371/* View the message from lert. */
[14081]372static void view_message(char *msgs, int type, int no_more)
[14073]373{
374  char name[sizeof(LERTS_MSG_FILES) + 2];
375  char *user, *subject = NULL;
376  char *header, *body = NULL;
377
378  if (type == LERT_Z || type == LERT_MAIL)
[14069]379    {
[14073]380      struct passwd *pw;
381
[14069]382      pw = getpwuid(getuid());
383      if (pw)
[14073]384        user = pw->pw_name;
[14069]385      else
[14073]386        {
387          user = getenv("USER");
388          if (!user)
389            user = getlogin();
390        }
391      if (!user)
392        type = LERT_CAT;
[7780]393    }
[14069]394
[14073]395  if (type == LERT_MAIL)
[14069]396    {
[14073]397      subject = read_file(LERTS_MSG_SUBJECT);
398      if (!subject)
399        subject = strdup(LERTS_DEF_SUBJECT);
400      if (!subject)
401        bombout(ERR_MEMORY);
402    }
[7780]403
[14081]404  header = read_file(no_more ? LERTS_MSG_FILES "1" : LERTS_MSG_FILES "0");
[7780]405
[14073]406  while (*msgs)
407    {
408      sprintf(name, "%s%c", LERTS_MSG_FILES, *msgs);
409      body = read_file(name);
410      switch (type)
411        {
412        case LERT_Z:
413          zephyr_message(user, header, body);
414          break;
[7780]415
[14073]416        case LERT_MAIL:
417          mail_message(user, subject, header, body);
418          break;
[7780]419
[14073]420        default:
421          cat_message(header, body);
422          break;
423        }
[7780]424
[14073]425      free(body);
426      msgs++;
[7780]427    }
[14073]428
429  free(header);
430  free(subject);
[7780]431}
432
[14069]433int main(int argc, char **argv)
[7780]434{
[14857]435  char *whoami, *message, *server = NULL;
[14073]436  int method = LERT_CAT, no_more = FALSE;
[14857]437  char **arg = argv;
[7780]438
[14073]439  whoami = strrchr(argv[0], '/');
440  if (whoami)
441    whoami++;
442  else
443    whoami = argv[0];
[7780]444
445  /* Argument Processing:
446   *    -z or -zephyr: send the message as a zephyrgram.
447   *    -m or -mail: send the message as email
448   *    -n or -no: no more messages!
449   */
[14069]450
[14857]451  while (++arg - argv < argc)
[14069]452    {
[14857]453      if (**arg == '-')
[14069]454        {
[14857]455          if (argis("zephyr", "z"))
456            method = LERT_Z;
457          else if (argis("mail","m"))
458            method = LERT_MAIL;
459          else if (argis("no", "n"))
460            no_more = TRUE;
461          else if (argis("quiet", "q"))
462            error_messages = FALSE;
463          else if (argis("server", "s"))
464            {
465              if (arg - argv < argc - 1)
466                {
467                  ++arg;
468                  server = *arg;
469                }
470              else
471                usage(whoami);
472            }
473          else
474            {
475              usage(whoami);
476              exit(1);
477            }
[14069]478        }
479    }
480  /* Get the server's string for this user. */
[14857]481  message = lert_says(no_more, server);
[14073]482  if (message)
483    {
[14081]484      view_message(message, method, no_more);
[14073]485      free(message);
486    }
[7780]487
[14069]488  return 0;
[7780]489}
Note: See TracBrowser for help on using the repository browser.