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