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