1 /* $OpenBSD: chat.c,v 1.39 2024/11/04 11:12:52 deraadt Exp $ */ 2 3 /* 4 * Chat -- a program for automatic session establishment (i.e. dial 5 * the phone and log in). 6 * 7 * Standard termination codes: 8 * 0 - successful completion of the script 9 * 1 - invalid argument, expect string too large, etc. 10 * 2 - error on an I/O operation or fatal error condition. 11 * 3 - timeout waiting for a simple string. 12 * 4 - the first string declared as "ABORT" 13 * 5 - the second string declared as "ABORT" 14 * 6 - ... and so on for successive ABORT strings. 15 * 16 * This software is in the public domain. 17 * 18 * ----------------- 19 * added -T and -U option and \T and \U substitution to pass a phone 20 * number into chat script. Two are needed for some ISDN TA applications. 21 * Keith Dart <kdart@cisco.com> 22 * 23 * 24 * Added SAY keyword to send output to stderr. 25 * This allows to turn ECHO OFF and to output specific, user selected, 26 * text to give progress messages. This best works when stderr 27 * exists (i.e.: pppd in nodetach mode). 28 * 29 * Added HANGUP directives to allow for us to be called 30 * back. When HANGUP is set to NO, chat will not hangup at HUP signal. 31 * We rely on timeouts in that case. 32 * 33 * Added CLR_ABORT to clear previously set ABORT string. This has been 34 * dictated by the HANGUP above as "NO CARRIER" (for example) must be 35 * an ABORT condition until we know the other host is going to close 36 * the connection for call back. As soon as we have completed the 37 * first stage of the call back sequence, "NO CARRIER" is a valid, non 38 * fatal string. As soon as we got called back (probably get "CONNECT"), 39 * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command. 40 * Note that CLR_ABORT packs the abort_strings[] array so that we do not 41 * have unused entries not being reclaimed. 42 * 43 * In the same vein as above, added CLR_REPORT keyword. 44 * 45 * Allow for comments. Line starting with '#' are comments and are 46 * ignored. If a '#' is to be expected as the first character, the 47 * expect string must be quoted. 48 * 49 * 50 * Francis Demierre <Francis@SwissMail.Com> 51 * Thu May 15 17:15:40 MET DST 1997 52 * 53 * 54 * Added -r "report file" switch & REPORT keyword. 55 * Robert Geer <bgeer@xmission.com> 56 * 57 * Added -s "use stderr" and -S "don't use syslog" switches. 58 * June 18, 1997 59 * Karl O. Pinc <kop@meme.com> 60 * 61 * 62 * Added -e "echo" switch & ECHO keyword 63 * Dick Streefland <dicks@tasking.nl> 64 * 65 * 66 * Considerable updates and modifications by 67 * Al Longyear <longyear@pobox.com> 68 * Paul Mackerras <paulus@cs.anu.edu.au> 69 * 70 * 71 * The original author is: 72 * 73 * Karl Fox <karl@MorningStar.Com> 74 * Morning Star Technologies, Inc. 75 * 1760 Zollinger Road 76 * Columbus, OH 43221 77 * (614)451-1883 78 * 79 * 80 */ 81 82 #include <stdio.h> 83 #include <ctype.h> 84 #include <time.h> 85 #include <fcntl.h> 86 #include <signal.h> 87 #include <errno.h> 88 #include <string.h> 89 #include <stdlib.h> 90 #include <unistd.h> 91 #include <sys/types.h> 92 #include <sys/stat.h> 93 #include <syslog.h> 94 #include <stdarg.h> 95 96 #ifndef TERMIO 97 #undef TERMIOS 98 #define TERMIOS 99 #endif 100 101 #ifdef TERMIO 102 #include <termio.h> 103 #endif 104 #ifdef TERMIOS 105 #include <termios.h> 106 #endif 107 108 #define STR_LEN 1024 109 110 #ifndef SIGTYPE 111 #define SIGTYPE void 112 #endif 113 114 #ifndef O_NONBLOCK 115 #define O_NONBLOCK O_NDELAY 116 #endif 117 118 #ifdef SUNOS 119 extern int sys_nerr; 120 extern char *sys_errlist[]; 121 #define memmove(to, from, n) bcopy(from, to, n) 122 #define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\ 123 "unknown error") 124 #endif 125 126 #define MAX_ABORTS 50 127 #define MAX_REPORTS 50 128 #define DEFAULT_CHAT_TIMEOUT 45 129 130 int echo = 0; 131 int verbose = 0; 132 int to_log = 1; 133 int to_stderr = 0; 134 int Verbose = 0; 135 int quiet = 0; 136 int report = 0; 137 int exit_code = 0; 138 FILE* report_fp = (FILE *) 0; 139 char *report_file = (char *) 0; 140 char *chat_file = (char *) 0; 141 char *phone_num = (char *) 0; 142 char *phone_num2 = (char *) 0; 143 int timeout = DEFAULT_CHAT_TIMEOUT; 144 145 int have_tty_parameters = 0; 146 147 extern char *__progname; 148 149 #ifdef TERMIO 150 #define term_parms struct termio 151 #define get_term_param(param) ioctl(0, TCGETA, param) 152 #define set_term_param(param) ioctl(0, TCSETA, param) 153 struct termio saved_tty_parameters; 154 #endif 155 156 #ifdef TERMIOS 157 #define term_parms struct termios 158 #define get_term_param(param) tcgetattr(0, param) 159 #define set_term_param(param) tcsetattr(0, TCSANOW, param) 160 struct termios saved_tty_parameters; 161 #endif 162 163 char *abort_string[MAX_ABORTS], *fail_reason = NULL, 164 fail_buffer[50]; 165 int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0; 166 int clear_abort_next = 0; 167 168 char *report_string[MAX_REPORTS] ; 169 char report_buffer[50] ; 170 int n_reports = 0, report_next = 0, report_gathering = 0 ; 171 int clear_report_next = 0; 172 173 int say_next = 0, hup_next = 0; 174 175 void usage(void); 176 void logmsg(const char *fmt, ...); 177 void fatal(int code, const char *fmt, ...); 178 SIGTYPE sigalrm(int signo); 179 SIGTYPE sigint(int signo); 180 SIGTYPE sigterm(int signo); 181 SIGTYPE sighup(int signo); 182 void unalarm(void); 183 void init(void); 184 void set_tty_parameters(void); 185 void echo_stderr(int); 186 void break_sequence(void); 187 void terminate(int status); 188 void do_file(char *chat_file); 189 int get_string(char *string); 190 int put_string(char *s); 191 int write_char(int c); 192 int put_char(int c); 193 int get_char(void); 194 void chat_send(char *s); 195 char *character(int c); 196 void chat_expect(char *s); 197 char *clean(char *s, int sending); 198 void break_sequence(void); 199 void terminate(int status); 200 void pack_array(char **array, int end); 201 char *expect_strtok(char *, char *); 202 int vfmtmsg(char *, int, const char *, va_list); /* vsnprintf++ */ 203 204 int main(int, char *[]); 205 206 /* 207 * chat [ -v ] [-T number] [-U number] [ -t timeout ] [ -f chat-file ] \ 208 * [ -r report-file ] \ 209 * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]] 210 * 211 * Perform a UUCP-dialer-like chat script on stdin and stdout. 212 */ 213 int 214 main(int argc, char **argv) 215 { 216 const char *errstr; 217 int option; 218 219 tzset(); 220 221 while ((option = getopt(argc, argv, "esSvVt:r:f:T:U:")) != -1) { 222 switch (option) { 223 case 'e': 224 echo = 1; 225 break; 226 227 case 'v': 228 verbose = 1; 229 break; 230 231 case 'V': 232 Verbose = 1; 233 break; 234 235 case 's': 236 to_stderr = 1; 237 break; 238 239 case 'S': 240 to_log = 0; 241 break; 242 243 case 'f': 244 if ((chat_file = strdup(optarg)) == NULL) 245 fatal(2, "memory error!"); 246 break; 247 248 case 't': 249 timeout = strtonum(optarg, 0, 10000, &errstr); 250 if (errstr) 251 fatal(2, "-t %s: %s\n", optarg, errstr); 252 break; 253 254 case 'r': 255 if (report_fp != NULL) 256 fclose (report_fp); 257 if ((report_file = strdup(optarg)) == NULL) 258 fatal(2, "memory error!"); 259 report_fp = fopen (report_file, "a"); 260 if (report_fp != NULL) { 261 if (verbose) 262 fprintf (report_fp, "Opening \"%s\"...\n", 263 report_file); 264 report = 1; 265 } 266 break; 267 268 case 'T': 269 if ((phone_num = strdup(optarg)) == NULL) 270 fatal(2, "memory error!"); 271 break; 272 273 case 'U': 274 phone_num2 = strdup(optarg); 275 if ((phone_num2 = strdup(optarg)) == NULL) 276 fatal(2, "memory error!"); 277 break; 278 279 case ':': 280 fprintf(stderr, "Option -%c requires an argument\n", 281 optopt); 282 283 default: 284 usage(); 285 break; 286 } 287 } 288 argc -= optind; 289 argv += optind; 290 /* 291 * Default the report file to the stderr location 292 */ 293 if (report_fp == NULL) 294 report_fp = stderr; 295 296 if (to_log) { 297 #ifdef ultrix 298 openlog("chat", LOG_PID); 299 #else 300 openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); 301 302 if (verbose) 303 setlogmask(LOG_UPTO(LOG_INFO)); 304 else 305 setlogmask(LOG_UPTO(LOG_WARNING)); 306 #endif 307 } 308 309 init(); 310 311 if (chat_file != NULL) { 312 if (argc > 0) 313 usage(); 314 else 315 do_file (chat_file); 316 } else { 317 while (argc-- > 0) { 318 chat_expect(*argv++); 319 320 if (argc-- > 0) { 321 chat_send(*argv++); 322 } 323 } 324 } 325 326 terminate(0); 327 return 0; 328 } 329 330 /* 331 * Process a chat script when read from a file. 332 */ 333 334 void 335 do_file(char *chat_file) 336 { 337 int linect, sendflg; 338 char *sp, *arg, quote; 339 char buf [STR_LEN]; 340 FILE *cfp; 341 342 cfp = fopen (chat_file, "r"); 343 if (cfp == NULL) 344 fatal(1, "%s -- open failed: %m", chat_file); 345 346 linect = 0; 347 sendflg = 0; 348 349 while (fgets(buf, STR_LEN, cfp) != NULL) { 350 buf[strcspn(buf, "\n")] = '\0'; 351 352 linect++; 353 sp = buf; 354 355 /* lines starting with '#' are comments. If a real '#' 356 is to be expected, it should be quoted .... */ 357 if ( *sp == '#' ) 358 continue; 359 360 while (*sp != '\0') { 361 if (*sp == ' ' || *sp == '\t') { 362 ++sp; 363 continue; 364 } 365 366 if (*sp == '"' || *sp == '\'') { 367 quote = *sp++; 368 arg = sp; 369 while (*sp != quote) { 370 if (*sp == '\0') 371 fatal(1, "unterminated quote (line %d)", linect); 372 373 if (*sp++ == '\\') { 374 if (*sp != '\0') 375 ++sp; 376 } 377 } 378 } 379 else { 380 arg = sp; 381 while (*sp != '\0' && *sp != ' ' && *sp != '\t') 382 ++sp; 383 } 384 385 if (*sp != '\0') 386 *sp++ = '\0'; 387 388 if (sendflg) 389 chat_send (arg); 390 else 391 chat_expect (arg); 392 sendflg = !sendflg; 393 } 394 } 395 fclose (cfp); 396 } 397 398 /* 399 * We got an error parsing the command line. 400 */ 401 void usage(void) 402 { 403 fprintf(stderr, "\ 404 usage: %s [-eSsVv] [-f chat_file] [-r report_file] [-T phone_number]\n\ 405 [-t timeout] [-U phone_number_2] script\n", 406 __progname); 407 exit(1); 408 } 409 410 char line[1024]; 411 412 /* 413 * Send a message to syslog and/or stderr. 414 */ 415 void logmsg(const char *fmt, ...) 416 { 417 va_list args; 418 419 va_start(args, fmt); 420 vfmtmsg(line, sizeof(line), fmt, args); 421 va_end(args); 422 if (to_log) 423 syslog(LOG_INFO, "%s", line); 424 if (to_stderr) 425 fprintf(stderr, "%s\n", line); 426 } 427 428 /* 429 * Print an error message and terminate. 430 */ 431 432 void fatal(int code, const char *fmt, ...) 433 { 434 va_list args; 435 436 va_start(args, fmt); 437 vfmtmsg(line, sizeof(line), fmt, args); 438 va_end(args); 439 if (to_log) 440 syslog(LOG_ERR, "%s", line); 441 if (to_stderr) 442 fprintf(stderr, "%s\n", line); 443 terminate(code); 444 } 445 446 int alarmed = 0; 447 448 SIGTYPE sigalrm(int signo) 449 { 450 int flags; 451 452 alarm(1); 453 alarmed = 1; /* Reset alarm to avoid race window */ 454 signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ 455 456 if ((flags = fcntl(0, F_GETFL)) == -1) 457 fatal(2, "Can't get file mode flags on stdin: %m"); 458 459 if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) 460 fatal(2, "Can't set file mode flags on stdin: %m"); 461 462 if (verbose) 463 logmsg("alarm"); 464 } 465 466 void unalarm(void) 467 { 468 int flags; 469 470 if ((flags = fcntl(0, F_GETFL)) == -1) 471 fatal(2, "Can't get file mode flags on stdin: %m"); 472 473 if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1) 474 fatal(2, "Can't set file mode flags on stdin: %m"); 475 } 476 477 SIGTYPE sigint(int signo) 478 { 479 fatal(2, "SIGINT"); 480 } 481 482 SIGTYPE sigterm(int signo) 483 { 484 fatal(2, "SIGTERM"); 485 } 486 487 SIGTYPE sighup(int signo) 488 { 489 fatal(2, "SIGHUP"); 490 } 491 492 void init(void) 493 { 494 signal(SIGINT, sigint); 495 signal(SIGTERM, sigterm); 496 signal(SIGHUP, sighup); 497 498 set_tty_parameters(); 499 signal(SIGALRM, sigalrm); 500 alarm(0); 501 alarmed = 0; 502 } 503 504 void set_tty_parameters(void) 505 { 506 #if defined(get_term_param) 507 term_parms t; 508 509 if (get_term_param (&t) < 0) 510 fatal(2, "Can't get terminal parameters: %m"); 511 512 saved_tty_parameters = t; 513 have_tty_parameters = 1; 514 515 t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; 516 t.c_oflag = 0; 517 t.c_lflag = 0; 518 t.c_cc[VERASE] = 519 t.c_cc[VKILL] = 0; 520 t.c_cc[VMIN] = 1; 521 t.c_cc[VTIME] = 0; 522 523 if (set_term_param (&t) < 0) 524 fatal(2, "Can't set terminal parameters: %m"); 525 #endif 526 } 527 528 void break_sequence(void) 529 { 530 #ifdef TERMIOS 531 tcsendbreak (0, 0); 532 #endif 533 } 534 535 void terminate(int status) 536 { 537 echo_stderr(-1); 538 if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { 539 /* 540 * Allow the last of the report string to be gathered before we terminate. 541 */ 542 if (report_gathering) { 543 int c, rep_len; 544 545 rep_len = strlen(report_buffer); 546 while (rep_len + 1 <= sizeof(report_buffer)) { 547 alarm(1); 548 c = get_char(); 549 alarm(0); 550 if (c < 0 || iscntrl(c)) 551 break; 552 report_buffer[rep_len] = c; 553 ++rep_len; 554 } 555 report_buffer[rep_len] = 0; 556 fprintf (report_fp, "chat: %s\n", report_buffer); 557 } 558 if (verbose) 559 fprintf (report_fp, "Closing \"%s\".\n", report_file); 560 fclose (report_fp); 561 report_fp = (FILE *) NULL; 562 } 563 564 #if defined(get_term_param) 565 if (have_tty_parameters) { 566 if (set_term_param (&saved_tty_parameters) < 0) 567 fatal(2, "Can't restore terminal parameters: %m"); 568 } 569 #endif 570 571 exit(status); 572 } 573 574 /* 575 * 'Clean up' this string. 576 */ 577 char *clean(char *s, int sending) 578 { 579 char *ret, *t, cur_chr; 580 int new_length; 581 char *s1, *phchar; 582 int add_return = sending; 583 #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) 584 585 /* Overestimate new length: */ 586 new_length = 0; 587 for (t = s; *t; t++) 588 if (*t == '^' && *(t+1) != '\0') { 589 t++; 590 new_length++; 591 } else if (*t != '\\') { 592 new_length++; 593 } else { 594 t++; 595 switch (*t) { 596 case 'c': 597 case 'b': 598 case 'r': 599 case 'n': 600 case 's': 601 case 't': 602 new_length++; 603 break; 604 case 'K': 605 case 'p': 606 case 'd': 607 case '\0': 608 case '\\': 609 case 'N': 610 new_length += 2; 611 break; 612 case 'T': 613 new_length += sending && phone_num ? strlen(phone_num) : 2; 614 break; 615 case 'U': 616 new_length += sending && phone_num2 ? strlen(phone_num2) : 2; 617 break; 618 default: 619 if (isoctal(*t)) { 620 t++; 621 if (isoctal(*t)) { 622 t++; 623 if (isoctal(*t)) 624 t++; 625 } 626 } 627 t--; 628 new_length += 2; /* Could become \\ */ 629 } 630 if (*t == '\0') 631 break; 632 } 633 634 new_length += 3; /* \r and two nuls */ 635 636 ret = malloc(new_length); 637 if (ret == NULL) 638 fatal(2, "cannot allocate memory"); 639 640 s1 = ret; 641 while (*s) { 642 cur_chr = *s++; 643 if (cur_chr == '^') { 644 cur_chr = *s++; 645 if (cur_chr == '\0') { 646 *s1++ = '^'; 647 break; 648 } 649 cur_chr &= 0x1F; 650 if (cur_chr != 0) { 651 *s1++ = cur_chr; 652 } 653 continue; 654 } 655 656 if (cur_chr != '\\') { 657 *s1++ = cur_chr; 658 continue; 659 } 660 661 cur_chr = *s++; 662 if (cur_chr == '\0') { 663 if (sending) { 664 *s1++ = '\\'; 665 *s1++ = '\\'; 666 } 667 break; 668 } 669 670 switch (cur_chr) { 671 case 'b': 672 *s1++ = '\b'; 673 break; 674 675 case 'c': 676 if (sending && *s == '\0') 677 add_return = 0; 678 else 679 *s1++ = cur_chr; 680 break; 681 682 case '\\': 683 case 'K': 684 case 'p': 685 case 'd': 686 if (sending) 687 *s1++ = '\\'; 688 689 *s1++ = cur_chr; 690 break; 691 692 case 'T': 693 if (sending && phone_num) { 694 for ( phchar = phone_num; *phchar != '\0'; phchar++) 695 *s1++ = *phchar; 696 } 697 else { 698 *s1++ = '\\'; 699 *s1++ = 'T'; 700 } 701 break; 702 703 case 'U': 704 if (sending && phone_num2) { 705 for ( phchar = phone_num2; *phchar != '\0'; phchar++) 706 *s1++ = *phchar; 707 } 708 else { 709 *s1++ = '\\'; 710 *s1++ = 'U'; 711 } 712 break; 713 714 case 'q': 715 quiet = 1; 716 break; 717 718 case 'r': 719 *s1++ = '\r'; 720 break; 721 722 case 'n': 723 *s1++ = '\n'; 724 break; 725 726 case 's': 727 *s1++ = ' '; 728 break; 729 730 case 't': 731 *s1++ = '\t'; 732 break; 733 734 case 'N': 735 if (sending) { 736 *s1++ = '\\'; 737 *s1++ = '\0'; 738 } 739 else 740 *s1++ = 'N'; 741 break; 742 743 default: 744 if (isoctal (cur_chr)) { 745 cur_chr &= 0x07; 746 if (isoctal (*s)) { 747 cur_chr <<= 3; 748 cur_chr |= *s++ - '0'; 749 if (isoctal (*s)) { 750 cur_chr <<= 3; 751 cur_chr |= *s++ - '0'; 752 } 753 } 754 755 if (cur_chr != 0 || sending) { 756 if (sending && (cur_chr == '\\' || cur_chr == 0)) 757 *s1++ = '\\'; 758 *s1++ = cur_chr; 759 } 760 break; 761 } 762 763 if (sending) 764 *s1++ = '\\'; 765 *s1++ = cur_chr; 766 break; 767 } 768 } 769 770 if (add_return) 771 *s1++ = '\r'; 772 773 *s1++ = '\0'; /* guarantee closure */ 774 *s1++ = '\0'; /* terminate the string */ 775 776 #ifdef DEBUG 777 fprintf(stderr, "clean(): guessed %d and used %d\n", new_length, s1-ret); 778 #endif 779 if (new_length < s1 - ret) 780 logmsg("clean(): new_length too short! %d < %d: \"%s\" -> \"%s\"", 781 new_length, s1 - ret, s, ret); 782 783 return ret; 784 } 785 786 /* 787 * A modified version of 'strtok'. This version skips \ sequences. 788 */ 789 790 char *expect_strtok (char *s, char *term) 791 { 792 static char *str = ""; 793 int escape_flag = 0; 794 char *result; 795 796 /* 797 * If a string was specified then do initial processing. 798 */ 799 if (s) 800 str = s; 801 802 /* 803 * If this is the escape flag then reset it and ignore the character. 804 */ 805 if (*str) 806 result = str; 807 else 808 result = (char *) 0; 809 810 while (*str) { 811 if (escape_flag) { 812 escape_flag = 0; 813 ++str; 814 continue; 815 } 816 817 if (*str == '\\') { 818 ++str; 819 escape_flag = 1; 820 continue; 821 } 822 823 /* 824 * If this is not in the termination string, continue. 825 */ 826 if (strchr (term, *str) == (char *) 0) { 827 ++str; 828 continue; 829 } 830 831 /* 832 * This is the terminator. Mark the end of the string and stop. 833 */ 834 *str++ = '\0'; 835 break; 836 } 837 return (result); 838 } 839 840 /* 841 * Process the expect string 842 */ 843 844 void chat_expect (char *s) 845 { 846 char *expect; 847 char *reply; 848 849 if (strcmp(s, "HANGUP") == 0) { 850 ++hup_next; 851 return; 852 } 853 854 if (strcmp(s, "ABORT") == 0) { 855 ++abort_next; 856 return; 857 } 858 859 if (strcmp(s, "CLR_ABORT") == 0) { 860 ++clear_abort_next; 861 return; 862 } 863 864 if (strcmp(s, "REPORT") == 0) { 865 ++report_next; 866 return; 867 } 868 869 if (strcmp(s, "CLR_REPORT") == 0) { 870 ++clear_report_next; 871 return; 872 } 873 874 if (strcmp(s, "TIMEOUT") == 0) { 875 ++timeout_next; 876 return; 877 } 878 879 if (strcmp(s, "ECHO") == 0) { 880 ++echo_next; 881 return; 882 } 883 884 if (strcmp(s, "SAY") == 0) { 885 ++say_next; 886 return; 887 } 888 889 /* 890 * Fetch the expect and reply string. 891 */ 892 for (;;) { 893 expect = expect_strtok (s, "-"); 894 s = (char *) 0; 895 896 if (expect == (char *) 0) 897 return; 898 899 reply = expect_strtok (s, "-"); 900 901 /* 902 * Handle the expect string. If successful then exit. 903 */ 904 if (get_string (expect)) 905 return; 906 907 /* 908 * If there is a sub-reply string then send it. Otherwise any condition 909 * is terminal. 910 */ 911 if (reply == (char *) 0 || exit_code != 3) 912 break; 913 914 chat_send (reply); 915 } 916 917 /* 918 * The expectation did not occur. This is terminal. 919 */ 920 if (fail_reason) 921 logmsg("Failed (%s)", fail_reason); 922 else 923 logmsg("Failed"); 924 terminate(exit_code); 925 } 926 927 /* 928 * Translate the input character to the appropriate string for printing 929 * the data. 930 */ 931 932 char *character(int c) 933 { 934 static char string[10]; 935 char *meta; 936 937 meta = (c & 0x80) ? "M-" : ""; 938 c &= 0x7F; 939 940 if (c < 32) 941 snprintf(string, sizeof string, "%s^%c", meta, (int)c + '@'); 942 else if (c == 127) 943 snprintf(string, sizeof string, "%s^?", meta); 944 else 945 snprintf(string, sizeof string, "%s%c", meta, c); 946 947 return (string); 948 } 949 950 /* 951 * process the reply string 952 */ 953 void chat_send (char *s) 954 { 955 const char *errstr; 956 957 if (say_next) { 958 say_next = 0; 959 s = clean(s,0); 960 write(STDERR_FILENO, s, strlen(s)); 961 free(s); 962 return; 963 } 964 965 if (hup_next) { 966 hup_next = 0; 967 if (strcmp(s, "OFF") == 0) 968 signal(SIGHUP, SIG_IGN); 969 else 970 signal(SIGHUP, sighup); 971 return; 972 } 973 974 if (echo_next) { 975 echo_next = 0; 976 echo = (strcmp(s, "ON") == 0); 977 return; 978 } 979 980 if (abort_next) { 981 char *s1; 982 983 abort_next = 0; 984 985 if (n_aborts >= MAX_ABORTS) 986 fatal(2, "Too many ABORT strings"); 987 988 s1 = clean(s, 0); 989 990 if (strlen(s1) > strlen(s) 991 || strlen(s1) + 1 > sizeof(fail_buffer)) 992 fatal(1, "Illegal or too-long ABORT string ('%v')", s); 993 994 abort_string[n_aborts++] = s1; 995 996 if (verbose) 997 logmsg("abort on (%v)", s); 998 return; 999 } 1000 1001 if (clear_abort_next) { 1002 char *s1; 1003 int i; 1004 int old_max; 1005 int pack = 0; 1006 1007 clear_abort_next = 0; 1008 1009 s1 = clean(s, 0); 1010 1011 if (strlen(s1) > strlen(s) 1012 || strlen(s1) + 1 > sizeof(fail_buffer)) 1013 fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); 1014 1015 old_max = n_aborts; 1016 for (i=0; i < n_aborts; i++) { 1017 if ( strcmp(s1,abort_string[i]) == 0 ) { 1018 free(abort_string[i]); 1019 abort_string[i] = NULL; 1020 pack++; 1021 n_aborts--; 1022 if (verbose) 1023 logmsg("clear abort on (%v)", s); 1024 } 1025 } 1026 free(s1); 1027 if (pack) 1028 pack_array(abort_string,old_max); 1029 return; 1030 } 1031 1032 if (report_next) { 1033 char *s1; 1034 1035 report_next = 0; 1036 if (n_reports >= MAX_REPORTS) 1037 fatal(2, "Too many REPORT strings"); 1038 1039 s1 = clean(s, 0); 1040 1041 if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 1042 fatal(1, "Illegal or too-long REPORT string ('%v')", s); 1043 1044 report_string[n_reports++] = s1; 1045 1046 if (verbose) 1047 logmsg("report (%v)", s); 1048 return; 1049 } 1050 1051 if (clear_report_next) { 1052 char *s1; 1053 int i; 1054 int old_max; 1055 int pack = 0; 1056 1057 clear_report_next = 0; 1058 1059 s1 = clean(s, 0); 1060 1061 if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 1062 fatal(1, "Illegal or too-long REPORT string ('%v')", s); 1063 1064 old_max = n_reports; 1065 for (i=0; i < n_reports; i++) { 1066 if ( strcmp(s1,report_string[i]) == 0 ) { 1067 free(report_string[i]); 1068 report_string[i] = NULL; 1069 pack++; 1070 n_reports--; 1071 if (verbose) 1072 logmsg("clear report (%v)", s); 1073 } 1074 } 1075 free(s1); 1076 if (pack) 1077 pack_array(report_string,old_max); 1078 1079 return; 1080 } 1081 1082 if (timeout_next) { 1083 timeout_next = 0; 1084 timeout = strtonum(s, -1, 10000, &errstr); 1085 if (errstr) { 1086 logmsg("invalid timeout %s: %s\n", s, errstr); 1087 timeout = -1; 1088 } 1089 if (timeout <= 0) 1090 timeout = DEFAULT_CHAT_TIMEOUT; 1091 1092 if (verbose) 1093 logmsg("timeout set to %d seconds", timeout); 1094 1095 return; 1096 } 1097 1098 if (strcmp(s, "EOT") == 0) 1099 s = "^D\\c"; 1100 else if (strcmp(s, "BREAK") == 0) 1101 s = "\\K\\c"; 1102 1103 if (!put_string(s)) 1104 fatal(1, "Failed"); 1105 } 1106 1107 int get_char(void) 1108 { 1109 int status; 1110 char c; 1111 1112 status = read(0, &c, 1); 1113 1114 switch (status) { 1115 case 1: 1116 return ((int)c & 0x7F); 1117 1118 default: 1119 logmsg("warning: read() on stdin returned %d", status); 1120 1121 case -1: 1122 if ((status = fcntl(0, F_GETFL)) == -1) 1123 fatal(2, "Can't get file mode flags on stdin: %m"); 1124 1125 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 1126 fatal(2, "Can't set file mode flags on stdin: %m"); 1127 1128 return (-1); 1129 } 1130 } 1131 1132 int put_char(int c) 1133 { 1134 int status; 1135 char ch = c; 1136 1137 usleep(10000); /* inter-character typing delay (?) */ 1138 1139 status = write(STDOUT_FILENO, &ch, 1); 1140 1141 switch (status) { 1142 case 1: 1143 return (0); 1144 1145 default: 1146 logmsg("warning: write() on stdout returned %d", status); 1147 1148 case -1: 1149 if ((status = fcntl(0, F_GETFL)) == -1) 1150 fatal(2, "Can't get file mode flags on stdin, %m"); 1151 1152 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 1153 fatal(2, "Can't set file mode flags on stdin: %m"); 1154 1155 return (-1); 1156 } 1157 } 1158 1159 int write_char (int c) 1160 { 1161 if (alarmed || put_char(c) < 0) { 1162 alarm(0); 1163 alarmed = 0; 1164 1165 if (verbose) { 1166 if (errno == EINTR || errno == EWOULDBLOCK) 1167 logmsg(" -- write timed out"); 1168 else 1169 logmsg(" -- write failed: %m"); 1170 } 1171 return (0); 1172 } 1173 return (1); 1174 } 1175 1176 int put_string (char *s) 1177 { 1178 quiet = 0; 1179 s = clean(s, 1); 1180 1181 if (verbose) { 1182 if (quiet) 1183 logmsg("send (hidden)"); 1184 else 1185 logmsg("send (%v)", s); 1186 } 1187 1188 alarm(timeout); alarmed = 0; 1189 1190 while (*s) { 1191 char c = *s++; 1192 1193 if (c != '\\') { 1194 if (!write_char (c)) 1195 return 0; 1196 continue; 1197 } 1198 1199 c = *s++; 1200 switch (c) { 1201 case 'd': 1202 sleep(1); 1203 break; 1204 1205 case 'K': 1206 break_sequence(); 1207 break; 1208 1209 case 'p': 1210 usleep(10000); /* 1/100th of a second (arg is microseconds) */ 1211 break; 1212 1213 default: 1214 if (!write_char (c)) 1215 return 0; 1216 break; 1217 } 1218 } 1219 1220 alarm(0); 1221 alarmed = 0; 1222 return (1); 1223 } 1224 1225 /* 1226 * Echo a character to stderr. 1227 * When called with -1, a '\n' character is generated when 1228 * the cursor is not at the beginning of a line. 1229 */ 1230 void echo_stderr(int n) 1231 { 1232 static int need_lf; 1233 char *s; 1234 1235 switch (n) { 1236 case '\r': /* ignore '\r' */ 1237 break; 1238 case -1: 1239 if (need_lf == 0) 1240 break; 1241 /* fall through */ 1242 case '\n': 1243 write(STDERR_FILENO, "\n", 1); 1244 need_lf = 0; 1245 break; 1246 default: 1247 s = character(n); 1248 write(STDERR_FILENO, s, strlen(s)); 1249 need_lf = 1; 1250 break; 1251 } 1252 } 1253 1254 /* 1255 * 'Wait for' this string to appear on this file descriptor. 1256 */ 1257 int get_string(char *string) 1258 { 1259 char temp[STR_LEN]; 1260 int c, printed = 0, len, minlen; 1261 char *s = temp, *end = s + STR_LEN; 1262 char *logged = temp; 1263 1264 fail_reason = NULL; 1265 string = clean(string, 0); 1266 len = strlen(string); 1267 minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; 1268 1269 if (verbose) 1270 logmsg("expect (%v)", string); 1271 1272 if (len > STR_LEN) { 1273 logmsg("expect string is too long"); 1274 exit_code = 1; 1275 return 0; 1276 } 1277 1278 if (len == 0) { 1279 if (verbose) 1280 logmsg("got it"); 1281 return (1); 1282 } 1283 1284 alarm(timeout); 1285 alarmed = 0; 1286 1287 while ( ! alarmed && (c = get_char()) >= 0) { 1288 int n, abort_len, report_len; 1289 1290 if (echo) 1291 echo_stderr(c); 1292 if (verbose && c == '\n') { 1293 if (s == logged) 1294 logmsg(""); /* blank line */ 1295 else 1296 logmsg("%0.*v", s - logged, logged); 1297 logged = s + 1; 1298 } 1299 1300 *s++ = c; 1301 1302 if (verbose && s >= logged + 80) { 1303 logmsg("%0.*v", s - logged, logged); 1304 logged = s; 1305 } 1306 1307 if (Verbose) { 1308 if (c == '\n') 1309 fputc( '\n', stderr ); 1310 else if (c != '\r') 1311 fprintf( stderr, "%s", character(c) ); 1312 } 1313 1314 if (!report_gathering) { 1315 for (n = 0; n < n_reports; ++n) { 1316 if ((report_string[n] != (char*) NULL) && 1317 s - temp >= (report_len = strlen(report_string[n])) && 1318 strncmp(s - report_len, report_string[n], report_len) == 0) { 1319 time_t time_now = time (NULL); 1320 struct tm* tm_now = localtime (&time_now); 1321 1322 strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); 1323 strlcat (report_buffer, report_string[n], sizeof(report_buffer)); 1324 1325 report_string[n] = (char *) NULL; 1326 report_gathering = 1; 1327 break; 1328 } 1329 } 1330 } 1331 else { 1332 if (!iscntrl (c)) { 1333 int rep_len = strlen (report_buffer); 1334 report_buffer[rep_len] = c; 1335 report_buffer[rep_len + 1] = '\0'; 1336 } 1337 else { 1338 report_gathering = 0; 1339 fprintf (report_fp, "chat: %s\n", report_buffer); 1340 } 1341 } 1342 1343 if (s - temp >= len && 1344 c == string[len - 1] && 1345 strncmp(s - len, string, len) == 0) { 1346 if (verbose) { 1347 if (s > logged) 1348 logmsg("%0.*v", s - logged, logged); 1349 logmsg(" -- got it\n"); 1350 } 1351 1352 alarm(0); 1353 alarmed = 0; 1354 return (1); 1355 } 1356 1357 for (n = 0; n < n_aborts; ++n) { 1358 if (s - temp >= (abort_len = strlen(abort_string[n])) && 1359 strncmp(s - abort_len, abort_string[n], abort_len) == 0) { 1360 if (verbose) { 1361 if (s > logged) 1362 logmsg("%0.*v", s - logged, logged); 1363 logmsg(" -- failed"); 1364 } 1365 1366 alarm(0); 1367 alarmed = 0; 1368 exit_code = n + 4; 1369 strlcpy(fail_buffer, abort_string[n], sizeof fail_buffer); 1370 fail_reason = fail_buffer; 1371 return (0); 1372 } 1373 } 1374 1375 if (s >= end) { 1376 if (logged < s - minlen) { 1377 logmsg("%0.*v", s - logged, logged); 1378 logged = s; 1379 } 1380 s -= minlen; 1381 memmove(temp, s, minlen); 1382 logged = temp + (logged - s); 1383 s = temp + minlen; 1384 } 1385 1386 if (alarmed && verbose) 1387 logmsg("warning: alarm synchronization problem"); 1388 } 1389 1390 alarm(0); 1391 1392 if (verbose && printed) { 1393 if (alarmed) 1394 logmsg(" -- read timed out"); 1395 else 1396 logmsg(" -- read failed: %m"); 1397 } 1398 1399 exit_code = 3; 1400 alarmed = 0; 1401 return (0); 1402 } 1403 1404 void 1405 pack_array (char **array, int end) 1406 { 1407 int i, j; 1408 1409 for (i = 0; i < end; i++) { 1410 if (array[i] == NULL) { 1411 for (j = i+1; j < end; ++j) 1412 if (array[j] != NULL) 1413 array[i++] = array[j]; 1414 for (; i < end; ++i) 1415 array[i] = NULL; 1416 break; 1417 } 1418 } 1419 } 1420 1421 /* 1422 * vfmtmsg - format a message into a buffer. Like vsnprintf except we 1423 * also specify the length of the output buffer, and we handle the 1424 * %m (error message) format. 1425 * Doesn't do floating-point formats. 1426 * Returns the number of chars put into buf. 1427 */ 1428 #define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) 1429 1430 int 1431 vfmtmsg(char *buf, int buflen, const char *fmt, va_list args) 1432 { 1433 int c, i, n; 1434 int width, prec, fillch; 1435 int base, len, neg, quoted; 1436 unsigned long val = 0; 1437 char *str, *buf0; 1438 const char *f; 1439 unsigned char *p; 1440 char num[32]; 1441 static char hexchars[] = "0123456789abcdef"; 1442 1443 buf0 = buf; 1444 --buflen; 1445 while (buflen > 0) { 1446 for (f = fmt; *f != '%' && *f != 0; ++f) 1447 ; 1448 if (f > fmt) { 1449 len = f - fmt; 1450 if (len > buflen) 1451 len = buflen; 1452 memcpy(buf, fmt, len); 1453 buf += len; 1454 buflen -= len; 1455 fmt = f; 1456 } 1457 if (*fmt == 0) 1458 break; 1459 c = *++fmt; 1460 width = prec = 0; 1461 fillch = ' '; 1462 if (c == '0') { 1463 fillch = '0'; 1464 c = *++fmt; 1465 } 1466 if (c == '*') { 1467 width = va_arg(args, int); 1468 c = *++fmt; 1469 } else { 1470 while (isdigit(c)) { 1471 width = width * 10 + c - '0'; 1472 c = *++fmt; 1473 } 1474 } 1475 if (c == '.') { 1476 c = *++fmt; 1477 if (c == '*') { 1478 prec = va_arg(args, int); 1479 c = *++fmt; 1480 } else { 1481 while (isdigit(c)) { 1482 prec = prec * 10 + c - '0'; 1483 c = *++fmt; 1484 } 1485 } 1486 } 1487 str = 0; 1488 base = 0; 1489 neg = 0; 1490 ++fmt; 1491 switch (c) { 1492 case 'd': 1493 i = va_arg(args, int); 1494 if (i < 0) { 1495 neg = 1; 1496 val = -i; 1497 } else 1498 val = i; 1499 base = 10; 1500 break; 1501 case 'o': 1502 val = va_arg(args, unsigned int); 1503 base = 8; 1504 break; 1505 case 'x': 1506 val = va_arg(args, unsigned int); 1507 base = 16; 1508 break; 1509 case 'p': 1510 val = (unsigned long) va_arg(args, void *); 1511 base = 16; 1512 neg = 2; 1513 break; 1514 case 's': 1515 str = va_arg(args, char *); 1516 break; 1517 case 'c': 1518 num[0] = va_arg(args, int); 1519 num[1] = 0; 1520 str = num; 1521 break; 1522 case 'm': 1523 str = strerror(errno); 1524 break; 1525 case 'v': /* "visible" string */ 1526 case 'q': /* quoted string */ 1527 quoted = c == 'q'; 1528 p = va_arg(args, unsigned char *); 1529 if (fillch == '0' && prec > 0) { 1530 n = prec; 1531 } else { 1532 n = strlen((char *)p); 1533 if (prec > 0 && prec < n) 1534 n = prec; 1535 } 1536 while (n > 0 && buflen > 0) { 1537 c = *p++; 1538 --n; 1539 if (!quoted && c >= 0x80) { 1540 OUTCHAR('M'); 1541 OUTCHAR('-'); 1542 c -= 0x80; 1543 } 1544 if (quoted && (c == '"' || c == '\\')) 1545 OUTCHAR('\\'); 1546 if (c < 0x20 || (0x7f <= c && c < 0xa0)) { 1547 if (quoted) { 1548 OUTCHAR('\\'); 1549 switch (c) { 1550 case '\t': OUTCHAR('t'); break; 1551 case '\n': OUTCHAR('n'); break; 1552 case '\b': OUTCHAR('b'); break; 1553 case '\f': OUTCHAR('f'); break; 1554 default: 1555 OUTCHAR('x'); 1556 OUTCHAR(hexchars[c >> 4]); 1557 OUTCHAR(hexchars[c & 0xf]); 1558 } 1559 } else { 1560 if (c == '\t') 1561 OUTCHAR(c); 1562 else { 1563 OUTCHAR('^'); 1564 OUTCHAR(c ^ 0x40); 1565 } 1566 } 1567 } else 1568 OUTCHAR(c); 1569 } 1570 continue; 1571 default: 1572 *buf++ = '%'; 1573 if (c != '%') 1574 --fmt; /* so %z outputs %z etc. */ 1575 --buflen; 1576 continue; 1577 } 1578 if (base != 0) { 1579 str = num + sizeof(num); 1580 *--str = 0; 1581 while (str > num + neg) { 1582 *--str = hexchars[val % base]; 1583 val = val / base; 1584 if (--prec <= 0 && val == 0) 1585 break; 1586 } 1587 switch (neg) { 1588 case 1: 1589 *--str = '-'; 1590 break; 1591 case 2: 1592 *--str = 'x'; 1593 *--str = '0'; 1594 break; 1595 } 1596 len = num + sizeof(num) - 1 - str; 1597 } else { 1598 len = strlen(str); 1599 if (prec > 0 && len > prec) 1600 len = prec; 1601 } 1602 if (width > 0) { 1603 if (width > buflen) 1604 width = buflen; 1605 if ((n = width - len) > 0) { 1606 buflen -= n; 1607 for (; n > 0; --n) 1608 *buf++ = fillch; 1609 } 1610 } 1611 if (len > buflen) 1612 len = buflen; 1613 memcpy(buf, str, len); 1614 buf += len; 1615 buflen -= len; 1616 } 1617 *buf = 0; 1618 return buf - buf0; 1619 } 1620