xref: /freebsd/usr.sbin/ppp/chat.c (revision 42d4d396689ff18f91456f636cf531b2f116e79b)
11ae349f5Scvs2svn /*
21ae349f5Scvs2svn  *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
31ae349f5Scvs2svn  *
41ae349f5Scvs2svn  *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
51ae349f5Scvs2svn  *
61ae349f5Scvs2svn  *  Most of codes are derived from chat.c by Karl Fox (karl@MorningStar.Com).
71ae349f5Scvs2svn  *
81ae349f5Scvs2svn  *	Chat -- a program for automatic session establishment (i.e. dial
91ae349f5Scvs2svn  *		the phone and log in).
101ae349f5Scvs2svn  *
111ae349f5Scvs2svn  *	This software is in the public domain.
121ae349f5Scvs2svn  *
131ae349f5Scvs2svn  *	Please send all bug reports, requests for information, etc. to:
141ae349f5Scvs2svn  *
151ae349f5Scvs2svn  *		Karl Fox <karl@MorningStar.Com>
161ae349f5Scvs2svn  *		Morning Star Technologies, Inc.
171ae349f5Scvs2svn  *		1760 Zollinger Road
181ae349f5Scvs2svn  *		Columbus, OH  43221
191ae349f5Scvs2svn  *		(614)451-1883
201ae349f5Scvs2svn  *
2142d4d396SBrian Somers  * $Id: chat.c,v 1.44.2.4 1998/02/06 02:23:30 brian Exp $
221ae349f5Scvs2svn  *
231ae349f5Scvs2svn  *  TODO:
241ae349f5Scvs2svn  *	o Support more UUCP compatible control sequences.
251ae349f5Scvs2svn  *	o Dialing shoud not block monitor process.
261ae349f5Scvs2svn  *	o Reading modem by select should be unified into main.c
271ae349f5Scvs2svn  */
281ae349f5Scvs2svn #include <sys/param.h>
291ae349f5Scvs2svn #include <netinet/in.h>
301ae349f5Scvs2svn 
311ae349f5Scvs2svn #include <ctype.h>
321ae349f5Scvs2svn #include <errno.h>
331ae349f5Scvs2svn #include <fcntl.h>
341ae349f5Scvs2svn #include <setjmp.h>
351ae349f5Scvs2svn #include <signal.h>
361ae349f5Scvs2svn #include <stdio.h>
371ae349f5Scvs2svn #include <stdlib.h>
381ae349f5Scvs2svn #include <string.h>
391ae349f5Scvs2svn #include <sys/time.h>
401ae349f5Scvs2svn #include <sys/wait.h>
411ae349f5Scvs2svn #include <termios.h>
421ae349f5Scvs2svn #include <unistd.h>
431ae349f5Scvs2svn 
441ae349f5Scvs2svn #include "command.h"
451ae349f5Scvs2svn #include "mbuf.h"
461ae349f5Scvs2svn #include "log.h"
471ae349f5Scvs2svn #include "defs.h"
481ae349f5Scvs2svn #include "timer.h"
491ae349f5Scvs2svn #include "loadalias.h"
501ae349f5Scvs2svn #include "vars.h"
511ae349f5Scvs2svn #include "modem.h"
526140ba11SBrian Somers #include "hdlc.h"
536140ba11SBrian Somers #include "throughput.h"
546140ba11SBrian Somers #include "fsm.h"
556140ba11SBrian Somers #include "lcp.h"
566140ba11SBrian Somers #include "link.h"
576140ba11SBrian Somers #include "async.h"
5842d4d396SBrian Somers #include "descriptor.h"
5963b73463SBrian Somers #include "physical.h"
602289f246SBrian Somers #include "chat.h"
611ae349f5Scvs2svn 
621ae349f5Scvs2svn #ifndef isblank
631ae349f5Scvs2svn #define	isblank(c)	((c) == '\t' || (c) == ' ')
641ae349f5Scvs2svn #endif
651ae349f5Scvs2svn 
661ae349f5Scvs2svn 
671ae349f5Scvs2svn #define	IBSIZE LINE_LEN
681ae349f5Scvs2svn 
691ae349f5Scvs2svn static int TimeoutSec;
701ae349f5Scvs2svn static int abort_next, timeout_next;
711ae349f5Scvs2svn static int numaborts;
721ae349f5Scvs2svn static char *AbortStrings[50];
731ae349f5Scvs2svn static char inbuff[IBSIZE * 2 + 1];
741ae349f5Scvs2svn static jmp_buf ChatEnv;
751ae349f5Scvs2svn 
761ae349f5Scvs2svn #define	MATCH	1
771ae349f5Scvs2svn #define	NOMATCH	0
781ae349f5Scvs2svn #define	ABORT	-1
791ae349f5Scvs2svn 
801ae349f5Scvs2svn static char *
811ae349f5Scvs2svn findblank(char *p, int instring)
821ae349f5Scvs2svn {
831ae349f5Scvs2svn   if (instring) {
841ae349f5Scvs2svn     while (*p) {
851ae349f5Scvs2svn       if (*p == '\\') {
861ae349f5Scvs2svn 	strcpy(p, p + 1);
871ae349f5Scvs2svn 	if (!*p)
881ae349f5Scvs2svn 	  break;
891ae349f5Scvs2svn       } else if (*p == '"')
901ae349f5Scvs2svn 	return (p);
911ae349f5Scvs2svn       p++;
921ae349f5Scvs2svn     }
931ae349f5Scvs2svn   } else {
941ae349f5Scvs2svn     while (*p) {
951ae349f5Scvs2svn       if (isblank(*p))
961ae349f5Scvs2svn 	return (p);
971ae349f5Scvs2svn       p++;
981ae349f5Scvs2svn     }
991ae349f5Scvs2svn   }
1001ae349f5Scvs2svn   return p;
1011ae349f5Scvs2svn }
1021ae349f5Scvs2svn 
1031ae349f5Scvs2svn int
1041ae349f5Scvs2svn MakeArgs(char *script, char **pvect, int maxargs)
1051ae349f5Scvs2svn {
1061ae349f5Scvs2svn   int nargs, nb;
1071ae349f5Scvs2svn   int instring;
1081ae349f5Scvs2svn 
1091ae349f5Scvs2svn   nargs = 0;
1101ae349f5Scvs2svn   while (*script) {
1111ae349f5Scvs2svn     nb = strspn(script, " \t");
1121ae349f5Scvs2svn     script += nb;
1131ae349f5Scvs2svn     if (*script) {
1141ae349f5Scvs2svn       if (*script == '"') {
1151ae349f5Scvs2svn 	instring = 1;
1161ae349f5Scvs2svn 	script++;
1171ae349f5Scvs2svn 	if (*script == '\0')
1181ae349f5Scvs2svn 	  break;		/* Shouldn't return here. Need to null
1191ae349f5Scvs2svn 				 * terminate below */
1201ae349f5Scvs2svn       } else
1211ae349f5Scvs2svn 	instring = 0;
1221ae349f5Scvs2svn       if (nargs >= maxargs - 1)
1231ae349f5Scvs2svn 	break;
1241ae349f5Scvs2svn       *pvect++ = script;
1251ae349f5Scvs2svn       nargs++;
1261ae349f5Scvs2svn       script = findblank(script, instring);
1271ae349f5Scvs2svn       if (*script)
1281ae349f5Scvs2svn 	*script++ = '\0';
1291ae349f5Scvs2svn     }
1301ae349f5Scvs2svn   }
1311ae349f5Scvs2svn   *pvect = NULL;
1321ae349f5Scvs2svn   return nargs;
1331ae349f5Scvs2svn }
1341ae349f5Scvs2svn 
1351ae349f5Scvs2svn /*
1361ae349f5Scvs2svn  *  \c	don't add a cr
1371ae349f5Scvs2svn  *  \d  Sleep a little (delay 2 seconds
1381ae349f5Scvs2svn  *  \n  Line feed character
1391ae349f5Scvs2svn  *  \P  Auth Key password
1401ae349f5Scvs2svn  *  \p  pause 0.25 sec
1411ae349f5Scvs2svn  *  \r	Carrige return character
1421ae349f5Scvs2svn  *  \s  Space character
1431ae349f5Scvs2svn  *  \T  Telephone number(s) (defined via `set phone')
1441ae349f5Scvs2svn  *  \t  Tab character
1451ae349f5Scvs2svn  *  \U  Auth User
1461ae349f5Scvs2svn  */
1471ae349f5Scvs2svn char *
1481ae349f5Scvs2svn ExpandString(const char *str, char *result, int reslen, int sendmode)
1491ae349f5Scvs2svn {
1501ae349f5Scvs2svn   int addcr = 0;
1511ae349f5Scvs2svn   char *phone;
1521ae349f5Scvs2svn 
1531ae349f5Scvs2svn   result[--reslen] = '\0';
1541ae349f5Scvs2svn   if (sendmode)
1551ae349f5Scvs2svn     addcr = 1;
1561ae349f5Scvs2svn   while (*str && reslen > 0) {
1571ae349f5Scvs2svn     switch (*str) {
1581ae349f5Scvs2svn     case '\\':
1591ae349f5Scvs2svn       str++;
1601ae349f5Scvs2svn       switch (*str) {
1611ae349f5Scvs2svn       case 'c':
1621ae349f5Scvs2svn 	if (sendmode)
1631ae349f5Scvs2svn 	  addcr = 0;
1641ae349f5Scvs2svn 	break;
1651ae349f5Scvs2svn       case 'd':		/* Delay 2 seconds */
1661ae349f5Scvs2svn 	nointr_sleep(2);
1671ae349f5Scvs2svn 	break;
1681ae349f5Scvs2svn       case 'p':
1691ae349f5Scvs2svn 	nointr_usleep(250000);
1701ae349f5Scvs2svn 	break;			/* Pause 0.25 sec */
1711ae349f5Scvs2svn       case 'n':
1721ae349f5Scvs2svn 	*result++ = '\n';
1731ae349f5Scvs2svn 	reslen--;
1741ae349f5Scvs2svn 	break;
1751ae349f5Scvs2svn       case 'r':
1761ae349f5Scvs2svn 	*result++ = '\r';
1771ae349f5Scvs2svn 	reslen--;
1781ae349f5Scvs2svn 	break;
1791ae349f5Scvs2svn       case 's':
1801ae349f5Scvs2svn 	*result++ = ' ';
1811ae349f5Scvs2svn 	reslen--;
1821ae349f5Scvs2svn 	break;
1831ae349f5Scvs2svn       case 't':
1841ae349f5Scvs2svn 	*result++ = '\t';
1851ae349f5Scvs2svn 	reslen--;
1861ae349f5Scvs2svn 	break;
1871ae349f5Scvs2svn       case 'P':
1881ae349f5Scvs2svn 	strncpy(result, VarAuthKey, reslen);
1891ae349f5Scvs2svn 	reslen -= strlen(result);
1901ae349f5Scvs2svn 	result += strlen(result);
1911ae349f5Scvs2svn 	break;
1921ae349f5Scvs2svn       case 'T':
1931ae349f5Scvs2svn 	if (VarAltPhone == NULL) {
1941ae349f5Scvs2svn 	  if (VarNextPhone == NULL) {
1951ae349f5Scvs2svn 	    strncpy(VarPhoneCopy, VarPhoneList, sizeof VarPhoneCopy - 1);
1961ae349f5Scvs2svn 	    VarPhoneCopy[sizeof VarPhoneCopy - 1] = '\0';
1971ae349f5Scvs2svn 	    VarNextPhone = VarPhoneCopy;
1981ae349f5Scvs2svn 	  }
1991ae349f5Scvs2svn 	  VarAltPhone = strsep(&VarNextPhone, ":");
2001ae349f5Scvs2svn 	}
2011ae349f5Scvs2svn 	phone = strsep(&VarAltPhone, "|");
2021ae349f5Scvs2svn 	strncpy(result, phone, reslen);
2031ae349f5Scvs2svn 	reslen -= strlen(result);
2041ae349f5Scvs2svn 	result += strlen(result);
2051ae349f5Scvs2svn 	if (VarTerm)
2061ae349f5Scvs2svn 	  fprintf(VarTerm, "Phone: %s\n", phone);
2071ae349f5Scvs2svn 	LogPrintf(LogPHASE, "Phone: %s\n", phone);
2081ae349f5Scvs2svn 	break;
2091ae349f5Scvs2svn       case 'U':
2101ae349f5Scvs2svn 	strncpy(result, VarAuthName, reslen);
2111ae349f5Scvs2svn 	reslen -= strlen(result);
2121ae349f5Scvs2svn 	result += strlen(result);
2131ae349f5Scvs2svn 	break;
2141ae349f5Scvs2svn       default:
2151ae349f5Scvs2svn 	reslen--;
2161ae349f5Scvs2svn 	*result++ = *str;
2171ae349f5Scvs2svn 	break;
2181ae349f5Scvs2svn       }
2191ae349f5Scvs2svn       if (*str)
2201ae349f5Scvs2svn 	str++;
2211ae349f5Scvs2svn       break;
2221ae349f5Scvs2svn     case '^':
2231ae349f5Scvs2svn       str++;
2241ae349f5Scvs2svn       if (*str) {
2251ae349f5Scvs2svn 	*result++ = *str++ & 0x1f;
2261ae349f5Scvs2svn 	reslen--;
2271ae349f5Scvs2svn       }
2281ae349f5Scvs2svn       break;
2291ae349f5Scvs2svn     default:
2301ae349f5Scvs2svn       *result++ = *str++;
2311ae349f5Scvs2svn       reslen--;
2321ae349f5Scvs2svn       break;
2331ae349f5Scvs2svn     }
2341ae349f5Scvs2svn   }
2351ae349f5Scvs2svn   if (--reslen > 0) {
2361ae349f5Scvs2svn     if (addcr)
2371ae349f5Scvs2svn       *result++ = '\r';
2381ae349f5Scvs2svn   }
2391ae349f5Scvs2svn   if (--reslen > 0)
2401ae349f5Scvs2svn     *result++ = '\0';
2411ae349f5Scvs2svn   return (result);
2421ae349f5Scvs2svn }
2431ae349f5Scvs2svn 
2441ae349f5Scvs2svn #define MAXLOGBUFF LINE_LEN
2451ae349f5Scvs2svn static char logbuff[MAXLOGBUFF];
2461ae349f5Scvs2svn static int loglen = 0;
2471ae349f5Scvs2svn 
2481ae349f5Scvs2svn static void
2491ae349f5Scvs2svn clear_log(void)
2501ae349f5Scvs2svn {
2511ae349f5Scvs2svn   memset(logbuff, 0, MAXLOGBUFF);
2521ae349f5Scvs2svn   loglen = 0;
2531ae349f5Scvs2svn }
2541ae349f5Scvs2svn 
2551ae349f5Scvs2svn static void
2561ae349f5Scvs2svn flush_log(void)
2571ae349f5Scvs2svn {
2581ae349f5Scvs2svn   if (LogIsKept(LogCONNECT))
2591ae349f5Scvs2svn     LogPrintf(LogCONNECT, "%s\n", logbuff);
2601ae349f5Scvs2svn   else if (LogIsKept(LogCARRIER) && strstr(logbuff, "CARRIER"))
2611ae349f5Scvs2svn     LogPrintf(LogCARRIER, "%s\n", logbuff);
2621ae349f5Scvs2svn 
2631ae349f5Scvs2svn   clear_log();
2641ae349f5Scvs2svn }
2651ae349f5Scvs2svn 
2661ae349f5Scvs2svn static void
2671ae349f5Scvs2svn connect_log(const char *str, int single_p)
2681ae349f5Scvs2svn {
2691ae349f5Scvs2svn   int space = MAXLOGBUFF - loglen - 1;
2701ae349f5Scvs2svn 
2711ae349f5Scvs2svn   while (space--) {
2721ae349f5Scvs2svn     if (*str == '\n') {
2731ae349f5Scvs2svn       flush_log();
2741ae349f5Scvs2svn     } else {
2751ae349f5Scvs2svn       logbuff[loglen++] = *str;
2761ae349f5Scvs2svn     }
2771ae349f5Scvs2svn     if (single_p || !*++str)
2781ae349f5Scvs2svn       break;
2791ae349f5Scvs2svn   }
2801ae349f5Scvs2svn   if (!space)
2811ae349f5Scvs2svn     flush_log();
2821ae349f5Scvs2svn }
2831ae349f5Scvs2svn 
2841ae349f5Scvs2svn static void
28563b73463SBrian Somers ExecStr(struct physical *physical, char *command, char *out, int olen)
2861ae349f5Scvs2svn {
2871ae349f5Scvs2svn   pid_t pid;
2881ae349f5Scvs2svn   int fids[2];
2891ae349f5Scvs2svn   char *vector[MAXARGS], *startout, *endout;
2901ae349f5Scvs2svn   int stat, nb;
2911ae349f5Scvs2svn 
2921ae349f5Scvs2svn   LogPrintf(LogCHAT, "Exec: %s\n", command);
2931ae349f5Scvs2svn   MakeArgs(command, vector, VECSIZE(vector));
2941ae349f5Scvs2svn 
2951ae349f5Scvs2svn   if (pipe(fids) < 0) {
2961ae349f5Scvs2svn     LogPrintf(LogCHAT, "Unable to create pipe in ExecStr: %s\n",
2971ae349f5Scvs2svn 	      strerror(errno));
2981ae349f5Scvs2svn     longjmp(ChatEnv, 2);
2991ae349f5Scvs2svn   }
3001ae349f5Scvs2svn   if ((pid = fork()) == 0) {
3011ae349f5Scvs2svn     TermTimerService();
3021ae349f5Scvs2svn     signal(SIGINT, SIG_DFL);
3031ae349f5Scvs2svn     signal(SIGQUIT, SIG_DFL);
3041ae349f5Scvs2svn     signal(SIGTERM, SIG_DFL);
3051ae349f5Scvs2svn     signal(SIGHUP, SIG_DFL);
3061ae349f5Scvs2svn     signal(SIGALRM, SIG_DFL);
30763b73463SBrian Somers 	/* XXX-ML This looks like it might need more encapsulation. */
30863b73463SBrian Somers     if (Physical_GetFD(physical) == 2) {
30963b73463SBrian Somers 	   Physical_DupAndClose(physical);
3101ae349f5Scvs2svn     }
3111ae349f5Scvs2svn     close(fids[0]);
3121ae349f5Scvs2svn     dup2(fids[1], 2);
3131ae349f5Scvs2svn     close(fids[1]);
31463b73463SBrian Somers     dup2(Physical_GetFD(physical), 0);
31563b73463SBrian Somers     dup2(Physical_GetFD(physical), 1);
3161ae349f5Scvs2svn     if ((nb = open("/dev/tty", O_RDWR)) > 3) {
3171ae349f5Scvs2svn       dup2(nb, 3);
3181ae349f5Scvs2svn       close(nb);
3191ae349f5Scvs2svn     }
3201ae349f5Scvs2svn     setuid(geteuid());
3211ae349f5Scvs2svn     execvp(vector[0], vector);
3221ae349f5Scvs2svn     fprintf(stderr, "execvp failed: %s: %s\n", vector[0], strerror(errno));
3231ae349f5Scvs2svn     exit(127);
3241ae349f5Scvs2svn   } else {
3251ae349f5Scvs2svn     char *name = strdup(vector[0]);
3261ae349f5Scvs2svn 
3271ae349f5Scvs2svn     close(fids[1]);
3281ae349f5Scvs2svn     endout = out + olen - 1;
3291ae349f5Scvs2svn     startout = out;
3301ae349f5Scvs2svn     while (out < endout) {
3311ae349f5Scvs2svn       nb = read(fids[0], out, 1);
3321ae349f5Scvs2svn       if (nb <= 0)
3331ae349f5Scvs2svn 	break;
3341ae349f5Scvs2svn       out++;
3351ae349f5Scvs2svn     }
3361ae349f5Scvs2svn     *out = '\0';
3371ae349f5Scvs2svn     close(fids[0]);
3381ae349f5Scvs2svn     close(fids[1]);
3391ae349f5Scvs2svn     waitpid(pid, &stat, WNOHANG);
3401ae349f5Scvs2svn     if (WIFSIGNALED(stat)) {
3411ae349f5Scvs2svn       LogPrintf(LogWARN, "%s: signal %d\n", name, WTERMSIG(stat));
3421ae349f5Scvs2svn       free(name);
3431ae349f5Scvs2svn       longjmp(ChatEnv, 3);
3441ae349f5Scvs2svn     } else if (WIFEXITED(stat)) {
3451ae349f5Scvs2svn       switch (WEXITSTATUS(stat)) {
3461ae349f5Scvs2svn         case 0:
3471ae349f5Scvs2svn           free(name);
3481ae349f5Scvs2svn           break;
3491ae349f5Scvs2svn         case 127:
3501ae349f5Scvs2svn           LogPrintf(LogWARN, "%s: %s\n", name, startout);
3511ae349f5Scvs2svn           free(name);
3521ae349f5Scvs2svn           longjmp(ChatEnv, 4);
3531ae349f5Scvs2svn           break;
3541ae349f5Scvs2svn         default:
3551ae349f5Scvs2svn           LogPrintf(LogWARN, "%s: exit %d\n", name, WEXITSTATUS(stat));
3561ae349f5Scvs2svn           free(name);
3571ae349f5Scvs2svn           longjmp(ChatEnv, 5);
3581ae349f5Scvs2svn           break;
3591ae349f5Scvs2svn       }
3601ae349f5Scvs2svn     } else {
3611ae349f5Scvs2svn       LogPrintf(LogWARN, "%s: Unexpected exit result\n", name);
3621ae349f5Scvs2svn       free(name);
3631ae349f5Scvs2svn       longjmp(ChatEnv, 6);
3641ae349f5Scvs2svn     }
3651ae349f5Scvs2svn   }
3661ae349f5Scvs2svn }
3671ae349f5Scvs2svn 
3681ae349f5Scvs2svn static int
36963b73463SBrian Somers WaitforString(struct physical *physical, const char *estr)
3701ae349f5Scvs2svn {
3711ae349f5Scvs2svn   struct timeval timeout;
3721ae349f5Scvs2svn   char *s, *str, ch;
3731ae349f5Scvs2svn   char *inp;
3741ae349f5Scvs2svn   fd_set rfds;
3751ae349f5Scvs2svn   int i, nfds, nb;
3761ae349f5Scvs2svn   char buff[IBSIZE];
3771ae349f5Scvs2svn #ifdef SIGALRM
3781ae349f5Scvs2svn   int omask;
3791ae349f5Scvs2svn 
3801ae349f5Scvs2svn   omask = sigblock(sigmask(SIGALRM));
3811ae349f5Scvs2svn #endif
3821ae349f5Scvs2svn   clear_log();
3831ae349f5Scvs2svn   if (*estr == '!') {
3841ae349f5Scvs2svn     ExpandString(estr + 1, buff, sizeof buff, 0);
38563b73463SBrian Somers     ExecStr(physical, buff, buff, sizeof buff);
3861ae349f5Scvs2svn   } else {
3871ae349f5Scvs2svn     ExpandString(estr, buff, sizeof buff, 0);
3881ae349f5Scvs2svn   }
3891ae349f5Scvs2svn   if (LogIsKept(LogCHAT)) {
3901ae349f5Scvs2svn     s = buff + strlen(buff) - 1;
3911ae349f5Scvs2svn     while (s >= buff && *s == '\n')
3921ae349f5Scvs2svn       s--;
3931ae349f5Scvs2svn     if (!strcmp(estr, buff))
3941ae349f5Scvs2svn       LogPrintf(LogCHAT, "Wait for (%d): %.*s\n",
3951ae349f5Scvs2svn                 TimeoutSec, s - buff + 1, buff);
3961ae349f5Scvs2svn     else
3971ae349f5Scvs2svn       LogPrintf(LogCHAT, "Wait for (%d): %s --> %.*s\n",
3981ae349f5Scvs2svn                 TimeoutSec, estr, s - buff + 1, buff);
3991ae349f5Scvs2svn   }
4001ae349f5Scvs2svn 
4011ae349f5Scvs2svn   if (buff[0] == '\0')
4021ae349f5Scvs2svn     return (MATCH);
4031ae349f5Scvs2svn 
4041ae349f5Scvs2svn   str = buff;
4051ae349f5Scvs2svn   inp = inbuff;
4061ae349f5Scvs2svn 
4071ae349f5Scvs2svn   if (strlen(str) >= IBSIZE) {
4081ae349f5Scvs2svn     str[IBSIZE - 1] = 0;
4091ae349f5Scvs2svn     LogPrintf(LogCHAT, "Truncating String to %d character: %s\n", IBSIZE, str);
4101ae349f5Scvs2svn   }
41163b73463SBrian Somers   /* XXX-ML - this look REALLY fishy.  */
41263b73463SBrian Somers   nfds = Physical_GetFD(physical) + 1;
4131ae349f5Scvs2svn   s = str;
4141ae349f5Scvs2svn   for (;;) {
4151ae349f5Scvs2svn     FD_ZERO(&rfds);
41663b73463SBrian Somers     FD_SET(Physical_GetFD(physical), &rfds);
4171ae349f5Scvs2svn 
4181ae349f5Scvs2svn     /*
4191ae349f5Scvs2svn      * Because it is not clear whether select() modifies timeout value, it is
4201ae349f5Scvs2svn      * better to initialize timeout values everytime.
4211ae349f5Scvs2svn      */
4221ae349f5Scvs2svn     timeout.tv_sec = TimeoutSec;
4231ae349f5Scvs2svn     timeout.tv_usec = 0;
4241ae349f5Scvs2svn     i = select(nfds, &rfds, NULL, NULL, &timeout);
4251ae349f5Scvs2svn #ifdef notdef
4261ae349f5Scvs2svn     TimerService();
4271ae349f5Scvs2svn #endif
4281ae349f5Scvs2svn     if (i < 0) {
4291ae349f5Scvs2svn #ifdef SIGALRM
4301ae349f5Scvs2svn       if (errno == EINTR)
4311ae349f5Scvs2svn 	continue;
4321ae349f5Scvs2svn       sigsetmask(omask);
4331ae349f5Scvs2svn #endif
4341ae349f5Scvs2svn       LogPrintf(LogERROR, "WaitForString: select(): %s\n", strerror(errno));
4351ae349f5Scvs2svn       *inp = 0;
4361ae349f5Scvs2svn       return (NOMATCH);
4371ae349f5Scvs2svn     } else if (i == 0) {	/* Timeout reached! */
4381ae349f5Scvs2svn       *inp = 0;
4391ae349f5Scvs2svn       if (inp != inbuff)
4401ae349f5Scvs2svn 	LogPrintf(LogCHAT, "Got: %s\n", inbuff);
4411ae349f5Scvs2svn       LogPrintf(LogCHAT, "Can't get (%d).\n", timeout.tv_sec);
4421ae349f5Scvs2svn #ifdef SIGALRM
4431ae349f5Scvs2svn       sigsetmask(omask);
4441ae349f5Scvs2svn #endif
4451ae349f5Scvs2svn       return (NOMATCH);
4461ae349f5Scvs2svn     }
44763b73463SBrian Somers     if (Physical_FD_ISSET(physical, &rfds)) {	/* got something */
44863b73463SBrian Somers       if (Physical_IsSync(physical)) {
4491ae349f5Scvs2svn 	int length;
4501ae349f5Scvs2svn 
4511ae349f5Scvs2svn 	if ((length = strlen(inbuff)) > IBSIZE) {
4521ae349f5Scvs2svn 	  /* shuffle down next part */
4531ae349f5Scvs2svn 	  memcpy(inbuff, &(inbuff[IBSIZE]), IBSIZE + 1);
4541ae349f5Scvs2svn 	  length = strlen(inbuff);
4551ae349f5Scvs2svn 	}
45663b73463SBrian Somers 	if (length + IBSIZE > sizeof(inbuff))
45763b73463SBrian Somers 	    abort();		/* Bug & security problem */
45863b73463SBrian Somers 	nb = Physical_Read(physical, &(inbuff[length]), IBSIZE);
4591ae349f5Scvs2svn 	inbuff[nb + length] = 0;
4601ae349f5Scvs2svn 	connect_log(inbuff, 0);
4611ae349f5Scvs2svn 	if (strstr(inbuff, str)) {
4621ae349f5Scvs2svn #ifdef SIGALRM
4631ae349f5Scvs2svn 	  sigsetmask(omask);
4641ae349f5Scvs2svn #endif
4651ae349f5Scvs2svn 	  flush_log();
4661ae349f5Scvs2svn 	  return (MATCH);
4671ae349f5Scvs2svn 	}
4681ae349f5Scvs2svn 	for (i = 0; i < numaborts; i++) {
4691ae349f5Scvs2svn 	  if (strstr(inbuff, AbortStrings[i])) {
4701ae349f5Scvs2svn 	    LogPrintf(LogCHAT, "Abort: %s\n", AbortStrings[i]);
4711ae349f5Scvs2svn #ifdef SIGALRM
4721ae349f5Scvs2svn 	    sigsetmask(omask);
4731ae349f5Scvs2svn #endif
4741ae349f5Scvs2svn 	    flush_log();
4751ae349f5Scvs2svn 	    return (ABORT);
4761ae349f5Scvs2svn 	  }
4771ae349f5Scvs2svn 	}
4781ae349f5Scvs2svn       } else {
47963b73463SBrian Somers 	if (Physical_Read(physical, &ch, 1) < 0) {
4801ae349f5Scvs2svn 	  LogPrintf(LogERROR, "read error: %s\n", strerror(errno));
4811ae349f5Scvs2svn 	  *inp = '\0';
4821ae349f5Scvs2svn 	  return (NOMATCH);
4831ae349f5Scvs2svn 	}
4841ae349f5Scvs2svn 	connect_log(&ch, 1);
4851ae349f5Scvs2svn 	*inp++ = ch;
4861ae349f5Scvs2svn 	if (ch == *s) {
4871ae349f5Scvs2svn 	  s++;
4881ae349f5Scvs2svn 	  if (*s == '\0') {
4891ae349f5Scvs2svn #ifdef SIGALRM
4901ae349f5Scvs2svn 	    sigsetmask(omask);
4911ae349f5Scvs2svn #endif
4921ae349f5Scvs2svn 	    *inp = 0;
4931ae349f5Scvs2svn 	    flush_log();
4941ae349f5Scvs2svn 	    return (MATCH);
4951ae349f5Scvs2svn 	  }
4961ae349f5Scvs2svn 	} else
4971ae349f5Scvs2svn 	  s = str;
4981ae349f5Scvs2svn 	if (inp == inbuff + IBSIZE) {
4991ae349f5Scvs2svn 	  memcpy(inbuff, inp - 100, 100);
5001ae349f5Scvs2svn 	  inp = inbuff + 100;
5011ae349f5Scvs2svn 	}
5021ae349f5Scvs2svn 	if (s == str) {
5031ae349f5Scvs2svn 	  for (i = 0; i < numaborts; i++) {	/* Look for Abort strings */
5041ae349f5Scvs2svn 	    int len;
5051ae349f5Scvs2svn 	    char *s1;
5061ae349f5Scvs2svn 
5071ae349f5Scvs2svn 	    s1 = AbortStrings[i];
5081ae349f5Scvs2svn 	    len = strlen(s1);
5091ae349f5Scvs2svn 	    if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) {
5101ae349f5Scvs2svn 	      LogPrintf(LogCHAT, "Abort: %s\n", s1);
5111ae349f5Scvs2svn 	      *inp = 0;
5121ae349f5Scvs2svn #ifdef SIGALRM
5131ae349f5Scvs2svn 	      sigsetmask(omask);
5141ae349f5Scvs2svn #endif
5151ae349f5Scvs2svn 	      flush_log();
5161ae349f5Scvs2svn 	      return (ABORT);
5171ae349f5Scvs2svn 	    }
5181ae349f5Scvs2svn 	  }
5191ae349f5Scvs2svn 	}
5201ae349f5Scvs2svn       }
5211ae349f5Scvs2svn     }
5221ae349f5Scvs2svn   }
5231ae349f5Scvs2svn }
5241ae349f5Scvs2svn 
5251ae349f5Scvs2svn static void
52663b73463SBrian Somers SendString(struct physical *physical, const char *str)
5271ae349f5Scvs2svn {
5281ae349f5Scvs2svn   char *cp;
5291ae349f5Scvs2svn   int on;
5301ae349f5Scvs2svn   char buff[LINE_LEN];
5311ae349f5Scvs2svn 
5321ae349f5Scvs2svn   if (abort_next) {
5331ae349f5Scvs2svn     abort_next = 0;
5341ae349f5Scvs2svn     ExpandString(str, buff, sizeof buff, 0);
5351ae349f5Scvs2svn     AbortStrings[numaborts++] = strdup(buff);
5361ae349f5Scvs2svn   } else if (timeout_next) {
5371ae349f5Scvs2svn     timeout_next = 0;
5381ae349f5Scvs2svn     TimeoutSec = atoi(str);
5391ae349f5Scvs2svn     if (TimeoutSec <= 0)
5401ae349f5Scvs2svn       TimeoutSec = 30;
5411ae349f5Scvs2svn   } else {
5421ae349f5Scvs2svn     if (*str == '!') {
5431ae349f5Scvs2svn       ExpandString(str + 1, buff + 2, sizeof buff - 2, 0);
54463b73463SBrian Somers       ExecStr(physical, buff + 2, buff + 2, sizeof buff - 2);
5451ae349f5Scvs2svn     } else {
5461ae349f5Scvs2svn       ExpandString(str, buff + 2, sizeof buff - 2, 1);
5471ae349f5Scvs2svn     }
5481ae349f5Scvs2svn     if (strstr(str, "\\P"))	/* Do not log the password itself. */
5491ae349f5Scvs2svn       LogPrintf(LogCHAT, "Sending: %s", str);
5501ae349f5Scvs2svn     else {
5511ae349f5Scvs2svn       cp = buff + strlen(buff + 2) + 1;
5521ae349f5Scvs2svn       while (cp >= buff + 2 && *cp == '\n')
5531ae349f5Scvs2svn         cp--;
5541ae349f5Scvs2svn       LogPrintf(LogCHAT, "Sending: %.*s\n", cp - buff - 1, buff + 2);
5551ae349f5Scvs2svn     }
5561ae349f5Scvs2svn     cp = buff;
55763b73463SBrian Somers     if (Physical_IsSync(physical))
5581ae349f5Scvs2svn       memcpy(buff, "\377\003", 2);	/* Prepend HDLC header */
5591ae349f5Scvs2svn     else
5601ae349f5Scvs2svn       cp += 2;
5611ae349f5Scvs2svn     on = strlen(cp);
56263b73463SBrian Somers     /* XXX - missing return value check */
56363b73463SBrian Somers     Physical_Write(physical, cp, on);
5641ae349f5Scvs2svn   }
5651ae349f5Scvs2svn }
5661ae349f5Scvs2svn 
5671ae349f5Scvs2svn static int
56863b73463SBrian Somers ExpectString(struct physical *physical, char *str)
5691ae349f5Scvs2svn {
5701ae349f5Scvs2svn   char *minus;
5711ae349f5Scvs2svn   int state;
5721ae349f5Scvs2svn 
5731ae349f5Scvs2svn   if (strcmp(str, "ABORT") == 0) {
5741ae349f5Scvs2svn     ++abort_next;
5751ae349f5Scvs2svn     return (MATCH);
5761ae349f5Scvs2svn   }
5771ae349f5Scvs2svn   if (strcmp(str, "TIMEOUT") == 0) {
5781ae349f5Scvs2svn     ++timeout_next;
5791ae349f5Scvs2svn     return (MATCH);
5801ae349f5Scvs2svn   }
5811ae349f5Scvs2svn   LogPrintf(LogCHAT, "Expecting: %s\n", str);
5821ae349f5Scvs2svn   while (*str) {
5831ae349f5Scvs2svn     /*
5841ae349f5Scvs2svn      * Check whether if string contains sub-send-expect.
5851ae349f5Scvs2svn      */
5861ae349f5Scvs2svn     for (minus = str; *minus; minus++) {
5871ae349f5Scvs2svn       if (*minus == '-') {
5881ae349f5Scvs2svn 	if (minus == str || minus[-1] != '\\')
5891ae349f5Scvs2svn 	  break;
5901ae349f5Scvs2svn       }
5911ae349f5Scvs2svn     }
5921ae349f5Scvs2svn     if (*minus == '-') {	/* We have sub-send-expect. */
5931ae349f5Scvs2svn       *minus = '\0';	/* XXX: Cheat with the const string */
59463b73463SBrian Somers       state = WaitforString(physical, str);
5951ae349f5Scvs2svn       *minus = '-';	/* XXX: Cheat with the const string */
5961ae349f5Scvs2svn       minus++;
5971ae349f5Scvs2svn       if (state != NOMATCH)
5981ae349f5Scvs2svn 	return (state);
5991ae349f5Scvs2svn 
6001ae349f5Scvs2svn       /*
6011ae349f5Scvs2svn        * Can't get expect string. Sendout send part.
6021ae349f5Scvs2svn        */
6031ae349f5Scvs2svn       str = minus;
6041ae349f5Scvs2svn       for (minus = str; *minus; minus++) {
6051ae349f5Scvs2svn 	if (*minus == '-') {
6061ae349f5Scvs2svn 	  if (minus == str || minus[-1] != '\\')
6071ae349f5Scvs2svn 	    break;
6081ae349f5Scvs2svn 	}
6091ae349f5Scvs2svn       }
6101ae349f5Scvs2svn       if (*minus == '-') {
6111ae349f5Scvs2svn         *minus = '\0';	/* XXX: Cheat with the const string */
61263b73463SBrian Somers 	SendString(physical, str);
6131ae349f5Scvs2svn         *minus = '-';	/* XXX: Cheat with the const string */
6141ae349f5Scvs2svn 	str = ++minus;
6151ae349f5Scvs2svn       } else {
61663b73463SBrian Somers 	SendString(physical, str);
6171ae349f5Scvs2svn 	return (MATCH);
6181ae349f5Scvs2svn       }
6191ae349f5Scvs2svn     } else {
6201ae349f5Scvs2svn       /*
6211ae349f5Scvs2svn        * Simple case. Wait for string.
6221ae349f5Scvs2svn        */
62363b73463SBrian Somers       return (WaitforString(physical, str));
6241ae349f5Scvs2svn     }
6251ae349f5Scvs2svn   }
6261ae349f5Scvs2svn   return (MATCH);
6271ae349f5Scvs2svn }
6281ae349f5Scvs2svn 
6291ae349f5Scvs2svn static void (*oint) (int);
6301ae349f5Scvs2svn 
6311ae349f5Scvs2svn static void
6321ae349f5Scvs2svn StopDial(int sig)
6331ae349f5Scvs2svn {
6341ae349f5Scvs2svn   LogPrintf(LogPHASE, "DoChat: Caught signal %d, abort connect\n", sig);
6351ae349f5Scvs2svn   longjmp(ChatEnv, 1);
6361ae349f5Scvs2svn }
6371ae349f5Scvs2svn 
6381ae349f5Scvs2svn int
63963b73463SBrian Somers DoChat(struct physical *physical, char *script)
6401ae349f5Scvs2svn {
6411ae349f5Scvs2svn   char *vector[MAXARGS];
6421ae349f5Scvs2svn   char *const *argv;
6431ae349f5Scvs2svn   int argc, n, state, err;
6441ae349f5Scvs2svn 
6451ae349f5Scvs2svn   if (!script || !*script)
6461ae349f5Scvs2svn     return MATCH;
6471ae349f5Scvs2svn 
6481ae349f5Scvs2svn   if ((err = setjmp(ChatEnv))) {
6491ae349f5Scvs2svn     signal(SIGINT, oint);
6501ae349f5Scvs2svn     if (err == 1)
6511ae349f5Scvs2svn       /* Caught a SIGINT during chat */
6521ae349f5Scvs2svn       return (-1);
6531ae349f5Scvs2svn     return (NOMATCH);
6541ae349f5Scvs2svn   }
6551ae349f5Scvs2svn   oint = signal(SIGINT, StopDial);
6561ae349f5Scvs2svn 
6571ae349f5Scvs2svn   timeout_next = abort_next = 0;
6581ae349f5Scvs2svn   for (n = 0; AbortStrings[n]; n++) {
6591ae349f5Scvs2svn     free(AbortStrings[n]);
6601ae349f5Scvs2svn     AbortStrings[n] = NULL;
6611ae349f5Scvs2svn   }
6621ae349f5Scvs2svn   numaborts = 0;
6631ae349f5Scvs2svn 
6641ae349f5Scvs2svn   memset(vector, '\0', sizeof vector);
6651ae349f5Scvs2svn   argc = MakeArgs(script, vector, VECSIZE(vector));
6661ae349f5Scvs2svn   argv = vector;
6671ae349f5Scvs2svn   TimeoutSec = 30;
6681ae349f5Scvs2svn   while (*argv) {
6691ae349f5Scvs2svn     if (strcmp(*argv, "P_ZERO") == 0 ||
6701ae349f5Scvs2svn 	strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) {
671ecd5172aSBrian Somers       modem_SetParity(physical, *argv++);
6721ae349f5Scvs2svn       continue;
6731ae349f5Scvs2svn     }
67463b73463SBrian Somers     state = ExpectString(physical, *argv++);
6751ae349f5Scvs2svn     switch (state) {
6761ae349f5Scvs2svn     case MATCH:
6771ae349f5Scvs2svn       if (*argv)
67863b73463SBrian Somers 	SendString(physical, *argv++);
6791ae349f5Scvs2svn       break;
6801ae349f5Scvs2svn     case ABORT:
6811ae349f5Scvs2svn     case NOMATCH:
6821ae349f5Scvs2svn       signal(SIGINT, oint);
6831ae349f5Scvs2svn       return (NOMATCH);
6841ae349f5Scvs2svn     }
6851ae349f5Scvs2svn   }
6861ae349f5Scvs2svn   signal(SIGINT, oint);
6871ae349f5Scvs2svn   return (MATCH);
6881ae349f5Scvs2svn }
689