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