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.8 1996/03/08 13:22:21 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 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 * \r Carrige return character 114 * \s Space character 115 * \n Line feed character 116 * \T Telephone number(s) (defined via `set phone') 117 * \t Tab character 118 */ 119 char * 120 ExpandString(str, result, sendmode) 121 char *str; 122 char *result; 123 int sendmode; 124 { 125 int addcr = 0; 126 char *phone; 127 128 if (sendmode) 129 addcr = 1; 130 while (*str) { 131 switch (*str) { 132 case '\\': 133 str++; 134 switch (*str) { 135 case 'c': 136 if (sendmode) 137 addcr = 0; 138 break; 139 case 'd': /* Delay 2 seconds */ 140 sleep(2); break; 141 case 'p': 142 usleep(250000); break; /* Pause 0.25 sec */ 143 case 'n': 144 *result++ = '\n'; break; 145 case 'r': 146 *result++ = '\r'; break; 147 case 's': 148 *result++ = ' '; break; 149 case 't': 150 *result++ = '\t'; break; 151 case 'P': 152 bcopy(VarAuthKey, result, strlen(VarAuthKey)); 153 result += strlen(VarAuthKey); 154 break; 155 case 'T': 156 if (VarNextPhone == NULL) { 157 strcpy(VarPhoneCopy, VarPhoneList); 158 VarNextPhone = VarPhoneCopy; 159 } 160 phone = strsep(&VarNextPhone, ":"); 161 bcopy(phone, result, strlen(phone)); 162 result += strlen(phone); 163 if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER) 164 fprintf(stderr, "Phone: %s\n", phone); 165 LogPrintf(LOG_PHASE, "Phone: %s\n", phone); 166 break; 167 case 'U': 168 bcopy(VarAuthName, result, strlen(VarAuthName)); 169 result += strlen(VarAuthName); 170 break; 171 default: 172 *result++ = *str; break; 173 } 174 if (*str) str++; 175 break; 176 case '^': 177 str++; 178 if (*str) 179 *result++ = *str++ & 0x1f; 180 break; 181 default: 182 *result++ = *str++; 183 break; 184 } 185 } 186 if (addcr) 187 *result++ = '\r'; 188 *result++ = '\0'; 189 return(result); 190 } 191 192 int 193 WaitforString(estr) 194 char *estr; 195 { 196 struct timeval timeout; 197 char *s, *str, ch; 198 char *inp; 199 fd_set rfds; 200 int i, nfds, nb; 201 char buff[200]; 202 203 #ifdef SIGALRM 204 int omask; 205 omask = sigblock(sigmask(SIGALRM)); 206 #endif 207 (void) ExpandString(estr, buff, 0); 208 LogPrintf(LOG_CHAT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff); 209 str = buff; 210 inp = inbuff; 211 212 if (strlen(str)>=IBSIZE){ 213 str[IBSIZE]=0; 214 LogPrintf(LOG_CHAT, "Truncating String to %d character: %s\n", IBSIZE, str); 215 } 216 217 nfds = modem + 1; 218 s = str; 219 for (;;) { 220 FD_ZERO(&rfds); 221 FD_SET(modem, &rfds); 222 /* 223 * Because it is not clear whether select() modifies timeout value, 224 * it is better to initialize timeout values everytime. 225 */ 226 timeout.tv_sec = TimeoutSec; 227 timeout.tv_usec = 0; 228 i = select(nfds, &rfds, NULL, NULL, &timeout); 229 #ifdef notdef 230 TimerService(); 231 #endif 232 if (i < 0) { 233 #ifdef SIGALRM 234 if (errno == EINTR) 235 continue; 236 sigsetmask(omask); 237 #endif 238 perror("select"); 239 *inp = 0; 240 return(NOMATCH); 241 } else if (i == 0) { /* Timeout reached! */ 242 *inp = 0; 243 if (inp != inbuff) 244 LogPrintf(LOG_CHAT, "got: %s\n", inbuff); 245 LogPrintf(LOG_CHAT, "can't get (%d).\n", timeout.tv_sec); 246 #ifdef SIGALRM 247 sigsetmask(omask); 248 #endif 249 return(NOMATCH); 250 } 251 if (FD_ISSET(modem, &rfds)) { /* got something */ 252 if (DEV_IS_SYNC) { 253 int length; 254 if ((length=strlen(inbuff))>IBSIZE){ 255 bcopy(&(inbuff[IBSIZE]),inbuff,IBSIZE+1); /* shuffle down next part*/ 256 length=strlen(inbuff); 257 } 258 nb = read(modem, &(inbuff[length]), IBSIZE); 259 inbuff[nb + length] = 0; 260 if (strstr(inbuff, str)) { 261 #ifdef SIGALRM 262 sigsetmask(omask); 263 #endif 264 return(MATCH); 265 } 266 for (i = 0; i < numaborts; i++) { 267 if (strstr(inbuff, AbortStrings[i])) { 268 LogPrintf(LOG_CHAT, "Abort: %s\n", AbortStrings[i]); 269 #ifdef SIGALRM 270 sigsetmask(omask); 271 #endif 272 return(ABORT); 273 } 274 } 275 } else { 276 read(modem, &ch, 1); 277 *inp++ = ch; 278 if (ch == *s) { 279 s++; 280 if (*s == '\0') { 281 #ifdef SIGALRM 282 sigsetmask(omask); 283 #endif 284 *inp = 0; 285 return(MATCH); 286 } 287 } else { 288 s = str; 289 if (inp == inbuff+ IBSIZE) { 290 bcopy(inp - 100, inbuff, 100); 291 inp = inbuff + 100; 292 } 293 for (i = 0; i < numaborts; i++) { /* Look for Abort strings */ 294 int len; 295 char *s1; 296 297 s1 = AbortStrings[i]; 298 len = strlen(s1); 299 if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) { 300 LogPrintf(LOG_CHAT, "Abort: %s\n", s1); 301 *inp = 0; 302 #ifdef SIGALRM 303 sigsetmask(omask); 304 #endif 305 return(ABORT); 306 } 307 } 308 } 309 } 310 } 311 } 312 #ifdef SIGALRM 313 sigsetmask(omask); 314 #endif 315 } 316 317 void 318 ExecStr(command, out) 319 char *command, *out; 320 { 321 int pid; 322 int fids[2]; 323 char *vector[20]; 324 int stat, nb; 325 char *cp; 326 char tmp[300]; 327 extern int errno; 328 329 cp = inbuff + strlen(inbuff) - 1; 330 while (cp > inbuff) { 331 if (*cp < ' ' && *cp != '\t') { 332 cp++; 333 break; 334 } 335 cp--; 336 } 337 sprintf(tmp, "%s %s", command, cp); 338 (void) MakeArgs(tmp, &vector); 339 340 pipe(fids); 341 pid = fork(); 342 if (pid == 0) { 343 signal(SIGINT, SIG_DFL); 344 signal(SIGQUIT, SIG_DFL); 345 signal(SIGTERM, SIG_DFL); 346 signal(SIGHUP, SIG_DFL); 347 close(fids[0]); 348 dup2(fids[1], 1); 349 close(fids[1]); 350 nb = open("/dev/tty", O_RDWR); 351 dup2(nb, 0); 352 LogPrintf(LOG_CHAT, "exec: %s\n", command); 353 pid = execvp(command, vector); 354 LogPrintf(LOG_CHAT, "execvp failed for (%d/%d): %s\n", pid, errno, command); 355 exit(127); 356 } else { 357 close(fids[1]); 358 for (;;) { 359 nb = read(fids[0], out, 1); 360 if (nb <= 0) 361 break; 362 out++; 363 } 364 *out = '\0'; 365 close(fids[0]); 366 close(fids[1]); 367 waitpid(pid, &stat, WNOHANG); 368 } 369 } 370 371 void 372 SendString(str) 373 char *str; 374 { 375 char *cp; 376 int nb, on; 377 char buff[200]; 378 379 if (abort_next) { 380 abort_next = 0; 381 ExpandString(str, buff, 0); 382 AbortStrings[numaborts++] = strdup(buff); 383 } else if (timeout_next) { 384 timeout_next = 0; 385 TimeoutSec = atoi(str); 386 if (TimeoutSec <= 0) 387 TimeoutSec = 30; 388 } else { 389 if (*str == '!') { 390 (void) ExpandString(str+1, buff+2, 0); 391 ExecStr(buff + 2, buff + 2); 392 } else { 393 (void) ExpandString(str, buff+2, 1); 394 } 395 if (strstr(str, "\\P")) { /* Do not log the password itself. */ 396 LogPrintf(LOG_CHAT, "sending: %s\n", str); 397 } else { 398 LogPrintf(LOG_CHAT, "sending: %s\n", buff+2); 399 } 400 cp = buff; 401 if (DEV_IS_SYNC) 402 bcopy("\377\003", buff, 2); /* Prepend HDLC header */ 403 else 404 cp += 2; 405 on = strlen(cp); 406 nb = write(modem, cp, on); 407 } 408 } 409 410 int 411 ExpectString(str) 412 char *str; 413 { 414 char *minus; 415 int state; 416 417 if (strcmp(str, "ABORT") == 0) { 418 ++abort_next; 419 return(MATCH); 420 } 421 if (strcmp(str, "TIMEOUT") == 0) { 422 ++timeout_next; 423 return(MATCH); 424 } 425 LogPrintf(LOG_CHAT, "Expecting %s\n", str); 426 while (*str) { 427 /* 428 * Check whether if string contains sub-send-expect. 429 */ 430 for (minus = str; *minus; minus++) { 431 if (*minus == '-') { 432 if (minus == str || minus[-1] != '\\') 433 break; 434 } 435 } 436 if (*minus == '-') { /* We have sub-send-expect. */ 437 *minus++ = '\0'; 438 state = WaitforString(str); 439 if (state != NOMATCH) 440 return(state); 441 /* 442 * Can't get expect string. Sendout send part. 443 */ 444 str = minus; 445 for (minus = str; *minus; minus++) { 446 if (*minus == '-') { 447 if (minus == str || minus[-1] != '\\') 448 break; 449 } 450 } 451 if (*minus == '-') { 452 *minus++ = '\0'; 453 SendString(str); 454 str = minus; 455 } else { 456 SendString(str); 457 return(MATCH); 458 } 459 } else { 460 /* 461 * Simple case. Wait for string. 462 */ 463 return(WaitforString(str)); 464 } 465 } 466 return(MATCH); 467 } 468 469 int 470 DoChat(script) 471 char *script; 472 { 473 char *vector[20]; 474 char **argv; 475 int argc, n, state; 476 #ifdef DEBUG 477 int i; 478 #endif 479 480 timeout_next = abort_next = 0; 481 for (n = 0; AbortStrings[n]; n++) { 482 free(AbortStrings[n]); 483 AbortStrings[n] = NULL; 484 } 485 numaborts = 0; 486 487 bzero(vector, sizeof(vector)); 488 n = MakeArgs(script, &vector); 489 #ifdef DEBUG 490 logprintf("n = %d\n", n); 491 for (i = 0; i < n; i++) 492 logprintf(" %s\n", vector[i]); 493 #endif 494 argc = n; 495 argv = vector; 496 TimeoutSec = 30; 497 while (*argv) { 498 if (strcmp(*argv, "P_ZERO") == 0 || 499 strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) { 500 ChangeParity(*argv++); 501 continue; 502 } 503 state = ExpectString(*argv++); 504 switch (state) { 505 case MATCH: 506 if (*argv) 507 SendString(*argv++); 508 break; 509 case ABORT: 510 #ifdef notdef 511 HangupModem(); 512 #endif 513 case NOMATCH: 514 return(NOMATCH); 515 } 516 } 517 return(MATCH); 518 } 519