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.
Line 
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 */
15
16/* This is the client for the lert system. */
17
18static const char rcsid[] = "$Id: lert.c,v 1.10 2000-06-19 17:41:48 zacheiss Exp $";
19
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <sys/stat.h>
23#include <sys/time.h>
24
25#include <errno.h>
26#include <fcntl.h>
27#include <netdb.h>
28#include <netinet/in.h>
29#include <pwd.h>
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
39#include "lert.h"
40
41#define argis(a, b) (!strcmp(*arg + 1, a) || !strcmp(*arg + 1, b))
42
43static struct timeval timeout = { LERT_TIMEOUT, 0 };
44static int error_messages = TRUE;
45
46static void bombout(int mess);
47
48static void usage(char *pname)
49{
50  fprintf(stderr,
51          "Usage: %s [-zephyr|-z] [-mail|-m] [-no|-n] [-quiet|-q] [-server|-s server]\n", pname);
52}
53
54static char *lert_says(int no_more, char *server)
55{
56  void *hes_context = NULL;
57  char *lert_host, *message;
58  struct hostent *hp;
59  struct servent *sp;
60  struct sockaddr_in sin, lsin;
61  char *cp, *srealm, *sinst;
62  KTEXT_ST authent;
63  CREDENTIALS cred;
64  Key_schedule sched;
65  int plen, packetsize, s, i, tries, status, gotit;
66  unsigned char *packet;
67  fd_set readfds;
68  MSG_DAT msg_data;
69
70  /* Find out where lert lives. (Note the presumption that there is
71   * only one lert!)
72   */
73  lert_host = NULL;
74  if (server)
75    lert_host = server;
76  else if (hesiod_init(&hes_context) == 0)
77    {
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        }
86    }
87  if (!lert_host)
88    {
89      lert_host = strdup(LERT_HOME);
90      if (!lert_host)
91        bombout(ERR_MEMORY);
92    }
93
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
117  /* Find out what Kerberos realm the server is in and get a ticket
118   * for it.
119   */
120  cp = krb_realmofhost(lert_host);
121  if (cp == NULL)
122    bombout(ERR_KERB_REALM);
123  srealm = strdup(cp);
124  if (!srealm)
125    bombout(ERR_MEMORY);
126
127  /* Resolve hostname for service principal. */
128  cp = krb_get_phost(lert_host);
129  if (cp == NULL)
130    bombout(ERR_KERB_PHOST);
131  sinst = strdup(cp);
132  if (!sinst)
133    bombout(ERR_MEMORY);
134
135  status = krb_mk_req(&authent, LERT_SERVICE, sinst, srealm, no_more);
136  if (status != KSUCCESS)
137    bombout(ERR_KERB_AUTH);
138
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);
144
145  free(srealm);
146  free(sinst);
147
148  /* Lert's basic protocol:
149   * Client sends version, one byte query code, and authentication.
150   */
151  plen = LERT_LENGTH + sizeof(int) + authent.length;
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);
158
159  s = socket(AF_INET, SOCK_DGRAM, 0);
160  if (s < 0)
161    bombout(ERR_SOCKET);
162  if (connect(s, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0)
163    bombout(ERR_CONNECT);
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
170  gotit = 0;
171  for (tries = 0; tries < RETRIES && !gotit; tries++)
172    {
173      if (send(s, packet, plen, 0) < 0)
174        bombout(ERR_SEND);
175      FD_ZERO(&readfds);
176      FD_SET(s, &readfds);
177      gotit = select(s + 1, &readfds, NULL, NULL, &timeout) == 1;
178    }
179  free(packet);
180  if (tries == 0)
181    bombout(ERR_TIMEOUT);
182
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)
195        bombout(ERR_RCV);
196      plen += nread;
197
198      if (nread < packetsize - plen)
199        break;
200
201      packetsize *= 2;
202      packet = realloc(packet, packetsize);
203      if (!packet)
204        bombout(ERR_MEMORY);
205    }
206
207  /* Now close the socket. */
208  shutdown(s, 2);
209  close(s);
210
211  status = krb_rd_priv(packet, plen, sched, cred.session,
212                       &sin, &lsin, &msg_data);
213  if (status)
214    bombout(ERR_SERVER);
215
216  if (msg_data.app_length == 0)
217    return NULL;
218
219  /* At this point, we have a packet.  Check it out:
220   * [0] LERT_VERSION
221   * [1] code response
222   * [2 on] data...
223   */
224
225  if (msg_data.app_data[0] != LERT_VERSION)
226    bombout(ERR_VERSION);
227
228  if (msg_data.app_data[1] == LERT_MSG)
229    {
230      /* We have a message from the server. */
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,
235           msg_data.app_length - LERT_CHECK);
236      message[msg_data.app_length - LERT_CHECK] = '\0';
237    }
238  else
239    message = NULL;
240  free(packet);
241
242  return message;
243}
244
245/* General error reporting */
246static void bombout(int mess)
247{
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;
285        case ERR_FILE:
286          fprintf(stderr, "Could not read message file\n");
287          break;
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");
296    }
297  exit(1);
298}
299
300/* Send a message from lert via zephyr. */
301static void zephyr_message(char *user, char *header, char *message)
302{
303  FILE *p;
304  char *cmd;
305
306  cmd = malloc(11 + strlen(user));
307  if (!cmd)
308    bombout(ERR_MEMORY);
309  sprintf(cmd, "zwrite -q %s", user);
310
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}
320
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;
327
328  cmd = malloc(20 + strlen(subject) + strlen(user));
329  if (!cmd)
330    bombout(ERR_MEMORY);
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);
339}
340
341/* Print a message from lert to stdout. */
342static void cat_message(char *header, char *message)
343{
344  fputs(header, stdout);
345  fputs(message, stdout);
346}         
347
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;
354
355  fd = open(name, O_RDONLY);
356  if (fd < 0 || fstat(fd, &st) < 0)
357    bombout(ERR_FILE);
358
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. */
372static void view_message(char *msgs, int type, int no_more)
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)
379    {
380      struct passwd *pw;
381
382      pw = getpwuid(getuid());
383      if (pw)
384        user = pw->pw_name;
385      else
386        {
387          user = getenv("USER");
388          if (!user)
389            user = getlogin();
390        }
391      if (!user)
392        type = LERT_CAT;
393    }
394
395  if (type == LERT_MAIL)
396    {
397      subject = read_file(LERTS_MSG_SUBJECT);
398      if (!subject)
399        subject = strdup(LERTS_DEF_SUBJECT);
400      if (!subject)
401        bombout(ERR_MEMORY);
402    }
403
404  header = read_file(no_more ? LERTS_MSG_FILES "1" : LERTS_MSG_FILES "0");
405
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;
415
416        case LERT_MAIL:
417          mail_message(user, subject, header, body);
418          break;
419
420        default:
421          cat_message(header, body);
422          break;
423        }
424
425      free(body);
426      msgs++;
427    }
428
429  free(header);
430  free(subject);
431}
432
433int main(int argc, char **argv)
434{
435  char *whoami, *message, *server = NULL;
436  int method = LERT_CAT, no_more = FALSE;
437  char **arg = argv;
438
439  whoami = strrchr(argv[0], '/');
440  if (whoami)
441    whoami++;
442  else
443    whoami = argv[0];
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   */
450
451  while (++arg - argv < argc)
452    {
453      if (**arg == '-')
454        {
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            }
478        }
479    }
480  /* Get the server's string for this user. */
481  message = lert_says(no_more, server);
482  if (message)
483    {
484      view_message(message, method, no_more);
485      free(message);
486    }
487
488  return 0;
489}
Note: See TracBrowser for help on using the repository browser.