chat.c revision 25560
12996Spst/* 22996Spst * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 32996Spst * 42996Spst * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. 52996Spst * 62996Spst * 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.22 1997/03/13 12:45:28 brian 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#include "sig.h" 42#include "chat.h" 43 44#define IBSIZE 200 45 46static int TimeoutSec; 47static int abort_next, timeout_next; 48static int numaborts; 49char *AbortStrings[50]; 50char inbuff[IBSIZE*2+1]; 51 52extern int ChangeParity(char *); 53 54#define MATCH 1 55#define NOMATCH 0 56#define ABORT -1 57 58static char * 59findblank(p, instring) 60char *p; 61int instring; 62{ 63 if (instring) { 64 while (*p) { 65 if (*p == '\\') { 66 strcpy(p, p + 1); 67 if (!*p) 68 break; 69 } else if (*p == '"') 70 return(p); 71 p++; 72 } 73 } else { 74 while (*p) { 75 if (isblank(*p)) 76 return(p); 77 p++; 78 } 79 } 80 return p; 81} 82 83int 84MakeArgs(script, pvect, maxargs) 85char *script; 86char **pvect; 87int maxargs; 88{ 89 int nargs, nb; 90 int instring; 91 92 nargs = 0; 93 while (*script) { 94 nb = strspn(script, " \t"); 95 script += nb; 96 if (*script) { 97 if (*script == '"') { 98 instring = 1; 99 script++; 100 if (*script == '\0') 101 break; /* Shouldn't return here. Need to null terminate below */ 102 } else 103 instring = 0; 104 if (nargs >= maxargs-1) 105 break; 106 *pvect++ = script; 107 nargs++; 108 script = findblank(script, instring); 109 if (*script) 110 *script++ = '\0'; 111 } 112 } 113 *pvect = NULL; 114 return nargs; 115} 116 117/* 118 * \c don't add a cr 119 * \d Sleep a little (delay 2 seconds 120 * \n Line feed character 121 * \P Auth Key password 122 * \p pause 0.25 sec 123 * \r Carrige return character 124 * \s Space character 125 * \T Telephone number(s) (defined via `set phone') 126 * \t Tab character 127 * \U Auth User 128 */ 129char * 130ExpandString(str, result, reslen, sendmode) 131char *str; 132char *result; 133int reslen; 134int sendmode; 135{ 136 int addcr = 0; 137 char *phone; 138 139 result[--reslen] = '\0'; 140 if (sendmode) 141 addcr = 1; 142 while (*str && reslen > 0) { 143 switch (*str) { 144 case '\\': 145 str++; 146 switch (*str) { 147 case 'c': 148 if (sendmode) 149 addcr = 0; 150 break; 151 case 'd': /* Delay 2 seconds */ 152 sleep(2); break; 153 case 'p': 154 usleep(250000); break; /* Pause 0.25 sec */ 155 case 'n': 156 *result++ = '\n'; reslen--; break; 157 case 'r': 158 *result++ = '\r'; reslen--; break; 159 case 's': 160 *result++ = ' '; reslen--; break; 161 case 't': 162 *result++ = '\t'; reslen--; break; 163 case 'P': 164 strncpy(result, VarAuthKey, reslen); 165 reslen -= strlen(result); 166 result += strlen(result); 167 break; 168 case 'T': 169 if (VarNextPhone == NULL) { 170 strcpy(VarPhoneCopy, VarPhoneList); 171 VarNextPhone = VarPhoneCopy; 172 } 173 phone = strsep(&VarNextPhone, ":"); 174 strncpy(result, phone, reslen); 175 reslen -= strlen(result); 176 result += strlen(result); 177 if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER) 178 fprintf(stderr, "Phone: %s\n", phone); 179 LogPrintf(LOG_PHASE_BIT, "Phone: %s\n", phone); 180 break; 181 case 'U': 182 strncpy(result, VarAuthName, reslen); 183 reslen -= strlen(result); 184 result += strlen(result); 185 break; 186 default: 187 reslen--; 188 *result++ = *str; 189 break; 190 } 191 if (*str) 192 str++; 193 break; 194 case '^': 195 str++; 196 if (*str) { 197 *result++ = *str++ & 0x1f; 198 reslen--; 199 } 200 break; 201 default: 202 *result++ = *str++; 203 reslen--; 204 break; 205 } 206 } 207 if (--reslen > 0) { 208 if (addcr) 209 *result++ = '\r'; 210 } 211 if (--reslen > 0) 212 *result++ = '\0'; 213 return(result); 214} 215 216#define MAXLOGBUFF 200 217static char logbuff[MAXLOGBUFF]; 218static int loglen = 0; 219 220static void clear_log() { 221 memset(logbuff,0,MAXLOGBUFF); 222 loglen = 0; 223} 224 225static void flush_log() { 226 if ((loglevel & LOG_CONNECT_BIT) 227 || ((loglevel & LOG_CARRIER_BIT) 228 && strstr(logbuff,"CARRIER"))) { 229 LogPrintf(LOG_CONNECT_BIT|LOG_CARRIER_BIT,"Chat: %s\n",logbuff); 230 } 231 clear_log(); 232} 233 234static void connect_log(char *str, int single_p) { 235 int space = MAXLOGBUFF - loglen - 1; 236 237 while (space--) { 238 if (*str == '\n') { 239 flush_log(); 240 } else { 241 logbuff[loglen++] = *str; 242 } 243 if (single_p || !*++str) break; 244 } 245 if (!space) flush_log(); 246} 247 248 249 250int 251WaitforString(estr) 252char *estr; 253{ 254 struct timeval timeout; 255 char *s, *str, ch; 256 char *inp; 257 fd_set rfds; 258 int i, nfds, nb, msg; 259 char buff[200]; 260 261 262#ifdef SIGALRM 263 int omask; 264 omask = sigblock(sigmask(SIGALRM)); 265#endif 266 clear_log(); 267 (void) ExpandString(estr, buff, sizeof(buff), 0); 268 LogPrintf(LOG_CHAT_BIT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff); 269 str = buff; 270 inp = inbuff; 271 272 if (strlen(str)>=IBSIZE){ 273 str[IBSIZE]=0; 274 LogPrintf(LOG_CHAT_BIT, "Truncating String to %d character: %s\n", IBSIZE, str); 275 } 276 277 nfds = modem + 1; 278 s = str; 279 msg = FALSE; 280 for (;;) { 281 FD_ZERO(&rfds); 282 FD_SET(modem, &rfds); 283 /* 284 * Because it is not clear whether select() modifies timeout value, 285 * it is better to initialize timeout values everytime. 286 */ 287 timeout.tv_sec = TimeoutSec; 288 timeout.tv_usec = 0; 289 i = select(nfds, &rfds, NULL, NULL, &timeout); 290#ifdef notdef 291 TimerService(); 292#endif 293 if (i < 0) { 294#ifdef SIGALRM 295 if (errno == EINTR) 296 continue; 297 sigsetmask(omask); 298#endif 299 perror("select"); 300 *inp = 0; 301 return(NOMATCH); 302 } else if (i == 0) { /* Timeout reached! */ 303 *inp = 0; 304 if (inp != inbuff) 305 LogPrintf(LOG_CHAT_BIT, "got: %s\n", inbuff); 306 LogPrintf(LOG_CHAT_BIT, "can't get (%d).\n", timeout.tv_sec); 307#ifdef SIGALRM 308 sigsetmask(omask); 309#endif 310 return(NOMATCH); 311 } 312 if (FD_ISSET(modem, &rfds)) { /* got something */ 313 if (DEV_IS_SYNC) { 314 int length; 315 if ((length=strlen(inbuff))>IBSIZE){ 316 bcopy(&(inbuff[IBSIZE]),inbuff,IBSIZE+1); /* shuffle down next part*/ 317 length=strlen(inbuff); 318 } 319 nb = read(modem, &(inbuff[length]), IBSIZE); 320 inbuff[nb + length] = 0; 321 connect_log(inbuff,0); 322 if (strstr(inbuff, str)) { 323#ifdef SIGALRM 324 sigsetmask(omask); 325#endif 326 flush_log(); 327 return(MATCH); 328 } 329 for (i = 0; i < numaborts; i++) { 330 if (strstr(inbuff, AbortStrings[i])) { 331 LogPrintf(LOG_CHAT_BIT, "Abort: %s\n", AbortStrings[i]); 332#ifdef SIGALRM 333 sigsetmask(omask); 334#endif 335 flush_log(); 336 return(ABORT); 337 } 338 } 339 } else { 340 read(modem, &ch, 1); 341 connect_log(&ch,1); 342 *inp++ = ch; 343 if (ch == *s) { 344 s++; 345 if (*s == '\0') { 346#ifdef SIGALRM 347 sigsetmask(omask); 348#endif 349 *inp = 0; 350 flush_log(); 351 return(MATCH); 352 } 353 } else { 354 s = str; 355 if (inp == inbuff+ IBSIZE) { 356 bcopy(inp - 100, inbuff, 100); 357 inp = inbuff + 100; 358 } 359 for (i = 0; i < numaborts; i++) { /* Look for Abort strings */ 360 int len; 361 char *s1; 362 363 s1 = AbortStrings[i]; 364 len = strlen(s1); 365 if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) { 366 LogPrintf(LOG_CHAT_BIT, "Abort: %s\n", s1); 367 *inp = 0; 368#ifdef SIGALRM 369 sigsetmask(omask); 370#endif 371 flush_log(); 372 return(ABORT); 373 } 374 } 375 } 376 } 377 } 378 } 379#ifdef SIGALRM 380 sigsetmask(omask); 381#endif 382} 383 384void 385ExecStr(command, out) 386char *command, *out; 387{ 388 int pid; 389 int fids[2]; 390 char *vector[20]; 391 int stat, nb; 392 char *cp; 393 char tmp[300]; 394 extern int errno; 395 396 cp = inbuff + strlen(inbuff) - 1; 397 while (cp > inbuff) { 398 if (*cp < ' ' && *cp != '\t') { 399 cp++; 400 break; 401 } 402 cp--; 403 } 404 snprintf(tmp, sizeof tmp, "%s %s", command, cp); 405 (void) MakeArgs(tmp, vector, VECSIZE(vector)); 406 407 pipe(fids); 408 pid = fork(); 409 if (pid == 0) { 410 TermTimerService(); 411 signal(SIGINT, SIG_DFL); 412 signal(SIGQUIT, SIG_DFL); 413 signal(SIGTERM, SIG_DFL); 414 signal(SIGHUP, SIG_DFL); 415 signal(SIGALRM, SIG_DFL); 416 close(fids[0]); 417 dup2(fids[1], 1); 418 close(fids[1]); 419 nb = open("/dev/tty", O_RDWR); 420 dup2(nb, 0); 421 LogPrintf(LOG_CHAT_BIT, "exec: %s\n", command); 422 /* switch back to original privileges */ 423 if (setgid(getgid()) < 0) { 424 LogPrintf(LOG_CHAT_BIT, "setgid: %s\n", strerror(errno)); 425 exit(1); 426 } 427 if (setuid(getuid()) < 0) { 428 LogPrintf(LOG_CHAT_BIT, "setuid: %s\n", strerror(errno)); 429 exit(1); 430 } 431 pid = execvp(command, vector); 432 LogPrintf(LOG_CHAT_BIT, "execvp failed for (%d/%d): %s\n", pid, errno, command); 433 exit(127); 434 } else { 435 close(fids[1]); 436 for (;;) { 437 nb = read(fids[0], out, 1); 438 if (nb <= 0) 439 break; 440 out++; 441 } 442 *out = '\0'; 443 close(fids[0]); 444 close(fids[1]); 445 waitpid(pid, &stat, WNOHANG); 446 } 447} 448 449void 450SendString(str) 451char *str; 452{ 453 char *cp; 454 int nb, on; 455 char buff[200]; 456 457 if (abort_next) { 458 abort_next = 0; 459 ExpandString(str, buff, sizeof(buff), 0); 460 AbortStrings[numaborts++] = strdup(buff); 461 } else if (timeout_next) { 462 timeout_next = 0; 463 TimeoutSec = atoi(str); 464 if (TimeoutSec <= 0) 465 TimeoutSec = 30; 466 } else { 467 if (*str == '!') { 468 (void) ExpandString(str+1, buff+2, sizeof(buff)-2, 0); 469 ExecStr(buff + 2, buff + 2); 470 } else { 471 (void) ExpandString(str, buff+2, sizeof(buff)-2, 1); 472 } 473 if (strstr(str, "\\P")) { /* Do not log the password itself. */ 474 LogPrintf(LOG_CHAT_BIT, "sending: %s\n", str); 475 } else { 476 LogPrintf(LOG_CHAT_BIT, "sending: %s\n", buff+2); 477 } 478 cp = buff; 479 if (DEV_IS_SYNC) 480 bcopy("\377\003", buff, 2); /* Prepend HDLC header */ 481 else 482 cp += 2; 483 on = strlen(cp); 484 nb = write(modem, cp, on); 485 } 486} 487 488int 489ExpectString(str) 490char *str; 491{ 492 char *minus; 493 int state; 494 495 if (strcmp(str, "ABORT") == 0) { 496 ++abort_next; 497 return(MATCH); 498 } 499 if (strcmp(str, "TIMEOUT") == 0) { 500 ++timeout_next; 501 return(MATCH); 502 } 503 LogPrintf(LOG_CHAT_BIT, "Expecting %s\n", str); 504 while (*str) { 505 /* 506 * Check whether if string contains sub-send-expect. 507 */ 508 for (minus = str; *minus; minus++) { 509 if (*minus == '-') { 510 if (minus == str || minus[-1] != '\\') 511 break; 512 } 513 } 514 if (*minus == '-') { /* We have sub-send-expect. */ 515 *minus++ = '\0'; 516 state = WaitforString(str); 517 if (state != NOMATCH) 518 return(state); 519 /* 520 * Can't get expect string. Sendout send part. 521 */ 522 str = minus; 523 for (minus = str; *minus; minus++) { 524 if (*minus == '-') { 525 if (minus == str || minus[-1] != '\\') 526 break; 527 } 528 } 529 if (*minus == '-') { 530 *minus++ = '\0'; 531 SendString(str); 532 str = minus; 533 } else { 534 SendString(str); 535 return(MATCH); 536 } 537 } else { 538 /* 539 * Simple case. Wait for string. 540 */ 541 return(WaitforString(str)); 542 } 543 } 544 return(MATCH); 545} 546 547int 548DoChat(script) 549char *script; 550{ 551 char *vector[40]; 552 char **argv; 553 int argc, n, state; 554#ifdef DEBUG 555 int i; 556#endif 557 558 timeout_next = abort_next = 0; 559 for (n = 0; AbortStrings[n]; n++) { 560 free(AbortStrings[n]); 561 AbortStrings[n] = NULL; 562 } 563 numaborts = 0; 564 565 bzero(vector, sizeof(vector)); 566 n = MakeArgs(script, vector, VECSIZE(vector)); 567#ifdef DEBUG 568 logprintf("n = %d\n", n); 569 for (i = 0; i < n; i++) 570 logprintf(" %s\n", vector[i]); 571#endif 572 argc = n; 573 argv = vector; 574 TimeoutSec = 30; 575 while (*argv) { 576 if (strcmp(*argv, "P_ZERO") == 0 || 577 strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) { 578 ChangeParity(*argv++); 579 continue; 580 } 581 state = ExpectString(*argv++); 582 switch (state) { 583 case MATCH: 584 if (*argv) 585 SendString(*argv++); 586 break; 587 case ABORT: 588#ifdef notdef 589 HangupModem(); 590#endif 591 case NOMATCH: 592 return(NOMATCH); 593 } 594 } 595 return(MATCH); 596} 597