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
main(int argc,char ** argv)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
do_file(char * chat_file)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 */
usage(void)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 */
logmsg(const char * fmt,...)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
fatal(int code,const char * fmt,...)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
sigalrm(int signo)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
unalarm(void)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
sigint(int signo)477 SIGTYPE sigint(int signo)
478 {
479 fatal(2, "SIGINT");
480 }
481
sigterm(int signo)482 SIGTYPE sigterm(int signo)
483 {
484 fatal(2, "SIGTERM");
485 }
486
sighup(int signo)487 SIGTYPE sighup(int signo)
488 {
489 fatal(2, "SIGHUP");
490 }
491
init(void)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
set_tty_parameters(void)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
break_sequence(void)528 void break_sequence(void)
529 {
530 #ifdef TERMIOS
531 tcsendbreak (0, 0);
532 #endif
533 }
534
terminate(int status)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 */
clean(char * s,int sending)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
expect_strtok(char * s,char * term)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
chat_expect(char * s)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
character(int c)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 */
chat_send(char * s)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
get_char(void)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
put_char(int c)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
write_char(int c)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
put_string(char * s)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 */
echo_stderr(int n)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 */
get_string(char * string)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
pack_array(char ** array,int end)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
vfmtmsg(char * buf,int buflen,const char * fmt,va_list args)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