136285Sbrian/*-
236285Sbrian * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
336285Sbrian * All rights reserved.
46059Samurai *
536285Sbrian * Redistribution and use in source and binary forms, with or without
636285Sbrian * modification, are permitted provided that the following conditions
736285Sbrian * are met:
836285Sbrian * 1. Redistributions of source code must retain the above copyright
936285Sbrian *    notice, this list of conditions and the following disclaimer.
1036285Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1136285Sbrian *    notice, this list of conditions and the following disclaimer in the
1236285Sbrian *    documentation and/or other materials provided with the distribution.
136059Samurai *
1436285Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1536285Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1636285Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1736285Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1836285Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1936285Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2036285Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2136285Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2236285Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2336285Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2436285Sbrian * SUCH DAMAGE.
256059Samurai *
2650479Speter * $FreeBSD: releng/11.0/usr.sbin/ppp/chat.c 261900 2014-02-14 20:34:24Z brueffer $
276059Samurai */
2836285Sbrian
2943313Sbrian#include <sys/param.h>
3030715Sbrian#include <netinet/in.h>
3136285Sbrian#include <netinet/in_systm.h>
3236285Sbrian#include <netinet/ip.h>
3381634Sbrian#include <sys/socket.h>
3436285Sbrian#include <sys/un.h>
3530715Sbrian
3630715Sbrian#include <errno.h>
376059Samurai#include <fcntl.h>
3836285Sbrian#include <paths.h>
3930715Sbrian#include <stdio.h>
4030715Sbrian#include <stdlib.h>
4130715Sbrian#include <string.h>
426735Samurai#include <sys/wait.h>
4330715Sbrian#include <termios.h>
4430715Sbrian#include <unistd.h>
4530715Sbrian
4646686Sbrian#include "layer.h"
4730715Sbrian#include "mbuf.h"
4830715Sbrian#include "log.h"
4930715Sbrian#include "defs.h"
5030715Sbrian#include "timer.h"
5136285Sbrian#include "lqr.h"
5236285Sbrian#include "hdlc.h"
5336285Sbrian#include "throughput.h"
5436285Sbrian#include "fsm.h"
5536285Sbrian#include "lcp.h"
5636285Sbrian#include "ccp.h"
5736285Sbrian#include "link.h"
5836285Sbrian#include "async.h"
5936285Sbrian#include "descriptor.h"
6036285Sbrian#include "physical.h"
6125630Sbrian#include "chat.h"
6236285Sbrian#include "mp.h"
6336285Sbrian#include "auth.h"
6436285Sbrian#include "chap.h"
6536285Sbrian#include "slcompress.h"
6636285Sbrian#include "iplist.h"
6781634Sbrian#include "ncpaddr.h"
6836285Sbrian#include "ipcp.h"
6936285Sbrian#include "filter.h"
7038174Sbrian#include "cbcp.h"
7143919Sbrian#include "command.h"
7236285Sbrian#include "datalink.h"
7343313Sbrian#ifndef NORADIUS
7443313Sbrian#include "radius.h"
7543313Sbrian#endif
7681634Sbrian#include "ipv6cp.h"
7781634Sbrian#include "ncp.h"
7836285Sbrian#include "bundle.h"
7955253Sbrian#include "id.h"
806059Samurai
8136285Sbrian#define BUFLEFT(c) (sizeof (c)->buf - ((c)->bufend - (c)->buf))
8230715Sbrian
8336285Sbrianstatic void ExecStr(struct physical *, char *, char *, int);
8436285Sbrianstatic char *ExpandString(struct chat *, const char *, char *, int, int);
8530715Sbrian
8636285Sbrianstatic void
8736285Sbrianchat_PauseTimer(void *v)
8836285Sbrian{
8936285Sbrian  struct chat *c = (struct chat *)v;
9036285Sbrian  timer_Stop(&c->pause);
9136285Sbrian  c->pause.load = 0;
9236285Sbrian}
936735Samurai
9436285Sbrianstatic void
9536285Sbrianchat_Pause(struct chat *c, u_long load)
9636285Sbrian{
9736285Sbrian  timer_Stop(&c->pause);
9836285Sbrian  c->pause.load += load;
9936285Sbrian  c->pause.func = chat_PauseTimer;
10036285Sbrian  c->pause.name = "chat pause";
10136285Sbrian  c->pause.arg = c;
10236285Sbrian  timer_Start(&c->pause);
10336285Sbrian}
1046059Samurai
10536285Sbrianstatic void
10636285Sbrianchat_TimeoutTimer(void *v)
10736285Sbrian{
10836285Sbrian  struct chat *c = (struct chat *)v;
10936285Sbrian  timer_Stop(&c->timeout);
11036285Sbrian  c->TimedOut = 1;
11136285Sbrian}
1126059Samurai
11336285Sbrianstatic void
11436285Sbrianchat_SetTimeout(struct chat *c)
11536285Sbrian{
11636285Sbrian  timer_Stop(&c->timeout);
11736285Sbrian  if (c->TimeoutSec > 0) {
11836285Sbrian    c->timeout.load = SECTICKS * c->TimeoutSec;
11936285Sbrian    c->timeout.func = chat_TimeoutTimer;
12036285Sbrian    c->timeout.name = "chat timeout";
12136285Sbrian    c->timeout.arg = c;
12236285Sbrian    timer_Start(&c->timeout);
12336285Sbrian  }
12436285Sbrian}
12536285Sbrian
1266059Samuraistatic char *
12736285Sbrianchat_NextChar(char *ptr, char ch)
12836285Sbrian{
12936285Sbrian  for (; *ptr; ptr++)
13036285Sbrian    if (*ptr == ch)
13136285Sbrian      return ptr;
13236285Sbrian    else if (*ptr == '\\')
13336285Sbrian      if (*++ptr == '\0')
13436285Sbrian        return NULL;
13536285Sbrian
13636285Sbrian  return NULL;
13736285Sbrian}
13836285Sbrian
13936285Sbrianstatic int
14058028Sbrianchat_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
14136285Sbrian{
14236285Sbrian  struct chat *c = descriptor2chat(d);
14336285Sbrian  int special, gotabort, gottimeout, needcr;
14436285Sbrian  int TimedOut = c->TimedOut;
14537010Sbrian  static char arg_term;		/* An empty string */
14636285Sbrian
14736285Sbrian  if (c->pause.state == TIMER_RUNNING)
14836285Sbrian    return 0;
14936285Sbrian
15036285Sbrian  if (TimedOut) {
15136285Sbrian    log_Printf(LogCHAT, "Expect timeout\n");
15236285Sbrian    if (c->nargptr == NULL)
15336285Sbrian      c->state = CHAT_FAILED;
15436285Sbrian    else {
15536285Sbrian      /* c->state = CHAT_EXPECT; */
15636285Sbrian      c->argptr = &arg_term;
157261900Sbrueffer      /*
158261900Sbrueffer	We have to clear the input buffer, because it contains output
159261900Sbrueffer	from the previous (timed out) command.
160261900Sbrueffer      */
161261900Sbrueffer      c->bufstart = c->bufend;
16236285Sbrian    }
16336285Sbrian    c->TimedOut = 0;
16436285Sbrian  }
16536285Sbrian
16636285Sbrian  if (c->state != CHAT_EXPECT && c->state != CHAT_SEND)
16736285Sbrian    return 0;
16836285Sbrian
16936285Sbrian  gottimeout = gotabort = 0;
17036285Sbrian
17136285Sbrian  if (c->arg < c->argc && (c->arg < 0 || *c->argptr == '\0')) {
17236285Sbrian    /* Go get the next string */
17336285Sbrian    if (c->arg < 0 || c->state == CHAT_SEND)
17436285Sbrian      c->state = CHAT_EXPECT;
17536285Sbrian    else
17636285Sbrian      c->state = CHAT_SEND;
17736285Sbrian
17836285Sbrian    special = 1;
17936285Sbrian    while (special && (c->nargptr || c->arg < c->argc - 1)) {
18036285Sbrian      if (c->arg < 0 || (!TimedOut && c->state == CHAT_SEND))
18136285Sbrian        c->nargptr = NULL;
18236285Sbrian
18336285Sbrian      if (c->nargptr != NULL) {
18436285Sbrian        /* We're doing expect-send-expect.... */
18536285Sbrian        c->argptr = c->nargptr;
18636285Sbrian        /* Put the '-' back in case we ever want to rerun our script */
18736285Sbrian        c->nargptr[-1] = '-';
18836285Sbrian        c->nargptr = chat_NextChar(c->nargptr, '-');
18936285Sbrian        if (c->nargptr != NULL)
19036285Sbrian          *c->nargptr++ = '\0';
19136285Sbrian      } else {
19236285Sbrian        int minus;
19336285Sbrian
19448226Sbrian        if ((c->argptr = c->argv[++c->arg]) == NULL) {
19548226Sbrian          /* End of script - all ok */
19648226Sbrian          c->state = CHAT_DONE;
19748226Sbrian          return 0;
19848226Sbrian        }
19936285Sbrian
20036285Sbrian        if (c->state == CHAT_EXPECT) {
20136285Sbrian          /* Look for expect-send-expect sequence */
20236285Sbrian          c->nargptr = c->argptr;
20336285Sbrian          minus = 0;
20436285Sbrian          while ((c->nargptr = chat_NextChar(c->nargptr, '-'))) {
20536285Sbrian            c->nargptr++;
20636285Sbrian            minus++;
20736285Sbrian          }
20836285Sbrian
20936285Sbrian          if (minus % 2)
21036285Sbrian            log_Printf(LogWARN, "chat_UpdateSet: \"%s\": Uneven number of"
21136285Sbrian                      " '-' chars, all ignored\n", c->argptr);
21236285Sbrian          else if (minus) {
21336285Sbrian            c->nargptr = chat_NextChar(c->argptr, '-');
21436285Sbrian            *c->nargptr++ = '\0';
21536285Sbrian          }
21636285Sbrian        }
21736285Sbrian      }
21836285Sbrian
21936285Sbrian      /*
22036285Sbrian       * c->argptr now temporarily points into c->script (via c->argv)
22136285Sbrian       * If it's an expect-send-expect sequence, we've just got the correct
22236285Sbrian       * portion of that sequence.
22336285Sbrian       */
22436285Sbrian
22564465Sbrian      needcr = c->state == CHAT_SEND &&
22664465Sbrian               (*c->argptr != '!' || c->argptr[1] == '!');
22736285Sbrian
22836285Sbrian      /* We leave room for a potential HDLC header in the target string */
22936285Sbrian      ExpandString(c, c->argptr, c->exp + 2, sizeof c->exp - 2, needcr);
23036285Sbrian
23147540Sbrian      /*
23247540Sbrian       * Now read our string.  If it's not a special string, we unset
23347540Sbrian       * ``special'' to break out of the loop.
23447540Sbrian       */
23536285Sbrian      if (gotabort) {
23636285Sbrian        if (c->abort.num < MAXABORTS) {
23736285Sbrian          int len, i;
23836285Sbrian
23936285Sbrian          len = strlen(c->exp+2);
24036285Sbrian          for (i = 0; i < c->abort.num; i++)
24136285Sbrian            if (len > c->abort.string[i].len) {
24236285Sbrian              int last;
24336285Sbrian
24436285Sbrian              for (last = c->abort.num; last > i; last--) {
24536285Sbrian                c->abort.string[last].data = c->abort.string[last-1].data;
24636285Sbrian                c->abort.string[last].len = c->abort.string[last-1].len;
24736285Sbrian              }
24836285Sbrian              break;
24936285Sbrian            }
25036285Sbrian          c->abort.string[i].len = len;
251136375Sbrian          if ((c->abort.string[i].data = (char *)malloc(len+1)) != NULL) {
252136375Sbrian            memcpy(c->abort.string[i].data, c->exp+2, len+1);
253136375Sbrian            c->abort.num++;
254136375Sbrian	  }
25536285Sbrian        } else
25636285Sbrian          log_Printf(LogERROR, "chat_UpdateSet: too many abort strings\n");
25736285Sbrian        gotabort = 0;
25836285Sbrian      } else if (gottimeout) {
25936285Sbrian        c->TimeoutSec = atoi(c->exp + 2);
26036285Sbrian        if (c->TimeoutSec <= 0)
26136285Sbrian          c->TimeoutSec = 30;
26236285Sbrian        gottimeout = 0;
26336285Sbrian      } else if (c->nargptr == NULL && !strcmp(c->exp+2, "ABORT"))
26436285Sbrian        gotabort = 1;
26536285Sbrian      else if (c->nargptr == NULL && !strcmp(c->exp+2, "TIMEOUT"))
26636285Sbrian        gottimeout = 1;
26736285Sbrian      else {
26864465Sbrian        if (c->exp[2] == '!' && c->exp[3] != '!')
26964465Sbrian          ExecStr(c->physical, c->exp + 3, c->exp + 3, sizeof c->exp - 3);
27036285Sbrian
27136285Sbrian        if (c->exp[2] == '\0') {
27236285Sbrian          /* Empty string, reparse (this may be better as a `goto start') */
27336285Sbrian          c->argptr = &arg_term;
27436285Sbrian          return chat_UpdateSet(d, r, w, e, n);
27536285Sbrian        }
27636285Sbrian
27736285Sbrian        special = 0;
27836285Sbrian      }
27936285Sbrian    }
28036285Sbrian
28136285Sbrian    if (special) {
28236285Sbrian      if (gottimeout)
28336285Sbrian        log_Printf(LogWARN, "chat_UpdateSet: TIMEOUT: Argument expected\n");
28436285Sbrian      else if (gotabort)
28536285Sbrian        log_Printf(LogWARN, "chat_UpdateSet: ABORT: Argument expected\n");
28636285Sbrian
28736285Sbrian      /* End of script - all ok */
28836285Sbrian      c->state = CHAT_DONE;
28936285Sbrian      return 0;
29036285Sbrian    }
29136285Sbrian
29236285Sbrian    /* set c->argptr to point in the right place */
29364465Sbrian    c->argptr = c->exp + (c->exp[2] == '!' ? 3 : 2);
29436285Sbrian    c->arglen = strlen(c->argptr);
29536285Sbrian
29636285Sbrian    if (c->state == CHAT_EXPECT) {
29736285Sbrian      /* We must check to see if the string's already been found ! */
29836285Sbrian      char *begin, *end;
29936285Sbrian
30036285Sbrian      end = c->bufend - c->arglen + 1;
30136285Sbrian      if (end < c->bufstart)
30236285Sbrian        end = c->bufstart;
30336285Sbrian      for (begin = c->bufstart; begin < end; begin++)
30436285Sbrian        if (!strncmp(begin, c->argptr, c->arglen)) {
30536285Sbrian          c->bufstart = begin + c->arglen;
30636285Sbrian          c->argptr += c->arglen;
30736285Sbrian          c->arglen = 0;
30836285Sbrian          /* Continue - we've already read our expect string */
30936285Sbrian          return chat_UpdateSet(d, r, w, e, n);
31036285Sbrian        }
31136285Sbrian
31236285Sbrian      log_Printf(LogCHAT, "Expect(%d): %s\n", c->TimeoutSec, c->argptr);
31336285Sbrian      chat_SetTimeout(c);
31436285Sbrian    }
31536285Sbrian  }
31636285Sbrian
31736285Sbrian  /*
31836285Sbrian   * We now have c->argptr pointing at what we want to expect/send and
31936285Sbrian   * c->state saying what we want to do... we now know what to put in
32036285Sbrian   * the fd_set :-)
32136285Sbrian   */
32236285Sbrian
32336285Sbrian  if (c->state == CHAT_EXPECT)
32446686Sbrian    return physical_doUpdateSet(&c->physical->desc, r, NULL, e, n, 1);
32536285Sbrian  else
32646686Sbrian    return physical_doUpdateSet(&c->physical->desc, NULL, w, e, n, 1);
32736285Sbrian}
32836285Sbrian
32936285Sbrianstatic int
33058028Sbrianchat_IsSet(struct fdescriptor *d, const fd_set *fdset)
33136285Sbrian{
33236285Sbrian  struct chat *c = descriptor2chat(d);
33348227Sbrian  return c->argptr && physical_IsSet(&c->physical->desc, fdset);
33436285Sbrian}
33536285Sbrian
33636285Sbrianstatic void
33736285Sbrianchat_UpdateLog(struct chat *c, int in)
33836285Sbrian{
33936285Sbrian  if (log_IsKept(LogCHAT) || log_IsKept(LogCONNECT)) {
34036285Sbrian    /*
34136285Sbrian     * If a linefeed appears in the last `in' characters of `c's input
34236285Sbrian     * buffer, output from there, all the way back to the last linefeed.
34336285Sbrian     * This is called for every read of `in' bytes.
34436285Sbrian     */
34536285Sbrian    char *ptr, *end, *stop, ch;
34636285Sbrian    int level;
34736285Sbrian
34836285Sbrian    level = log_IsKept(LogCHAT) ? LogCHAT : LogCONNECT;
34936285Sbrian    if (in == -1)
35036285Sbrian      end = ptr = c->bufend;
35136285Sbrian    else {
35236285Sbrian      ptr = c->bufend - in;
35336285Sbrian      for (end = c->bufend - 1; end >= ptr; end--)
35436285Sbrian        if (*end == '\n')
35536285Sbrian          break;
35636285Sbrian    }
35736285Sbrian
35836285Sbrian    if (end >= ptr) {
35936285Sbrian      for (ptr = c->bufend - (in == -1 ? 1 : in + 1); ptr >= c->bufstart; ptr--)
36036285Sbrian        if (*ptr == '\n')
36136285Sbrian          break;
36236285Sbrian      ptr++;
36336285Sbrian      stop = NULL;
36436285Sbrian      while (stop < end) {
36536285Sbrian        if ((stop = memchr(ptr, '\n', end - ptr)) == NULL)
36636285Sbrian          stop = end;
36736285Sbrian        ch = *stop;
36836285Sbrian        *stop = '\0';
36936285Sbrian        if (level == LogCHAT || strstr(ptr, "CONNECT"))
37036285Sbrian          log_Printf(level, "Received: %s\n", ptr);
37136285Sbrian        *stop = ch;
37236285Sbrian        ptr = stop + 1;
37336285Sbrian      }
37436285Sbrian    }
37536285Sbrian  }
37636285Sbrian}
37736285Sbrian
37836285Sbrianstatic void
379134789Sbrianchat_Read(struct fdescriptor *d, struct bundle *bundle __unused,
380134789Sbrian	  const fd_set *fdset __unused)
38136285Sbrian{
38236285Sbrian  struct chat *c = descriptor2chat(d);
38336285Sbrian
38436285Sbrian  if (c->state == CHAT_EXPECT) {
38536285Sbrian    ssize_t in;
38636285Sbrian    char *abegin, *ebegin, *begin, *aend, *eend, *end;
38736285Sbrian    int n;
38836285Sbrian
38936285Sbrian    /*
39036285Sbrian     * XXX - should this read only 1 byte to guarantee that we don't
39136285Sbrian     * swallow any ppp talk from the peer ?
39236285Sbrian     */
39336285Sbrian    in = BUFLEFT(c);
394134789Sbrian    if (in > (ssize_t)sizeof c->buf / 2)
39536285Sbrian      in = sizeof c->buf / 2;
39636285Sbrian
39736285Sbrian    in = physical_Read(c->physical, c->bufend, in);
39836285Sbrian    if (in <= 0)
39936285Sbrian      return;
40036285Sbrian
40136285Sbrian    /* `begin' and `end' delimit where we're going to strncmp() from */
40236285Sbrian    ebegin = c->bufend - c->arglen + 1;
40336285Sbrian    eend = ebegin + in;
40436285Sbrian    if (ebegin < c->bufstart)
40536285Sbrian      ebegin = c->bufstart;
40636285Sbrian
40736285Sbrian    if (c->abort.num) {
40836285Sbrian      abegin = c->bufend - c->abort.string[0].len + 1;
40936285Sbrian      aend = c->bufend - c->abort.string[c->abort.num-1].len + in + 1;
41036285Sbrian      if (abegin < c->bufstart)
41136285Sbrian        abegin = c->bufstart;
41236285Sbrian    } else {
41336285Sbrian      abegin = ebegin;
41436285Sbrian      aend = eend;
41536285Sbrian    }
41636285Sbrian    begin = abegin < ebegin ? abegin : ebegin;
41736285Sbrian    end = aend < eend ? eend : aend;
41836285Sbrian
41936285Sbrian    c->bufend += in;
42036285Sbrian
42136285Sbrian    chat_UpdateLog(c, in);
42236285Sbrian
42336285Sbrian    if (c->bufend > c->buf + sizeof c->buf / 2) {
42436285Sbrian      /* Shuffle our receive buffer back a bit */
42536285Sbrian      int chop;
42636285Sbrian
42736285Sbrian      for (chop = begin - c->buf; chop; chop--)
42836285Sbrian        if (c->buf[chop] == '\n')
42936285Sbrian          /* found some already-logged garbage to remove :-) */
43036285Sbrian          break;
43136285Sbrian
43236285Sbrian      if (!chop)
43336285Sbrian        chop = begin - c->buf;
43436285Sbrian
43536285Sbrian      if (chop) {
43636285Sbrian        char *from, *to;
43736285Sbrian
43836285Sbrian        to = c->buf;
43936285Sbrian        from = to + chop;
44036285Sbrian        while (from < c->bufend)
44136285Sbrian          *to++ = *from++;
44236285Sbrian        c->bufstart -= chop;
44336285Sbrian        c->bufend -= chop;
44436285Sbrian        begin -= chop;
44536285Sbrian        end -= chop;
44636285Sbrian        abegin -= chop;
44736285Sbrian        aend -= chop;
44836285Sbrian        ebegin -= chop;
44936285Sbrian        eend -= chop;
45036285Sbrian      }
45136285Sbrian    }
45236285Sbrian
45336285Sbrian    for (; begin < end; begin++)
45436285Sbrian      if (begin >= ebegin && begin < eend &&
45536285Sbrian          !strncmp(begin, c->argptr, c->arglen)) {
45636285Sbrian        /* Got it ! */
45740680Sbrian        timer_Stop(&c->timeout);
45836285Sbrian        if (memchr(begin + c->arglen - 1, '\n',
45998243Sbrian            c->bufend - begin - c->arglen + 1) == NULL) {
46036285Sbrian          /* force it into the log */
46136285Sbrian          end = c->bufend;
46236285Sbrian          c->bufend = begin + c->arglen;
46336285Sbrian          chat_UpdateLog(c, -1);
46436285Sbrian          c->bufend = end;
46536285Sbrian        }
46636285Sbrian        c->bufstart = begin + c->arglen;
46736285Sbrian        c->argptr += c->arglen;
46836285Sbrian        c->arglen = 0;
46936285Sbrian        break;
47036285Sbrian      } else if (begin >= abegin && begin < aend) {
47136285Sbrian        for (n = c->abort.num - 1; n >= 0; n--) {
47236285Sbrian          if (begin + c->abort.string[n].len > c->bufend)
47336285Sbrian            break;
47436285Sbrian          if (!strncmp(begin, c->abort.string[n].data,
47536285Sbrian                       c->abort.string[n].len)) {
47636285Sbrian            if (memchr(begin + c->abort.string[n].len - 1, '\n',
47798243Sbrian                c->bufend - begin - c->abort.string[n].len + 1) == NULL) {
47836285Sbrian              /* force it into the log */
47936285Sbrian              end = c->bufend;
48036285Sbrian              c->bufend = begin + c->abort.string[n].len;
48136285Sbrian              chat_UpdateLog(c, -1);
48236285Sbrian              c->bufend = end;
48336285Sbrian            }
48436285Sbrian            c->bufstart = begin + c->abort.string[n].len;
48536285Sbrian            c->state = CHAT_FAILED;
48636285Sbrian            return;
48736285Sbrian          }
48836285Sbrian        }
48936285Sbrian      }
49036285Sbrian  }
49136285Sbrian}
49236285Sbrian
49337141Sbrianstatic int
494134789Sbrianchat_Write(struct fdescriptor *d, struct bundle *bundle __unused,
495134789Sbrian	   const fd_set *fdset __unused)
49636285Sbrian{
49736285Sbrian  struct chat *c = descriptor2chat(d);
49837141Sbrian  int result = 0;
49936285Sbrian
50036285Sbrian  if (c->state == CHAT_SEND) {
50136285Sbrian    int wrote;
50236285Sbrian
50336285Sbrian    if (strstr(c->argv[c->arg], "\\P"))            /* Don't log the password */
50436285Sbrian      log_Printf(LogCHAT, "Send: %s\n", c->argv[c->arg]);
50536285Sbrian    else {
50636285Sbrian      int sz;
50736285Sbrian
50836285Sbrian      sz = c->arglen - 1;
50936285Sbrian      while (sz >= 0 && c->argptr[sz] == '\n')
51036285Sbrian        sz--;
51136285Sbrian      log_Printf(LogCHAT, "Send: %.*s\n", sz + 1, c->argptr);
51236285Sbrian    }
51336285Sbrian
51436285Sbrian    if (physical_IsSync(c->physical)) {
51547540Sbrian      /*
51647540Sbrian       * XXX: Fix me
51747540Sbrian       * This data should be stuffed down through the link layers
51847540Sbrian       */
51936285Sbrian      /* There's always room for the HDLC header */
52036285Sbrian      c->argptr -= 2;
52136285Sbrian      c->arglen += 2;
52236285Sbrian      memcpy(c->argptr, "\377\003", 2);	/* Prepend HDLC header */
52336285Sbrian    }
52436285Sbrian
52536285Sbrian    wrote = physical_Write(c->physical, c->argptr, c->arglen);
52693418Sbrian    result = wrote > 0 ? 1 : 0;
52736285Sbrian    if (wrote == -1) {
52893418Sbrian      if (errno != EINTR) {
52993418Sbrian        log_Printf(LogWARN, "chat_Write: %s\n", strerror(errno));
53093418Sbrian	result = -1;
53193418Sbrian      }
53236285Sbrian      if (physical_IsSync(c->physical)) {
53336285Sbrian        c->argptr += 2;
53436285Sbrian        c->arglen -= 2;
53536285Sbrian      }
53636285Sbrian    } else if (wrote < 2 && physical_IsSync(c->physical)) {
53736285Sbrian      /* Oops - didn't even write our HDLC header ! */
53836285Sbrian      c->argptr += 2;
53936285Sbrian      c->arglen -= 2;
54036285Sbrian    } else {
54136285Sbrian      c->argptr += wrote;
54236285Sbrian      c->arglen -= wrote;
54336285Sbrian    }
54436285Sbrian  }
54537141Sbrian
54637141Sbrian  return result;
54736285Sbrian}
54836285Sbrian
54936285Sbrianvoid
55054055Sbrianchat_Init(struct chat *c, struct physical *p)
55136285Sbrian{
55236285Sbrian  c->desc.type = CHAT_DESCRIPTOR;
55336285Sbrian  c->desc.UpdateSet = chat_UpdateSet;
55436285Sbrian  c->desc.IsSet = chat_IsSet;
55536285Sbrian  c->desc.Read = chat_Read;
55636285Sbrian  c->desc.Write = chat_Write;
55736285Sbrian  c->physical = p;
55854055Sbrian  *c->script = '\0';
55954055Sbrian  c->argc = 0;
56054055Sbrian  c->arg = -1;
56154055Sbrian  c->argptr = NULL;
56254055Sbrian  c->nargptr = NULL;
56354055Sbrian  c->bufstart = c->bufend = c->buf;
56436285Sbrian
56554055Sbrian  memset(&c->pause, '\0', sizeof c->pause);
56654055Sbrian  memset(&c->timeout, '\0', sizeof c->timeout);
56754055Sbrian}
56854055Sbrian
56954914Sbrianint
57054055Sbrianchat_Setup(struct chat *c, const char *data, const char *phone)
57154055Sbrian{
57236285Sbrian  c->state = CHAT_EXPECT;
57336285Sbrian
57436285Sbrian  if (data == NULL) {
57536285Sbrian    *c->script = '\0';
57636285Sbrian    c->argc = 0;
57736285Sbrian  } else {
57836285Sbrian    strncpy(c->script, data, sizeof c->script - 1);
57936285Sbrian    c->script[sizeof c->script - 1] = '\0';
58055145Sbrian    c->argc = MakeArgs(c->script, c->argv, VECSIZE(c->argv), PARSE_NOHASH);
58136285Sbrian  }
58236285Sbrian
58336285Sbrian  c->arg = -1;
58436285Sbrian  c->argptr = NULL;
58536285Sbrian  c->nargptr = NULL;
58636285Sbrian
58736285Sbrian  c->TimeoutSec = 30;
58836285Sbrian  c->TimedOut = 0;
58936285Sbrian  c->phone = phone;
59036285Sbrian  c->abort.num = 0;
59136285Sbrian
59254055Sbrian  timer_Stop(&c->pause);
59354055Sbrian  timer_Stop(&c->timeout);
59454914Sbrian
59554914Sbrian  return c->argc >= 0;
59636285Sbrian}
59736285Sbrian
59836285Sbrianvoid
59954055Sbrianchat_Finish(struct chat *c)
60036285Sbrian{
60136285Sbrian  timer_Stop(&c->pause);
60236285Sbrian  timer_Stop(&c->timeout);
60336285Sbrian  while (c->abort.num)
60436285Sbrian    free(c->abort.string[--c->abort.num].data);
60536285Sbrian  c->abort.num = 0;
60636285Sbrian}
60736285Sbrian
60854055Sbrianvoid
60954055Sbrianchat_Destroy(struct chat *c)
61054055Sbrian{
61154055Sbrian  chat_Finish(c);
61254055Sbrian}
61354055Sbrian
6146059Samurai/*
61521488Simp *  \c	don't add a cr
61621488Simp *  \d  Sleep a little (delay 2 seconds
61721488Simp *  \n  Line feed character
61821488Simp *  \P  Auth Key password
61921488Simp *  \p  pause 0.25 sec
6206059Samurai *  \r	Carrige return character
6216059Samurai *  \s  Space character
62214418Sache *  \T  Telephone number(s) (defined via `set phone')
6236059Samurai *  \t  Tab character
62421488Simp *  \U  Auth User
6256059Samurai */
62636285Sbrianstatic char *
62747540SbrianExpandString(struct chat *c, const char *str, char *result, int reslen, int cr)
6286059Samurai{
62947540Sbrian  int len;
6306059Samurai
63121488Simp  result[--reslen] = '\0';
63221488Simp  while (*str && reslen > 0) {
6336059Samurai    switch (*str) {
6346059Samurai    case '\\':
6356059Samurai      str++;
6366059Samurai      switch (*str) {
6376059Samurai      case 'c':
63847540Sbrian	cr = 0;
6396059Samurai	break;
6406059Samurai      case 'd':		/* Delay 2 seconds */
64136285Sbrian        chat_Pause(c, 2 * SECTICKS);
64228679Sbrian	break;
6436059Samurai      case 'p':
64436285Sbrian        chat_Pause(c, SECTICKS / 4);
64547540Sbrian	break;		/* Delay 0.25 seconds */
6466059Samurai      case 'n':
64728679Sbrian	*result++ = '\n';
64828679Sbrian	reslen--;
64928679Sbrian	break;
6506059Samurai      case 'r':
65128679Sbrian	*result++ = '\r';
65228679Sbrian	reslen--;
65328679Sbrian	break;
6546059Samurai      case 's':
65528679Sbrian	*result++ = ' ';
65628679Sbrian	reslen--;
65728679Sbrian	break;
6586059Samurai      case 't':
65928679Sbrian	*result++ = '\t';
66028679Sbrian	reslen--;
66128679Sbrian	break;
6626059Samurai      case 'P':
66336285Sbrian	strncpy(result, c->physical->dl->bundle->cfg.auth.key, reslen);
66447540Sbrian        len = strlen(result);
66547540Sbrian	reslen -= len;
66647540Sbrian	result += len;
6676059Samurai	break;
6686059Samurai      case 'T':
66938174Sbrian        if (c->phone) {
67038174Sbrian          strncpy(result, c->phone, reslen);
67147540Sbrian          len = strlen(result);
67247540Sbrian          reslen -= len;
67347540Sbrian          result += len;
67438174Sbrian        }
6756059Samurai	break;
6766059Samurai      case 'U':
67736285Sbrian	strncpy(result, c->physical->dl->bundle->cfg.auth.name, reslen);
67847540Sbrian        len = strlen(result);
67947540Sbrian	reslen -= len;
68047540Sbrian	result += len;
6816059Samurai	break;
6826059Samurai      default:
68321488Simp	reslen--;
68428679Sbrian	*result++ = *str;
68521488Simp	break;
6866059Samurai      }
68728679Sbrian      if (*str)
68828679Sbrian	str++;
6896059Samurai      break;
6906059Samurai    case '^':
6916059Samurai      str++;
69221488Simp      if (*str) {
6936059Samurai	*result++ = *str++ & 0x1f;
69421488Simp	reslen--;
69521488Simp      }
6966059Samurai      break;
6976059Samurai    default:
6986059Samurai      *result++ = *str++;
69921488Simp      reslen--;
7006059Samurai      break;
7016059Samurai    }
7026059Samurai  }
70321488Simp  if (--reslen > 0) {
70447540Sbrian    if (cr)
70521488Simp      *result++ = '\r';
70621488Simp  }
70721488Simp  if (--reslen > 0)
70821488Simp    *result++ = '\0';
70928679Sbrian  return (result);
7106059Samurai}
7116059Samurai
71228679Sbrianstatic void
71336285SbrianExecStr(struct physical *physical, char *command, char *out, int olen)
71426858Sbrian{
71532017Sbrian  pid_t pid;
71632017Sbrian  int fids[2];
71743919Sbrian  char *argv[MAXARGS], *vector[MAXARGS], *startout, *endout;
71849976Sbrian  int stat, nb, argc, i;
71932017Sbrian
72036285Sbrian  log_Printf(LogCHAT, "Exec: %s\n", command);
72155145Sbrian  if ((argc = MakeArgs(command, vector, VECSIZE(vector),
72255145Sbrian                       PARSE_REDUCE|PARSE_NOHASH)) <= 0) {
72354914Sbrian    if (argc < 0)
72454914Sbrian      log_Printf(LogWARN, "Syntax error in exec command\n");
72554914Sbrian    *out = '\0';
72654914Sbrian    return;
72754914Sbrian  }
72832017Sbrian
72932017Sbrian  if (pipe(fids) < 0) {
73036285Sbrian    log_Printf(LogCHAT, "Unable to create pipe in ExecStr: %s\n",
73132017Sbrian	      strerror(errno));
73236285Sbrian    *out = '\0';
73336285Sbrian    return;
73432017Sbrian  }
73532017Sbrian  if ((pid = fork()) == 0) {
73685991Sbrian    command_Expand(argv, argc, (char const *const *)vector,
73785991Sbrian                   physical->dl->bundle, 0, getpid());
73836452Sbrian    close(fids[0]);
73936285Sbrian    timer_TermService();
74049976Sbrian    if (fids[1] == STDIN_FILENO)
74149976Sbrian      fids[1] = dup(fids[1]);
74246686Sbrian    dup2(physical->fd, STDIN_FILENO);
74349976Sbrian    dup2(fids[1], STDERR_FILENO);
74436285Sbrian    dup2(STDIN_FILENO, STDOUT_FILENO);
74536285Sbrian    close(3);
74649976Sbrian    if (open(_PATH_TTY, O_RDWR) != 3)
74749976Sbrian      open(_PATH_DEVNULL, O_RDWR);	/* Leave it closed if it fails... */
74849976Sbrian    for (i = getdtablesize(); i > 3; i--)
74949976Sbrian      fcntl(i, F_SETFD, 1);
75064802Sbrian#ifndef NOSUID
75155252Sbrian    setuid(ID0realuid());
75264802Sbrian#endif
75343919Sbrian    execvp(argv[0], argv);
75443919Sbrian    fprintf(stderr, "execvp: %s: %s\n", argv[0], strerror(errno));
75549976Sbrian    _exit(127);
75632017Sbrian  } else {
75732017Sbrian    char *name = strdup(vector[0]);
75832017Sbrian
75932017Sbrian    close(fids[1]);
76032017Sbrian    endout = out + olen - 1;
76132017Sbrian    startout = out;
76232017Sbrian    while (out < endout) {
76332017Sbrian      nb = read(fids[0], out, 1);
76432017Sbrian      if (nb <= 0)
76532017Sbrian	break;
76632017Sbrian      out++;
76732017Sbrian    }
76832017Sbrian    *out = '\0';
76932017Sbrian    close(fids[0]);
77032017Sbrian    close(fids[1]);
77132017Sbrian    waitpid(pid, &stat, WNOHANG);
77232017Sbrian    if (WIFSIGNALED(stat)) {
77336285Sbrian      log_Printf(LogWARN, "%s: signal %d\n", name, WTERMSIG(stat));
77432017Sbrian      free(name);
77536285Sbrian      *out = '\0';
77636285Sbrian      return;
77732017Sbrian    } else if (WIFEXITED(stat)) {
77832017Sbrian      switch (WEXITSTATUS(stat)) {
77932017Sbrian        case 0:
78032017Sbrian          free(name);
78132017Sbrian          break;
78232017Sbrian        case 127:
78336285Sbrian          log_Printf(LogWARN, "%s: %s\n", name, startout);
78432017Sbrian          free(name);
78536285Sbrian          *out = '\0';
78636285Sbrian          return;
78732017Sbrian          break;
78832017Sbrian        default:
78936285Sbrian          log_Printf(LogWARN, "%s: exit %d\n", name, WEXITSTATUS(stat));
79032017Sbrian          free(name);
79136285Sbrian          *out = '\0';
79236285Sbrian          return;
79332017Sbrian          break;
79432017Sbrian      }
79532017Sbrian    } else {
79636285Sbrian      log_Printf(LogWARN, "%s: Unexpected exit result\n", name);
79732017Sbrian      free(name);
79836285Sbrian      *out = '\0';
79936285Sbrian      return;
80032017Sbrian    }
80132017Sbrian  }
80232017Sbrian}
803