xref: /freebsd/usr.sbin/ppp/chat.c (revision 05c7a37afb48ddd5ee1bd921a5d46fe59cc70b15)
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.8 1996/03/08 13:22:21 ache 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*2+1];
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(s) (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   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, "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 int
193 WaitforString(estr)
194 char *estr;
195 {
196   struct timeval timeout;
197   char *s, *str, ch;
198   char *inp;
199   fd_set rfds;
200   int i, nfds, nb;
201   char buff[200];
202 
203 #ifdef SIGALRM
204   int omask;
205   omask = sigblock(sigmask(SIGALRM));
206 #endif
207   (void) ExpandString(estr, buff, 0);
208   LogPrintf(LOG_CHAT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff);
209   str = buff;
210   inp = inbuff;
211 
212   if (strlen(str)>=IBSIZE){
213     str[IBSIZE]=0;
214     LogPrintf(LOG_CHAT, "Truncating String to %d character: %s\n", IBSIZE, str);
215   }
216 
217   nfds = modem + 1;
218   s = str;
219   for (;;) {
220     FD_ZERO(&rfds);
221     FD_SET(modem, &rfds);
222     /*
223      *  Because it is not clear whether select() modifies timeout value,
224      *  it is better to initialize timeout values everytime.
225      */
226     timeout.tv_sec = TimeoutSec;
227     timeout.tv_usec = 0;
228     i = select(nfds, &rfds, NULL, NULL, &timeout);
229 #ifdef notdef
230     TimerService();
231 #endif
232     if (i < 0) {
233 #ifdef SIGALRM
234       if (errno == EINTR)
235 	continue;
236       sigsetmask(omask);
237 #endif
238       perror("select");
239       *inp = 0;
240       return(NOMATCH);
241     } else if (i == 0) { 	/* Timeout reached! */
242       *inp = 0;
243       if (inp != inbuff)
244       LogPrintf(LOG_CHAT, "got: %s\n", inbuff);
245       LogPrintf(LOG_CHAT, "can't get (%d).\n", timeout.tv_sec);
246 #ifdef SIGALRM
247       sigsetmask(omask);
248 #endif
249       return(NOMATCH);
250     }
251     if (FD_ISSET(modem, &rfds)) {	/* got something */
252       if (DEV_IS_SYNC) {
253 	int length;
254 	if ((length=strlen(inbuff))>IBSIZE){
255 	  bcopy(&(inbuff[IBSIZE]),inbuff,IBSIZE+1); /* shuffle down next part*/
256 	  length=strlen(inbuff);
257 	}
258 	nb = read(modem, &(inbuff[length]), IBSIZE);
259 	inbuff[nb + length] = 0;
260 	if (strstr(inbuff, str)) {
261 #ifdef SIGALRM
262           sigsetmask(omask);
263 #endif
264 	  return(MATCH);
265 	}
266 	for (i = 0; i < numaborts; i++) {
267 	  if (strstr(inbuff, AbortStrings[i])) {
268 	    LogPrintf(LOG_CHAT, "Abort: %s\n", AbortStrings[i]);
269 #ifdef SIGALRM
270             sigsetmask(omask);
271 #endif
272 	    return(ABORT);
273 	  }
274 	}
275       } else {
276         read(modem, &ch, 1);
277         *inp++ = ch;
278         if (ch == *s) {
279 	  s++;
280 	  if (*s == '\0') {
281 #ifdef SIGALRM
282             sigsetmask(omask);
283 #endif
284 	    *inp = 0;
285 	    return(MATCH);
286 	  }
287         } else {
288 	  s = str;
289 	  if (inp == inbuff+ IBSIZE) {
290 	    bcopy(inp - 100, inbuff, 100);
291 	    inp = inbuff + 100;
292 	  }
293 	  for (i = 0; i < numaborts; i++) {	/* Look for Abort strings */
294 	    int len;
295 	    char *s1;
296 
297 	    s1 = AbortStrings[i];
298 	    len = strlen(s1);
299 	    if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) {
300 	      LogPrintf(LOG_CHAT, "Abort: %s\n", s1);
301 	      *inp = 0;
302 #ifdef SIGALRM
303       	      sigsetmask(omask);
304 #endif
305 	      return(ABORT);
306 	    }
307 	  }
308         }
309       }
310     }
311   }
312 #ifdef SIGALRM
313   sigsetmask(omask);
314 #endif
315 }
316 
317 void
318 ExecStr(command, out)
319 char *command, *out;
320 {
321   int pid;
322   int fids[2];
323   char *vector[20];
324   int stat, nb;
325   char *cp;
326   char tmp[300];
327   extern int errno;
328 
329   cp = inbuff + strlen(inbuff) - 1;
330   while (cp > inbuff) {
331     if (*cp < ' ' && *cp != '\t') {
332       cp++;
333       break;
334     }
335     cp--;
336   }
337   sprintf(tmp, "%s %s", command, cp);
338   (void) MakeArgs(tmp, &vector);
339 
340   pipe(fids);
341   pid = fork();
342   if (pid == 0) {
343     signal(SIGINT, SIG_DFL);
344     signal(SIGQUIT, SIG_DFL);
345     signal(SIGTERM, SIG_DFL);
346     signal(SIGHUP, SIG_DFL);
347     close(fids[0]);
348     dup2(fids[1], 1);
349     close(fids[1]);
350     nb = open("/dev/tty", O_RDWR);
351     dup2(nb, 0);
352 LogPrintf(LOG_CHAT, "exec: %s\n", command);
353     pid = execvp(command, vector);
354     LogPrintf(LOG_CHAT, "execvp failed for (%d/%d): %s\n", pid, errno, command);
355     exit(127);
356   } else {
357     close(fids[1]);
358     for (;;) {
359       nb = read(fids[0], out, 1);
360       if (nb <= 0)
361 	break;
362       out++;
363     }
364     *out = '\0';
365     close(fids[0]);
366     close(fids[1]);
367     waitpid(pid, &stat, WNOHANG);
368   }
369 }
370 
371 void
372 SendString(str)
373 char *str;
374 {
375   char *cp;
376   int nb, on;
377   char buff[200];
378 
379   if (abort_next) {
380     abort_next = 0;
381     ExpandString(str, buff, 0);
382     AbortStrings[numaborts++] = strdup(buff);
383   } else if (timeout_next) {
384     timeout_next = 0;
385     TimeoutSec = atoi(str);
386     if (TimeoutSec <= 0)
387       TimeoutSec = 30;
388   } else {
389     if (*str == '!') {
390       (void) ExpandString(str+1, buff+2, 0);
391       ExecStr(buff + 2, buff + 2);
392     } else {
393       (void) ExpandString(str, buff+2, 1);
394     }
395     if (strstr(str, "\\P")) { /* Do not log the password itself. */
396       LogPrintf(LOG_CHAT, "sending: %s\n", str);
397     } else {
398       LogPrintf(LOG_CHAT, "sending: %s\n", buff+2);
399     }
400     cp = buff;
401     if (DEV_IS_SYNC)
402       bcopy("\377\003", buff, 2);	/* Prepend HDLC header */
403     else
404       cp += 2;
405     on = strlen(cp);
406     nb = write(modem, cp, on);
407   }
408 }
409 
410 int
411 ExpectString(str)
412 char *str;
413 {
414   char *minus;
415   int state;
416 
417   if (strcmp(str, "ABORT") == 0) {
418     ++abort_next;
419     return(MATCH);
420   }
421   if (strcmp(str, "TIMEOUT") == 0) {
422     ++timeout_next;
423     return(MATCH);
424   }
425   LogPrintf(LOG_CHAT, "Expecting %s\n", str);
426   while (*str) {
427     /*
428      *  Check whether if string contains sub-send-expect.
429      */
430     for (minus = str; *minus; minus++) {
431       if (*minus == '-') {
432 	if (minus == str || minus[-1] != '\\')
433 	  break;
434       }
435     }
436     if (*minus == '-') {      /* We have sub-send-expect. */
437       *minus++ = '\0';
438       state = WaitforString(str);
439       if (state != NOMATCH)
440 	return(state);
441       /*
442        * Can't get expect string. Sendout send part.
443        */
444       str = minus;
445       for (minus = str; *minus; minus++) {
446 	if (*minus == '-') {
447 	  if (minus == str || minus[-1] != '\\')
448 	    break;
449 	}
450       }
451       if (*minus == '-') {
452 	*minus++ = '\0';
453 	SendString(str);
454 	str = minus;
455       } else {
456 	SendString(str);
457 	return(MATCH);
458       }
459     } else {
460       /*
461        *  Simple case. Wait for string.
462        */
463       return(WaitforString(str));
464     }
465   }
466   return(MATCH);
467 }
468 
469 int
470 DoChat(script)
471 char *script;
472 {
473   char *vector[20];
474   char **argv;
475   int argc, n, state;
476 #ifdef DEBUG
477   int i;
478 #endif
479 
480   timeout_next = abort_next = 0;
481   for (n = 0; AbortStrings[n]; n++) {
482     free(AbortStrings[n]);
483     AbortStrings[n] = NULL;
484   }
485   numaborts = 0;
486 
487   bzero(vector, sizeof(vector));
488   n = MakeArgs(script, &vector);
489 #ifdef DEBUG
490   logprintf("n = %d\n", n);
491   for (i = 0; i < n; i++)
492     logprintf("  %s\n", vector[i]);
493 #endif
494   argc = n;
495   argv = vector;
496   TimeoutSec = 30;
497   while (*argv) {
498     if (strcmp(*argv, "P_ZERO") == 0 ||
499 	strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) {
500       ChangeParity(*argv++);
501       continue;
502     }
503     state = ExpectString(*argv++);
504     switch (state) {
505     case MATCH:
506       if (*argv)
507 	SendString(*argv++);
508       break;
509     case ABORT:
510 #ifdef notdef
511       HangupModem();
512 #endif
513     case NOMATCH:
514       return(NOMATCH);
515     }
516   }
517   return(MATCH);
518 }
519