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