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