2 * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: src/usr.sbin/ppp/chat.c,v 1.70.2.3 2002/09/01 02:12:23 brian Exp $
27 * $DragonFly: src/usr.sbin/ppp/chat.c,v 1.2 2003/06/17 04:30:00 dillon Exp $
30 #include <sys/param.h>
31 #include <netinet/in.h>
32 #include <netinet/in_systm.h>
33 #include <netinet/ip.h>
34 #include <sys/socket.h>
54 #include "throughput.h"
60 #include "descriptor.h"
66 #include "slcompress.h"
82 #define BUFLEFT(c) (sizeof (c)->buf - ((c)->bufend - (c)->buf))
84 static void ExecStr(struct physical
*, char *, char *, int);
85 static char *ExpandString(struct chat
*, const char *, char *, int, int);
88 chat_PauseTimer(void *v
)
90 struct chat
*c
= (struct chat
*)v
;
91 timer_Stop(&c
->pause
);
96 chat_Pause(struct chat
*c
, u_long load
)
98 timer_Stop(&c
->pause
);
99 c
->pause
.load
+= load
;
100 c
->pause
.func
= chat_PauseTimer
;
101 c
->pause
.name
= "chat pause";
103 timer_Start(&c
->pause
);
107 chat_TimeoutTimer(void *v
)
109 struct chat
*c
= (struct chat
*)v
;
110 timer_Stop(&c
->timeout
);
115 chat_SetTimeout(struct chat
*c
)
117 timer_Stop(&c
->timeout
);
118 if (c
->TimeoutSec
> 0) {
119 c
->timeout
.load
= SECTICKS
* c
->TimeoutSec
;
120 c
->timeout
.func
= chat_TimeoutTimer
;
121 c
->timeout
.name
= "chat timeout";
123 timer_Start(&c
->timeout
);
128 chat_NextChar(char *ptr
, char ch
)
133 else if (*ptr
== '\\')
141 chat_UpdateSet(struct fdescriptor
*d
, fd_set
*r
, fd_set
*w
, fd_set
*e
, int *n
)
143 struct chat
*c
= descriptor2chat(d
);
144 int special
, gotabort
, gottimeout
, needcr
;
145 int TimedOut
= c
->TimedOut
;
146 static char arg_term
; /* An empty string */
148 if (c
->pause
.state
== TIMER_RUNNING
)
152 log_Printf(LogCHAT
, "Expect timeout\n");
153 if (c
->nargptr
== NULL
)
154 c
->state
= CHAT_FAILED
;
156 /* c->state = CHAT_EXPECT; */
157 c
->argptr
= &arg_term
;
162 if (c
->state
!= CHAT_EXPECT
&& c
->state
!= CHAT_SEND
)
165 gottimeout
= gotabort
= 0;
167 if (c
->arg
< c
->argc
&& (c
->arg
< 0 || *c
->argptr
== '\0')) {
168 /* Go get the next string */
169 if (c
->arg
< 0 || c
->state
== CHAT_SEND
)
170 c
->state
= CHAT_EXPECT
;
172 c
->state
= CHAT_SEND
;
175 while (special
&& (c
->nargptr
|| c
->arg
< c
->argc
- 1)) {
176 if (c
->arg
< 0 || (!TimedOut
&& c
->state
== CHAT_SEND
))
179 if (c
->nargptr
!= NULL
) {
180 /* We're doing expect-send-expect.... */
181 c
->argptr
= c
->nargptr
;
182 /* Put the '-' back in case we ever want to rerun our script */
183 c
->nargptr
[-1] = '-';
184 c
->nargptr
= chat_NextChar(c
->nargptr
, '-');
185 if (c
->nargptr
!= NULL
)
186 *c
->nargptr
++ = '\0';
190 if ((c
->argptr
= c
->argv
[++c
->arg
]) == NULL
) {
191 /* End of script - all ok */
192 c
->state
= CHAT_DONE
;
196 if (c
->state
== CHAT_EXPECT
) {
197 /* Look for expect-send-expect sequence */
198 c
->nargptr
= c
->argptr
;
200 while ((c
->nargptr
= chat_NextChar(c
->nargptr
, '-'))) {
206 log_Printf(LogWARN
, "chat_UpdateSet: \"%s\": Uneven number of"
207 " '-' chars, all ignored\n", c
->argptr
);
209 c
->nargptr
= chat_NextChar(c
->argptr
, '-');
210 *c
->nargptr
++ = '\0';
216 * c->argptr now temporarily points into c->script (via c->argv)
217 * If it's an expect-send-expect sequence, we've just got the correct
218 * portion of that sequence.
221 needcr
= c
->state
== CHAT_SEND
&&
222 (*c
->argptr
!= '!' || c
->argptr
[1] == '!');
224 /* We leave room for a potential HDLC header in the target string */
225 ExpandString(c
, c
->argptr
, c
->exp
+ 2, sizeof c
->exp
- 2, needcr
);
228 * Now read our string. If it's not a special string, we unset
229 * ``special'' to break out of the loop.
232 if (c
->abort
.num
< MAXABORTS
) {
235 len
= strlen(c
->exp
+2);
236 for (i
= 0; i
< c
->abort
.num
; i
++)
237 if (len
> c
->abort
.string
[i
].len
) {
240 for (last
= c
->abort
.num
; last
> i
; last
--) {
241 c
->abort
.string
[last
].data
= c
->abort
.string
[last
-1].data
;
242 c
->abort
.string
[last
].len
= c
->abort
.string
[last
-1].len
;
246 c
->abort
.string
[i
].len
= len
;
247 c
->abort
.string
[i
].data
= (char *)malloc(len
+1);
248 memcpy(c
->abort
.string
[i
].data
, c
->exp
+2, len
+1);
251 log_Printf(LogERROR
, "chat_UpdateSet: too many abort strings\n");
253 } else if (gottimeout
) {
254 c
->TimeoutSec
= atoi(c
->exp
+ 2);
255 if (c
->TimeoutSec
<= 0)
258 } else if (c
->nargptr
== NULL
&& !strcmp(c
->exp
+2, "ABORT"))
260 else if (c
->nargptr
== NULL
&& !strcmp(c
->exp
+2, "TIMEOUT"))
263 if (c
->exp
[2] == '!' && c
->exp
[3] != '!')
264 ExecStr(c
->physical
, c
->exp
+ 3, c
->exp
+ 3, sizeof c
->exp
- 3);
266 if (c
->exp
[2] == '\0') {
267 /* Empty string, reparse (this may be better as a `goto start') */
268 c
->argptr
= &arg_term
;
269 return chat_UpdateSet(d
, r
, w
, e
, n
);
278 log_Printf(LogWARN
, "chat_UpdateSet: TIMEOUT: Argument expected\n");
280 log_Printf(LogWARN
, "chat_UpdateSet: ABORT: Argument expected\n");
282 /* End of script - all ok */
283 c
->state
= CHAT_DONE
;
287 /* set c->argptr to point in the right place */
288 c
->argptr
= c
->exp
+ (c
->exp
[2] == '!' ? 3 : 2);
289 c
->arglen
= strlen(c
->argptr
);
291 if (c
->state
== CHAT_EXPECT
) {
292 /* We must check to see if the string's already been found ! */
295 end
= c
->bufend
- c
->arglen
+ 1;
296 if (end
< c
->bufstart
)
298 for (begin
= c
->bufstart
; begin
< end
; begin
++)
299 if (!strncmp(begin
, c
->argptr
, c
->arglen
)) {
300 c
->bufstart
= begin
+ c
->arglen
;
301 c
->argptr
+= c
->arglen
;
303 /* Continue - we've already read our expect string */
304 return chat_UpdateSet(d
, r
, w
, e
, n
);
307 log_Printf(LogCHAT
, "Expect(%d): %s\n", c
->TimeoutSec
, c
->argptr
);
313 * We now have c->argptr pointing at what we want to expect/send and
314 * c->state saying what we want to do... we now know what to put in
318 if (c
->state
== CHAT_EXPECT
)
319 return physical_doUpdateSet(&c
->physical
->desc
, r
, NULL
, e
, n
, 1);
321 return physical_doUpdateSet(&c
->physical
->desc
, NULL
, w
, e
, n
, 1);
325 chat_IsSet(struct fdescriptor
*d
, const fd_set
*fdset
)
327 struct chat
*c
= descriptor2chat(d
);
328 return c
->argptr
&& physical_IsSet(&c
->physical
->desc
, fdset
);
332 chat_UpdateLog(struct chat
*c
, int in
)
334 if (log_IsKept(LogCHAT
) || log_IsKept(LogCONNECT
)) {
336 * If a linefeed appears in the last `in' characters of `c's input
337 * buffer, output from there, all the way back to the last linefeed.
338 * This is called for every read of `in' bytes.
340 char *ptr
, *end
, *stop
, ch
;
343 level
= log_IsKept(LogCHAT
) ? LogCHAT
: LogCONNECT
;
345 end
= ptr
= c
->bufend
;
347 ptr
= c
->bufend
- in
;
348 for (end
= c
->bufend
- 1; end
>= ptr
; end
--)
354 for (ptr
= c
->bufend
- (in
== -1 ? 1 : in
+ 1); ptr
>= c
->bufstart
; ptr
--)
360 if ((stop
= memchr(ptr
, '\n', end
- ptr
)) == NULL
)
364 if (level
== LogCHAT
|| strstr(ptr
, "CONNECT"))
365 log_Printf(level
, "Received: %s\n", ptr
);
374 chat_Read(struct fdescriptor
*d
, struct bundle
*bundle
, const fd_set
*fdset
)
376 struct chat
*c
= descriptor2chat(d
);
378 if (c
->state
== CHAT_EXPECT
) {
380 char *abegin
, *ebegin
, *begin
, *aend
, *eend
, *end
;
384 * XXX - should this read only 1 byte to guarantee that we don't
385 * swallow any ppp talk from the peer ?
388 if (in
> sizeof c
->buf
/ 2)
389 in
= sizeof c
->buf
/ 2;
391 in
= physical_Read(c
->physical
, c
->bufend
, in
);
395 /* `begin' and `end' delimit where we're going to strncmp() from */
396 ebegin
= c
->bufend
- c
->arglen
+ 1;
398 if (ebegin
< c
->bufstart
)
399 ebegin
= c
->bufstart
;
402 abegin
= c
->bufend
- c
->abort
.string
[0].len
+ 1;
403 aend
= c
->bufend
- c
->abort
.string
[c
->abort
.num
-1].len
+ in
+ 1;
404 if (abegin
< c
->bufstart
)
405 abegin
= c
->bufstart
;
410 begin
= abegin
< ebegin
? abegin
: ebegin
;
411 end
= aend
< eend
? eend
: aend
;
415 chat_UpdateLog(c
, in
);
417 if (c
->bufend
> c
->buf
+ sizeof c
->buf
/ 2) {
418 /* Shuffle our receive buffer back a bit */
421 for (chop
= begin
- c
->buf
; chop
; chop
--)
422 if (c
->buf
[chop
] == '\n')
423 /* found some already-logged garbage to remove :-) */
427 chop
= begin
- c
->buf
;
434 while (from
< c
->bufend
)
447 for (; begin
< end
; begin
++)
448 if (begin
>= ebegin
&& begin
< eend
&&
449 !strncmp(begin
, c
->argptr
, c
->arglen
)) {
451 timer_Stop(&c
->timeout
);
452 if (memchr(begin
+ c
->arglen
- 1, '\n',
453 c
->bufend
- begin
- c
->arglen
+ 1) == NULL
) {
454 /* force it into the log */
456 c
->bufend
= begin
+ c
->arglen
;
457 chat_UpdateLog(c
, -1);
460 c
->bufstart
= begin
+ c
->arglen
;
461 c
->argptr
+= c
->arglen
;
464 } else if (begin
>= abegin
&& begin
< aend
) {
465 for (n
= c
->abort
.num
- 1; n
>= 0; n
--) {
466 if (begin
+ c
->abort
.string
[n
].len
> c
->bufend
)
468 if (!strncmp(begin
, c
->abort
.string
[n
].data
,
469 c
->abort
.string
[n
].len
)) {
470 if (memchr(begin
+ c
->abort
.string
[n
].len
- 1, '\n',
471 c
->bufend
- begin
- c
->abort
.string
[n
].len
+ 1) == NULL
) {
472 /* force it into the log */
474 c
->bufend
= begin
+ c
->abort
.string
[n
].len
;
475 chat_UpdateLog(c
, -1);
478 c
->bufstart
= begin
+ c
->abort
.string
[n
].len
;
479 c
->state
= CHAT_FAILED
;
488 chat_Write(struct fdescriptor
*d
, struct bundle
*bundle
, const fd_set
*fdset
)
490 struct chat
*c
= descriptor2chat(d
);
493 if (c
->state
== CHAT_SEND
) {
496 if (strstr(c
->argv
[c
->arg
], "\\P")) /* Don't log the password */
497 log_Printf(LogCHAT
, "Send: %s\n", c
->argv
[c
->arg
]);
502 while (sz
>= 0 && c
->argptr
[sz
] == '\n')
504 log_Printf(LogCHAT
, "Send: %.*s\n", sz
+ 1, c
->argptr
);
507 if (physical_IsSync(c
->physical
)) {
510 * This data should be stuffed down through the link layers
512 /* There's always room for the HDLC header */
515 memcpy(c
->argptr
, "\377\003", 2); /* Prepend HDLC header */
518 wrote
= physical_Write(c
->physical
, c
->argptr
, c
->arglen
);
519 result
= wrote
> 0 ? 1 : 0;
521 if (errno
!= EINTR
) {
522 log_Printf(LogWARN
, "chat_Write: %s\n", strerror(errno
));
525 if (physical_IsSync(c
->physical
)) {
529 } else if (wrote
< 2 && physical_IsSync(c
->physical
)) {
530 /* Oops - didn't even write our HDLC header ! */
543 chat_Init(struct chat
*c
, struct physical
*p
)
545 c
->desc
.type
= CHAT_DESCRIPTOR
;
546 c
->desc
.UpdateSet
= chat_UpdateSet
;
547 c
->desc
.IsSet
= chat_IsSet
;
548 c
->desc
.Read
= chat_Read
;
549 c
->desc
.Write
= chat_Write
;
556 c
->bufstart
= c
->bufend
= c
->buf
;
558 memset(&c
->pause
, '\0', sizeof c
->pause
);
559 memset(&c
->timeout
, '\0', sizeof c
->timeout
);
563 chat_Setup(struct chat
*c
, const char *data
, const char *phone
)
565 c
->state
= CHAT_EXPECT
;
571 strncpy(c
->script
, data
, sizeof c
->script
- 1);
572 c
->script
[sizeof c
->script
- 1] = '\0';
573 c
->argc
= MakeArgs(c
->script
, c
->argv
, VECSIZE(c
->argv
), PARSE_NOHASH
);
585 timer_Stop(&c
->pause
);
586 timer_Stop(&c
->timeout
);
592 chat_Finish(struct chat
*c
)
594 timer_Stop(&c
->pause
);
595 timer_Stop(&c
->timeout
);
597 free(c
->abort
.string
[--c
->abort
.num
].data
);
602 chat_Destroy(struct chat
*c
)
609 * \d Sleep a little (delay 2 seconds
610 * \n Line feed character
611 * \P Auth Key password
613 * \r Carrige return character
615 * \T Telephone number(s) (defined via `set phone')
620 ExpandString(struct chat
*c
, const char *str
, char *result
, int reslen
, int cr
)
624 result
[--reslen
] = '\0';
625 while (*str
&& reslen
> 0) {
633 case 'd': /* Delay 2 seconds */
634 chat_Pause(c
, 2 * SECTICKS
);
637 chat_Pause(c
, SECTICKS
/ 4);
638 break; /* Delay 0.25 seconds */
656 strncpy(result
, c
->physical
->dl
->bundle
->cfg
.auth
.key
, reslen
);
657 len
= strlen(result
);
663 strncpy(result
, c
->phone
, reslen
);
664 len
= strlen(result
);
670 strncpy(result
, c
->physical
->dl
->bundle
->cfg
.auth
.name
, reslen
);
671 len
= strlen(result
);
686 *result
++ = *str
++ & 0x1f;
706 ExecStr(struct physical
*physical
, char *command
, char *out
, int olen
)
710 char *argv
[MAXARGS
], *vector
[MAXARGS
], *startout
, *endout
;
711 int stat
, nb
, argc
, i
;
713 log_Printf(LogCHAT
, "Exec: %s\n", command
);
714 if ((argc
= MakeArgs(command
, vector
, VECSIZE(vector
),
715 PARSE_REDUCE
|PARSE_NOHASH
)) <= 0) {
717 log_Printf(LogWARN
, "Syntax error in exec command\n");
722 if (pipe(fids
) < 0) {
723 log_Printf(LogCHAT
, "Unable to create pipe in ExecStr: %s\n",
728 if ((pid
= fork()) == 0) {
729 command_Expand(argv
, argc
, (char const *const *)vector
,
730 physical
->dl
->bundle
, 0, getpid());
733 if (fids
[1] == STDIN_FILENO
)
734 fids
[1] = dup(fids
[1]);
735 dup2(physical
->fd
, STDIN_FILENO
);
736 dup2(fids
[1], STDERR_FILENO
);
737 dup2(STDIN_FILENO
, STDOUT_FILENO
);
739 if (open(_PATH_TTY
, O_RDWR
) != 3)
740 open(_PATH_DEVNULL
, O_RDWR
); /* Leave it closed if it fails... */
741 for (i
= getdtablesize(); i
> 3; i
--)
742 fcntl(i
, F_SETFD
, 1);
744 setuid(ID0realuid());
746 execvp(argv
[0], argv
);
747 fprintf(stderr
, "execvp: %s: %s\n", argv
[0], strerror(errno
));
750 char *name
= strdup(vector
[0]);
753 endout
= out
+ olen
- 1;
755 while (out
< endout
) {
756 nb
= read(fids
[0], out
, 1);
764 waitpid(pid
, &stat
, WNOHANG
);
765 if (WIFSIGNALED(stat
)) {
766 log_Printf(LogWARN
, "%s: signal %d\n", name
, WTERMSIG(stat
));
770 } else if (WIFEXITED(stat
)) {
771 switch (WEXITSTATUS(stat
)) {
776 log_Printf(LogWARN
, "%s: %s\n", name
, startout
);
782 log_Printf(LogWARN
, "%s: exit %d\n", name
, WEXITSTATUS(stat
));
789 log_Printf(LogWARN
, "%s: Unexpected exit result\n", name
);