xref: /freebsd/usr.sbin/ppp/chat.c (revision d82e286489da73321a47e329d98a98817b0438b6)
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.4 1995/05/30 03:50:29 rgrimes 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 
44 static int TimeoutSec;
45 static int abort_next, timeout_next;
46 static int numaborts;
47 char *AbortStrings[50];
48 char inbuff[IBSIZE];
49 
50 extern int ChangeParity(char *);
51 
52 #define	MATCH	1
53 #define	NOMATCH	0
54 #define	ABORT	-1
55 
56 static char *
57 findblank(p, instring)
58 char *p;
59 int 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 
81 int
82 MakeArgs(script, pvect)
83 char *script;
84 char **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 (defined via `set phone'
117  *  \t  Tab character
118  */
119 char *
120 ExpandString(str, result, sendmode)
121 char *str;
122 char *result;
123 int sendmode;
124 {
125   int addcr = 0;
126 
127   if (sendmode)
128     addcr = 1;
129   while (*str) {
130     switch (*str) {
131     case '\\':
132       str++;
133       switch (*str) {
134       case 'c':
135 	if (sendmode)
136 	  addcr = 0;
137 	break;
138       case 'd':		/* Delay 2 seconds */
139         sleep(2); break;
140       case 'p':
141         usleep(250000); break;	/* Pause 0.25 sec */
142       case 'n':
143 	*result++ = '\n'; break;
144       case 'r':
145 	*result++ = '\r'; break;
146       case 's':
147 	*result++ = ' '; break;
148       case 't':
149 	*result++ = '\t'; break;
150       case 'P':
151 	bcopy(VarAuthKey, result, strlen(VarAuthKey));
152 	result += strlen(VarAuthKey);
153 	break;
154       case 'T':
155 	bcopy(VarPhone, result, strlen(VarPhone));
156 	result += strlen(VarPhone);
157 	break;
158       case 'U':
159 	bcopy(VarAuthName, result, strlen(VarAuthName));
160 	result += strlen(VarAuthName);
161 	break;
162       default:
163 	*result++ = *str; break;
164       }
165       if (*str) str++;
166       break;
167     case '^':
168       str++;
169       if (*str)
170 	*result++ = *str++ & 0x1f;
171       break;
172     default:
173       *result++ = *str++;
174       break;
175     }
176   }
177   if (addcr)
178     *result++ = '\r';
179   *result++ = '\0';
180   return(result);
181 }
182 
183 int
184 WaitforString(estr)
185 char *estr;
186 {
187   struct timeval timeout;
188   char *s, *str, ch;
189   char *inp;
190   fd_set rfds;
191   int i, nfds, nb;
192   char buff[200];
193 
194 #ifdef SIGALRM
195   int omask;
196   omask = sigblock(sigmask(SIGALRM));
197 #endif
198   (void) ExpandString(estr, buff, 0);
199   LogPrintf(LOG_CHAT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff);
200   str = buff;
201   inp = inbuff;
202 
203   nfds = modem + 1;
204   s = str;
205   for (;;) {
206     FD_ZERO(&rfds);
207     FD_SET(modem, &rfds);
208     /*
209      *  Because it is not clear whether select() modifies timeout value,
210      *  it is better to initialize timeout values everytime.
211      */
212     timeout.tv_sec = TimeoutSec;
213     timeout.tv_usec = 0;
214     i = select(nfds, &rfds, NULL, NULL, &timeout);
215 #ifdef notdef
216     TimerService();
217 #endif
218     if (i < 0) {
219 #ifdef SIGALRM
220       if (errno == EINTR)
221 	continue;
222       sigsetmask(omask);
223 #endif
224       perror("select");
225       *inp = 0;
226       return(NOMATCH);
227     } else if (i == 0) { 	/* Timeout reached! */
228       *inp = 0;
229       if (inp != inbuff)
230       LogPrintf(LOG_CHAT, "got: %s\n", inbuff);
231       LogPrintf(LOG_CHAT, "can't get (%d).\n", timeout.tv_sec);
232 #ifdef SIGALRM
233       sigsetmask(omask);
234 #endif
235       return(NOMATCH);
236     }
237     if (FD_ISSET(modem, &rfds)) {	/* got something */
238       if (DEV_IS_SYNC) {
239         nb = read(modem, inbuff, IBSIZE-1);
240 	inbuff[nb] = 0;
241 	if (strstr(inbuff, str)) {
242 #ifdef SIGALRM
243           sigsetmask(omask);
244 #endif
245 	  return(MATCH);
246 	}
247 	for (i = 0; i < numaborts; i++) {
248 	  if (strstr(inbuff, AbortStrings[i])) {
249 	    LogPrintf(LOG_CHAT, "Abort: %s\n", AbortStrings[i]);
250 #ifdef SIGALRM
251             sigsetmask(omask);
252 #endif
253 	    return(ABORT);
254 	  }
255 	}
256       } else {
257         read(modem, &ch, 1);
258         *inp++ = ch;
259         if (ch == *s) {
260 	  s++;
261 	  if (*s == '\0') {
262 #ifdef SIGALRM
263             sigsetmask(omask);
264 #endif
265 	    *inp = 0;
266 	    return(MATCH);
267 	  }
268         } else {
269 	  s = str;
270 	  if (inp == inbuff+ IBSIZE) {
271 	    bcopy(inp - 100, inbuff, 100);
272 	    inp = inbuff + 100;
273 	  }
274 	  for (i = 0; i < numaborts; i++) {	/* Look for Abort strings */
275 	    int len;
276 	    char *s1;
277 
278 	    s1 = AbortStrings[i];
279 	    len = strlen(s1);
280 	    if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) {
281 	      LogPrintf(LOG_CHAT, "Abort: %s\n", s1);
282 	      *inp = 0;
283 #ifdef SIGALRM
284       	      sigsetmask(omask);
285 #endif
286 	      return(ABORT);
287 	    }
288 	  }
289         }
290       }
291     }
292   }
293 #ifdef SIGALRM
294   sigsetmask(omask);
295 #endif
296 }
297 
298 void
299 ExecStr(command, out)
300 char *command, *out;
301 {
302   int pid;
303   int fids[2];
304   char *vector[20];
305   int stat, nb;
306   char *cp;
307   char tmp[300];
308   extern int errno;
309 
310   cp = inbuff + strlen(inbuff) - 1;
311   while (cp > inbuff) {
312     if (*cp < ' ' && *cp != '\t') {
313       cp++;
314       break;
315     }
316     cp--;
317   }
318   sprintf(tmp, "%s %s", command, cp);
319   (void) MakeArgs(tmp, &vector);
320 
321   pipe(fids);
322   pid = fork();
323   if (pid == 0) {
324     signal(SIGINT, SIG_DFL);
325     signal(SIGQUIT, SIG_DFL);
326     signal(SIGTERM, SIG_DFL);
327     signal(SIGHUP, SIG_DFL);
328     close(fids[0]);
329     dup2(fids[1], 1);
330     close(fids[1]);
331     nb = open("/dev/tty", O_RDWR);
332     dup2(nb, 0);
333 LogPrintf(LOG_CHAT, "exec: %s\n", command);
334     pid = execvp(command, vector);
335     LogPrintf(LOG_CHAT, "execvp failed for (%d/%d): %s\n", pid, errno, command);
336     exit(127);
337   } else {
338     close(fids[1]);
339     for (;;) {
340       nb = read(fids[0], out, 1);
341       if (nb <= 0)
342 	break;
343       out++;
344     }
345     *out = '\0';
346     close(fids[0]);
347     close(fids[1]);
348     waitpid(pid, &stat, WNOHANG);
349   }
350 }
351 
352 void
353 SendString(str)
354 char *str;
355 {
356   char *cp;
357   int nb, on;
358   char buff[200];
359 
360   if (abort_next) {
361     abort_next = 0;
362     ExpandString(str, buff, 0);
363     AbortStrings[numaborts++] = strdup(buff);
364   } else if (timeout_next) {
365     timeout_next = 0;
366     TimeoutSec = atoi(str);
367     if (TimeoutSec <= 0)
368       TimeoutSec = 30;
369   } else {
370     if (*str == '!') {
371       (void) ExpandString(str+1, buff+2, 0);
372       ExecStr(buff + 2, buff + 2);
373     } else {
374       (void) ExpandString(str, buff+2, 1);
375     }
376     if (strstr(str, "\\P")) { /* Do not log the password itself. */
377       LogPrintf(LOG_CHAT, "sending: %s\n", str);
378     } else {
379       LogPrintf(LOG_CHAT, "sending: %s\n", buff+2);
380     }
381     cp = buff;
382     if (DEV_IS_SYNC)
383       bcopy("\377\003", buff, 2);	/* Prepend HDLC header */
384     else
385       cp += 2;
386     on = strlen(cp);
387     nb = write(modem, cp, on);
388   }
389 }
390 
391 int
392 ExpectString(str)
393 char *str;
394 {
395   char *minus;
396   int state;
397 
398   if (strcmp(str, "ABORT") == 0) {
399     ++abort_next;
400     return(MATCH);
401   }
402   if (strcmp(str, "TIMEOUT") == 0) {
403     ++timeout_next;
404     return(MATCH);
405   }
406   LogPrintf(LOG_CHAT, "Expecting %s\n", str);
407   while (*str) {
408     /*
409      *  Check whether if string contains sub-send-expect.
410      */
411     for (minus = str; *minus; minus++) {
412       if (*minus == '-') {
413 	if (minus == str || minus[-1] != '\\')
414 	  break;
415       }
416     }
417     if (*minus == '-') {      /* We have sub-send-expect. */
418       *minus++ = '\0';
419       state = WaitforString(str);
420       if (state != NOMATCH)
421 	return(state);
422       /*
423        * Can't get expect string. Sendout send part.
424        */
425       str = minus;
426       for (minus = str; *minus; minus++) {
427 	if (*minus == '-') {
428 	  if (minus == str || minus[-1] != '\\')
429 	    break;
430 	}
431       }
432       if (*minus == '-') {
433 	*minus++ = '\0';
434 	SendString(str);
435 	str = minus;
436       } else {
437 	SendString(str);
438 	return(MATCH);
439       }
440     } else {
441       /*
442        *  Simple case. Wait for string.
443        */
444       return(WaitforString(str));
445     }
446   }
447   return(MATCH);
448 }
449 
450 int
451 DoChat(script)
452 char *script;
453 {
454   char *vector[20];
455   char **argv;
456   int argc, n, state;
457 #ifdef DEBUG
458   int i;
459 #endif
460 
461   timeout_next = abort_next = 0;
462   for (n = 0; AbortStrings[n]; n++) {
463     free(AbortStrings[n]);
464     AbortStrings[n] = NULL;
465   }
466   numaborts = 0;
467 
468   bzero(vector, sizeof(vector));
469   n = MakeArgs(script, &vector);
470 #ifdef DEBUG
471   logprintf("n = %d\n", n);
472   for (i = 0; i < n; i++)
473     logprintf("  %s\n", vector[i]);
474 #endif
475   argc = n;
476   argv = vector;
477   TimeoutSec = 30;
478   while (*argv) {
479     if (strcmp(*argv, "P_ZERO") == 0 ||
480 	strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) {
481       ChangeParity(*argv++);
482       continue;
483     }
484     state = ExpectString(*argv++);
485     switch (state) {
486     case MATCH:
487       if (*argv)
488 	SendString(*argv++);
489       break;
490     case ABORT:
491 #ifdef notdef
492       HangupModem();
493 #endif
494     case NOMATCH:
495       return(NOMATCH);
496     }
497   }
498   return(MATCH);
499 }
500