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