chat.c revision 16263
1/*
2 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
3 *
4 *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
5 *
6 *  Most of codes are derived from chat.c by Karl Fox (karl@MorningStar.Com).
7 *
8 *	Chat -- a program for automatic session establishment (i.e. dial
9 *		the phone and log in).
10 *
11 *	This software is in the public domain.
12 *
13 *	Please send all bug reports, requests for information, etc. to:
14 *
15 *		Karl Fox <karl@MorningStar.Com>
16 *		Morning Star Technologies, Inc.
17 *		1760 Zollinger Road
18 *		Columbus, OH  43221
19 *		(614)451-1883
20 *
21 * $Id: chat.c,v 1.10 1996/05/11 20:48:20 phk Exp $
22 *
23 *  TODO:
24 *	o Support more UUCP compatible control sequences.
25 *	o Dialing shoud not block monitor process.
26 *	o Reading modem by select should be unified into main.c
27 */
28#include "defs.h"
29#include <ctype.h>
30#include <sys/uio.h>
31#ifndef isblank
32#define	isblank(c)	((c) == '\t' || (c) == ' ')
33#endif
34#include <sys/time.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <signal.h>
38#include <sys/wait.h>
39#include "timeout.h"
40#include "vars.h"
41
42#define	IBSIZE 200
43
44static int TimeoutSec;
45static int abort_next, timeout_next;
46static int numaborts;
47char *AbortStrings[50];
48char inbuff[IBSIZE*2+1];
49
50extern int ChangeParity(char *);
51
52#define	MATCH	1
53#define	NOMATCH	0
54#define	ABORT	-1
55
56static char *
57findblank(p, instring)
58char *p;
59int instring;
60{
61  if (instring) {
62    while (*p) {
63      if (*p == '\\') {
64	strcpy(p, p + 1);
65	if (!*p)
66	  break;
67      } else if (*p == '"')
68	return(p);
69      p++;
70    }
71  } else {
72    while (*p) {
73      if (isblank(*p))
74	return(p);
75      p++;
76    }
77  }
78  return p;
79}
80
81int
82MakeArgs(script, pvect)
83char *script;
84char **pvect;
85{
86  int nargs, nb;
87  int instring;
88
89  nargs = 0;
90  while (*script) {
91    nb = strspn(script, " \t");
92    script += nb;
93    if (*script) {
94      if (*script == '"') {
95	instring = 1;
96	script++;
97	if (*script == '\0')
98	  return(nargs);
99      } else
100	instring = 0;
101      *pvect++ = script;
102      nargs++;
103      script = findblank(script, instring);
104      if (*script)
105	*script++ = '\0';
106    }
107  }
108  *pvect = NULL;
109  return nargs;
110}
111
112/*
113 *  \r	Carrige return character
114 *  \s  Space character
115 *  \n  Line feed character
116 *  \T  Telephone number(s) (defined via `set phone')
117 *  \t  Tab character
118 */
119char *
120ExpandString(str, result, sendmode)
121char *str;
122char *result;
123int sendmode;
124{
125  int addcr = 0;
126  char *phone;
127
128  if (sendmode)
129    addcr = 1;
130  while (*str) {
131    switch (*str) {
132    case '\\':
133      str++;
134      switch (*str) {
135      case 'c':
136	if (sendmode)
137	  addcr = 0;
138	break;
139      case 'd':		/* Delay 2 seconds */
140        sleep(2); break;
141      case 'p':
142        usleep(250000); break;	/* Pause 0.25 sec */
143      case 'n':
144	*result++ = '\n'; break;
145      case 'r':
146	*result++ = '\r'; break;
147      case 's':
148	*result++ = ' '; break;
149      case 't':
150	*result++ = '\t'; break;
151      case 'P':
152	bcopy(VarAuthKey, result, strlen(VarAuthKey));
153	result += strlen(VarAuthKey);
154	break;
155      case 'T':
156	if (VarNextPhone == NULL) {
157	  strcpy(VarPhoneCopy, VarPhoneList);
158	  VarNextPhone = VarPhoneCopy;
159	}
160	phone = strsep(&VarNextPhone, ":");
161	bcopy(phone, result, strlen(phone));
162	result += strlen(phone);
163	if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER)
164	  fprintf(stderr, "Phone: %s\n", phone);
165	LogPrintf(LOG_PHASE_BIT, "Phone: %s\n", phone);
166	break;
167      case 'U':
168	bcopy(VarAuthName, result, strlen(VarAuthName));
169	result += strlen(VarAuthName);
170	break;
171      default:
172	*result++ = *str; break;
173      }
174      if (*str) str++;
175      break;
176    case '^':
177      str++;
178      if (*str)
179	*result++ = *str++ & 0x1f;
180      break;
181    default:
182      *result++ = *str++;
183      break;
184    }
185  }
186  if (addcr)
187    *result++ = '\r';
188  *result++ = '\0';
189  return(result);
190}
191
192#define MAXLOGBUFF 200
193static char logbuff[MAXLOGBUFF];
194static int loglen = 0;
195
196static void clear_log() {
197  memset(logbuff,0,MAXLOGBUFF);
198  loglen = 0;
199}
200
201static void flush_log() {
202  if ((loglevel & LOG_CONNECT_BIT)
203      || ((loglevel & LOG_CARRIER_BIT)
204	  && strstr(logbuff,"CARRIER"))) {
205    LogPrintf(LOG_CONNECT_BIT|LOG_CARRIER_BIT,"Chat: %s\n",logbuff);
206  }
207  clear_log();
208}
209
210static void connect_log(char *str, int single_p) {
211  int space = MAXLOGBUFF - loglen - 1;
212
213  while (space--) {
214    if (*str == '\n') {
215      flush_log();
216    } else {
217      logbuff[loglen++] = *str;
218    }
219    if (single_p || !*++str) break;
220  }
221  if (!space) flush_log();
222}
223
224
225
226int
227WaitforString(estr)
228char *estr;
229{
230  struct timeval timeout;
231  char *s, *str, ch;
232  char *inp;
233  fd_set rfds;
234  int i, nfds, nb, msg;
235  char buff[200];
236
237
238#ifdef SIGALRM
239  int omask;
240  omask = sigblock(sigmask(SIGALRM));
241#endif
242  clear_log();
243  (void) ExpandString(estr, buff, 0);
244  LogPrintf(LOG_CHAT_BIT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff);
245  str = buff;
246  inp = inbuff;
247
248  if (strlen(str)>=IBSIZE){
249    str[IBSIZE]=0;
250    LogPrintf(LOG_CHAT_BIT, "Truncating String to %d character: %s\n", IBSIZE, str);
251  }
252
253  nfds = modem + 1;
254  s = str;
255  msg = FALSE;
256  for (;;) {
257    FD_ZERO(&rfds);
258    FD_SET(modem, &rfds);
259    /*
260     *  Because it is not clear whether select() modifies timeout value,
261     *  it is better to initialize timeout values everytime.
262     */
263    timeout.tv_sec = TimeoutSec;
264    timeout.tv_usec = 0;
265    i = select(nfds, &rfds, NULL, NULL, &timeout);
266#ifdef notdef
267    TimerService();
268#endif
269    if (i < 0) {
270#ifdef SIGALRM
271      if (errno == EINTR)
272	continue;
273      sigsetmask(omask);
274#endif
275      perror("select");
276      *inp = 0;
277      return(NOMATCH);
278    } else if (i == 0) { 	/* Timeout reached! */
279      *inp = 0;
280      if (inp != inbuff)
281      LogPrintf(LOG_CHAT_BIT, "got: %s\n", inbuff);
282      LogPrintf(LOG_CHAT_BIT, "can't get (%d).\n", timeout.tv_sec);
283#ifdef SIGALRM
284      sigsetmask(omask);
285#endif
286      return(NOMATCH);
287    }
288    if (FD_ISSET(modem, &rfds)) {	/* got something */
289      if (DEV_IS_SYNC) {
290	int length;
291	if ((length=strlen(inbuff))>IBSIZE){
292	  bcopy(&(inbuff[IBSIZE]),inbuff,IBSIZE+1); /* shuffle down next part*/
293	  length=strlen(inbuff);
294	}
295	nb = read(modem, &(inbuff[length]), IBSIZE);
296	inbuff[nb + length] = 0;
297	connect_log(inbuff,0);
298	if (strstr(inbuff, str)) {
299#ifdef SIGALRM
300          sigsetmask(omask);
301#endif
302	  flush_log();
303	  return(MATCH);
304	}
305	for (i = 0; i < numaborts; i++) {
306	  if (strstr(inbuff, AbortStrings[i])) {
307	    LogPrintf(LOG_CHAT_BIT, "Abort: %s\n", AbortStrings[i]);
308#ifdef SIGALRM
309            sigsetmask(omask);
310#endif
311	    flush_log();
312	    return(ABORT);
313	  }
314	}
315      } else {
316        read(modem, &ch, 1);
317	connect_log(&ch,1);
318        *inp++ = ch;
319        if (ch == *s) {
320	  s++;
321	  if (*s == '\0') {
322#ifdef SIGALRM
323            sigsetmask(omask);
324#endif
325	    *inp = 0;
326	    flush_log();
327	    return(MATCH);
328	  }
329        } else {
330	  s = str;
331	  if (inp == inbuff+ IBSIZE) {
332	    bcopy(inp - 100, inbuff, 100);
333	    inp = inbuff + 100;
334	  }
335	  for (i = 0; i < numaborts; i++) {	/* Look for Abort strings */
336	    int len;
337	    char *s1;
338
339	    s1 = AbortStrings[i];
340	    len = strlen(s1);
341	    if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) {
342	      LogPrintf(LOG_CHAT_BIT, "Abort: %s\n", s1);
343	      *inp = 0;
344#ifdef SIGALRM
345      	      sigsetmask(omask);
346#endif
347	      flush_log();
348	      return(ABORT);
349	    }
350	  }
351        }
352      }
353    }
354  }
355#ifdef SIGALRM
356  sigsetmask(omask);
357#endif
358}
359
360void
361ExecStr(command, out)
362char *command, *out;
363{
364  int pid;
365  int fids[2];
366  char *vector[20];
367  int stat, nb;
368  char *cp;
369  char tmp[300];
370  extern int errno;
371
372  cp = inbuff + strlen(inbuff) - 1;
373  while (cp > inbuff) {
374    if (*cp < ' ' && *cp != '\t') {
375      cp++;
376      break;
377    }
378    cp--;
379  }
380  sprintf(tmp, "%s %s", command, cp);
381  (void) MakeArgs(tmp, &vector);
382
383  pipe(fids);
384  pid = fork();
385  if (pid == 0) {
386    signal(SIGINT, SIG_DFL);
387    signal(SIGQUIT, SIG_DFL);
388    signal(SIGTERM, SIG_DFL);
389    signal(SIGHUP, SIG_DFL);
390    close(fids[0]);
391    dup2(fids[1], 1);
392    close(fids[1]);
393    nb = open("/dev/tty", O_RDWR);
394    dup2(nb, 0);
395LogPrintf(LOG_CHAT_BIT, "exec: %s\n", command);
396    /* switch back to original privileges */
397    if (setgid(getgid()) < 0) {
398      LogPrintf(LOG_CHAT_BIT, "setgid: %s\n", strerror(errno));
399      exit(1);
400    }
401    if (setuid(getuid()) < 0) {
402      LogPrintf(LOG_CHAT_BIT, "setuid: %s\n", strerror(errno));
403      exit(1);
404    }
405    pid = execvp(command, vector);
406    LogPrintf(LOG_CHAT_BIT, "execvp failed for (%d/%d): %s\n", pid, errno, command);
407    exit(127);
408  } else {
409    close(fids[1]);
410    for (;;) {
411      nb = read(fids[0], out, 1);
412      if (nb <= 0)
413	break;
414      out++;
415    }
416    *out = '\0';
417    close(fids[0]);
418    close(fids[1]);
419    waitpid(pid, &stat, WNOHANG);
420  }
421}
422
423void
424SendString(str)
425char *str;
426{
427  char *cp;
428  int nb, on;
429  char buff[200];
430
431  if (abort_next) {
432    abort_next = 0;
433    ExpandString(str, buff, 0);
434    AbortStrings[numaborts++] = strdup(buff);
435  } else if (timeout_next) {
436    timeout_next = 0;
437    TimeoutSec = atoi(str);
438    if (TimeoutSec <= 0)
439      TimeoutSec = 30;
440  } else {
441    if (*str == '!') {
442      (void) ExpandString(str+1, buff+2, 0);
443      ExecStr(buff + 2, buff + 2);
444    } else {
445      (void) ExpandString(str, buff+2, 1);
446    }
447    if (strstr(str, "\\P")) { /* Do not log the password itself. */
448      LogPrintf(LOG_CHAT_BIT, "sending: %s\n", str);
449    } else {
450      LogPrintf(LOG_CHAT_BIT, "sending: %s\n", buff+2);
451    }
452    cp = buff;
453    if (DEV_IS_SYNC)
454      bcopy("\377\003", buff, 2);	/* Prepend HDLC header */
455    else
456      cp += 2;
457    on = strlen(cp);
458    nb = write(modem, cp, on);
459  }
460}
461
462int
463ExpectString(str)
464char *str;
465{
466  char *minus;
467  int state;
468
469  if (strcmp(str, "ABORT") == 0) {
470    ++abort_next;
471    return(MATCH);
472  }
473  if (strcmp(str, "TIMEOUT") == 0) {
474    ++timeout_next;
475    return(MATCH);
476  }
477  LogPrintf(LOG_CHAT_BIT, "Expecting %s\n", str);
478  while (*str) {
479    /*
480     *  Check whether if string contains sub-send-expect.
481     */
482    for (minus = str; *minus; minus++) {
483      if (*minus == '-') {
484	if (minus == str || minus[-1] != '\\')
485	  break;
486      }
487    }
488    if (*minus == '-') {      /* We have sub-send-expect. */
489      *minus++ = '\0';
490      state = WaitforString(str);
491      if (state != NOMATCH)
492	return(state);
493      /*
494       * Can't get expect string. Sendout send part.
495       */
496      str = minus;
497      for (minus = str; *minus; minus++) {
498	if (*minus == '-') {
499	  if (minus == str || minus[-1] != '\\')
500	    break;
501	}
502      }
503      if (*minus == '-') {
504	*minus++ = '\0';
505	SendString(str);
506	str = minus;
507      } else {
508	SendString(str);
509	return(MATCH);
510      }
511    } else {
512      /*
513       *  Simple case. Wait for string.
514       */
515      return(WaitforString(str));
516    }
517  }
518  return(MATCH);
519}
520
521int
522DoChat(script)
523char *script;
524{
525  char *vector[20];
526  char **argv;
527  int argc, n, state;
528#ifdef DEBUG
529  int i;
530#endif
531
532  timeout_next = abort_next = 0;
533  for (n = 0; AbortStrings[n]; n++) {
534    free(AbortStrings[n]);
535    AbortStrings[n] = NULL;
536  }
537  numaborts = 0;
538
539  bzero(vector, sizeof(vector));
540  n = MakeArgs(script, &vector);
541#ifdef DEBUG
542  logprintf("n = %d\n", n);
543  for (i = 0; i < n; i++)
544    logprintf("  %s\n", vector[i]);
545#endif
546  argc = n;
547  argv = vector;
548  TimeoutSec = 30;
549  while (*argv) {
550    if (strcmp(*argv, "P_ZERO") == 0 ||
551	strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) {
552      ChangeParity(*argv++);
553      continue;
554    }
555    state = ExpectString(*argv++);
556    switch (state) {
557    case MATCH:
558      if (*argv)
559	SendString(*argv++);
560      break;
561    case ABORT:
562#ifdef notdef
563      HangupModem();
564#endif
565    case NOMATCH:
566      return(NOMATCH);
567    }
568  }
569  return(MATCH);
570}
571