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

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