chat.c revision 15738
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.9 1996/04/06 02:00:17 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 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 pid = execvp(command, vector); 397 LogPrintf(LOG_CHAT_BIT, "execvp failed for (%d/%d): %s\n", pid, errno, command); 398 exit(127); 399 } else { 400 close(fids[1]); 401 for (;;) { 402 nb = read(fids[0], out, 1); 403 if (nb <= 0) 404 break; 405 out++; 406 } 407 *out = '\0'; 408 close(fids[0]); 409 close(fids[1]); 410 waitpid(pid, &stat, WNOHANG); 411 } 412} 413 414void 415SendString(str) 416char *str; 417{ 418 char *cp; 419 int nb, on; 420 char buff[200]; 421 422 if (abort_next) { 423 abort_next = 0; 424 ExpandString(str, buff, 0); 425 AbortStrings[numaborts++] = strdup(buff); 426 } else if (timeout_next) { 427 timeout_next = 0; 428 TimeoutSec = atoi(str); 429 if (TimeoutSec <= 0) 430 TimeoutSec = 30; 431 } else { 432 if (*str == '!') { 433 (void) ExpandString(str+1, buff+2, 0); 434 ExecStr(buff + 2, buff + 2); 435 } else { 436 (void) ExpandString(str, buff+2, 1); 437 } 438 if (strstr(str, "\\P")) { /* Do not log the password itself. */ 439 LogPrintf(LOG_CHAT_BIT, "sending: %s\n", str); 440 } else { 441 LogPrintf(LOG_CHAT_BIT, "sending: %s\n", buff+2); 442 } 443 cp = buff; 444 if (DEV_IS_SYNC) 445 bcopy("\377\003", buff, 2); /* Prepend HDLC header */ 446 else 447 cp += 2; 448 on = strlen(cp); 449 nb = write(modem, cp, on); 450 } 451} 452 453int 454ExpectString(str) 455char *str; 456{ 457 char *minus; 458 int state; 459 460 if (strcmp(str, "ABORT") == 0) { 461 ++abort_next; 462 return(MATCH); 463 } 464 if (strcmp(str, "TIMEOUT") == 0) { 465 ++timeout_next; 466 return(MATCH); 467 } 468 LogPrintf(LOG_CHAT_BIT, "Expecting %s\n", str); 469 while (*str) { 470 /* 471 * Check whether if string contains sub-send-expect. 472 */ 473 for (minus = str; *minus; minus++) { 474 if (*minus == '-') { 475 if (minus == str || minus[-1] != '\\') 476 break; 477 } 478 } 479 if (*minus == '-') { /* We have sub-send-expect. */ 480 *minus++ = '\0'; 481 state = WaitforString(str); 482 if (state != NOMATCH) 483 return(state); 484 /* 485 * Can't get expect string. Sendout send part. 486 */ 487 str = minus; 488 for (minus = str; *minus; minus++) { 489 if (*minus == '-') { 490 if (minus == str || minus[-1] != '\\') 491 break; 492 } 493 } 494 if (*minus == '-') { 495 *minus++ = '\0'; 496 SendString(str); 497 str = minus; 498 } else { 499 SendString(str); 500 return(MATCH); 501 } 502 } else { 503 /* 504 * Simple case. Wait for string. 505 */ 506 return(WaitforString(str)); 507 } 508 } 509 return(MATCH); 510} 511 512int 513DoChat(script) 514char *script; 515{ 516 char *vector[20]; 517 char **argv; 518 int argc, n, state; 519#ifdef DEBUG 520 int i; 521#endif 522 523 timeout_next = abort_next = 0; 524 for (n = 0; AbortStrings[n]; n++) { 525 free(AbortStrings[n]); 526 AbortStrings[n] = NULL; 527 } 528 numaborts = 0; 529 530 bzero(vector, sizeof(vector)); 531 n = MakeArgs(script, &vector); 532#ifdef DEBUG 533 logprintf("n = %d\n", n); 534 for (i = 0; i < n; i++) 535 logprintf(" %s\n", vector[i]); 536#endif 537 argc = n; 538 argv = vector; 539 TimeoutSec = 30; 540 while (*argv) { 541 if (strcmp(*argv, "P_ZERO") == 0 || 542 strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) { 543 ChangeParity(*argv++); 544 continue; 545 } 546 state = ExpectString(*argv++); 547 switch (state) { 548 case MATCH: 549 if (*argv) 550 SendString(*argv++); 551 break; 552 case ABORT: 553#ifdef notdef 554 HangupModem(); 555#endif 556 case NOMATCH: 557 return(NOMATCH); 558 } 559 } 560 return(MATCH); 561} 562