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