datalink.c revision 37012
136285Sbrian/*-
236285Sbrian * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
336285Sbrian * All rights reserved.
436285Sbrian *
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.
1336285Sbrian *
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.
2536285Sbrian *
2637012Sbrian *	$Id: datalink.c,v 1.9 1998/06/15 19:05:15 brian Exp $
2736285Sbrian */
2836285Sbrian
2936285Sbrian#include <sys/types.h>
3036285Sbrian#include <netinet/in.h>
3136285Sbrian#include <netinet/in_systm.h>
3236285Sbrian#include <netinet/ip.h>
3336285Sbrian#include <sys/un.h>
3436285Sbrian
3536285Sbrian#include <ctype.h>
3636285Sbrian#include <stdio.h>
3736285Sbrian#include <stdlib.h>
3836285Sbrian#include <string.h>
3936285Sbrian#include <sys/uio.h>
4036285Sbrian#include <termios.h>
4136285Sbrian#include <unistd.h>
4236285Sbrian
4336285Sbrian#include "mbuf.h"
4436285Sbrian#include "log.h"
4536285Sbrian#include "defs.h"
4636285Sbrian#include "timer.h"
4736285Sbrian#include "fsm.h"
4836285Sbrian#include "lcp.h"
4936285Sbrian#include "descriptor.h"
5036285Sbrian#include "lqr.h"
5136285Sbrian#include "hdlc.h"
5236285Sbrian#include "async.h"
5336285Sbrian#include "throughput.h"
5436285Sbrian#include "ccp.h"
5536285Sbrian#include "link.h"
5636285Sbrian#include "physical.h"
5736285Sbrian#include "iplist.h"
5836285Sbrian#include "slcompress.h"
5936285Sbrian#include "ipcp.h"
6036285Sbrian#include "filter.h"
6136285Sbrian#include "mp.h"
6236285Sbrian#include "bundle.h"
6336285Sbrian#include "chat.h"
6436285Sbrian#include "auth.h"
6536285Sbrian#include "modem.h"
6636285Sbrian#include "prompt.h"
6736285Sbrian#include "lcpproto.h"
6836285Sbrian#include "pap.h"
6936285Sbrian#include "chap.h"
7036285Sbrian#include "command.h"
7136285Sbrian#include "datalink.h"
7236285Sbrian
7336285Sbrianstatic void datalink_LoginDone(struct datalink *);
7436285Sbrianstatic void datalink_NewState(struct datalink *, int);
7536285Sbrian
7636285Sbrianstatic void
7736285Sbriandatalink_OpenTimeout(void *v)
7836285Sbrian{
7936285Sbrian  struct datalink *dl = (struct datalink *)v;
8036285Sbrian
8136285Sbrian  timer_Stop(&dl->dial_timer);
8236285Sbrian  if (dl->state == DATALINK_OPENING)
8336285Sbrian    log_Printf(LogPHASE, "%s: Redial timer expired.\n", dl->name);
8436285Sbrian}
8536285Sbrian
8636285Sbrianstatic void
8736285Sbriandatalink_StartDialTimer(struct datalink *dl, int Timeout)
8836285Sbrian{
8936285Sbrian  timer_Stop(&dl->dial_timer);
9036285Sbrian
9136285Sbrian  if (Timeout) {
9236285Sbrian    if (Timeout > 0)
9336285Sbrian      dl->dial_timer.load = Timeout * SECTICKS;
9436285Sbrian    else
9536285Sbrian      dl->dial_timer.load = (random() % DIAL_TIMEOUT) * SECTICKS;
9636285Sbrian    dl->dial_timer.func = datalink_OpenTimeout;
9736285Sbrian    dl->dial_timer.name = "dial";
9836285Sbrian    dl->dial_timer.arg = dl;
9936285Sbrian    timer_Start(&dl->dial_timer);
10036285Sbrian    if (dl->state == DATALINK_OPENING)
10136285Sbrian      log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
10236285Sbrian                dl->name, Timeout);
10336285Sbrian  }
10436285Sbrian}
10536285Sbrian
10636285Sbrianstatic void
10736285Sbriandatalink_HangupDone(struct datalink *dl)
10836285Sbrian{
10936285Sbrian  if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
11036285Sbrian      physical_GetFD(dl->physical) != -1) {
11136285Sbrian    /* Don't close our modem if the link is dedicated */
11236285Sbrian    datalink_LoginDone(dl);
11336285Sbrian    return;
11436285Sbrian  }
11536285Sbrian
11636285Sbrian  modem_Close(dl->physical);
11736285Sbrian  dl->phone.chosen = "N/A";
11836285Sbrian
11936285Sbrian  if (dl->bundle->CleaningUp ||
12036285Sbrian      (dl->physical->type == PHYS_DIRECT) ||
12136285Sbrian      ((!dl->dial_tries || (dl->dial_tries < 0 && !dl->reconnect_tries)) &&
12236465Sbrian       !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
12336285Sbrian    datalink_NewState(dl, DATALINK_CLOSED);
12436285Sbrian    dl->dial_tries = -1;
12536285Sbrian    dl->reconnect_tries = 0;
12636285Sbrian    bundle_LinkClosed(dl->bundle, dl);
12736285Sbrian    if (!dl->bundle->CleaningUp)
12836285Sbrian      datalink_StartDialTimer(dl, dl->cfg.dial.timeout);
12936285Sbrian  } else {
13036285Sbrian    datalink_NewState(dl, DATALINK_OPENING);
13136285Sbrian    if (dl->dial_tries < 0) {
13236285Sbrian      datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
13336285Sbrian      dl->dial_tries = dl->cfg.dial.max;
13436285Sbrian      dl->reconnect_tries--;
13536285Sbrian    } else {
13636285Sbrian      if (dl->phone.next == NULL)
13736285Sbrian        datalink_StartDialTimer(dl, dl->cfg.dial.timeout);
13836285Sbrian      else
13936285Sbrian        datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
14036285Sbrian    }
14136285Sbrian  }
14236285Sbrian}
14336285Sbrian
14436285Sbrianstatic const char *
14536285Sbriandatalink_ChoosePhoneNumber(struct datalink *dl)
14636285Sbrian{
14736285Sbrian  char *phone;
14836285Sbrian
14936285Sbrian  if (dl->phone.alt == NULL) {
15036285Sbrian    if (dl->phone.next == NULL) {
15136285Sbrian      strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
15236285Sbrian      dl->phone.list[sizeof dl->phone.list - 1] = '\0';
15336285Sbrian      dl->phone.next = dl->phone.list;
15436285Sbrian    }
15536285Sbrian    dl->phone.alt = strsep(&dl->phone.next, ":");
15636285Sbrian  }
15736285Sbrian  phone = strsep(&dl->phone.alt, "|");
15836285Sbrian  dl->phone.chosen = *phone ? phone : "[NONE]";
15936285Sbrian  if (*phone)
16036285Sbrian    log_Printf(LogPHASE, "Phone: %s\n", phone);
16136285Sbrian  return phone;
16236285Sbrian}
16336285Sbrian
16436285Sbrianstatic void
16536285Sbriandatalink_LoginDone(struct datalink *dl)
16636285Sbrian{
16736285Sbrian  if (!dl->script.packetmode) {
16836285Sbrian    dl->dial_tries = -1;
16936285Sbrian    datalink_NewState(dl, DATALINK_READY);
17036285Sbrian  } else if (modem_Raw(dl->physical, dl->bundle) < 0) {
17136285Sbrian    dl->dial_tries = 0;
17236285Sbrian    log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
17336285Sbrian    if (dl->script.run) {
17436285Sbrian      datalink_NewState(dl, DATALINK_HANGUP);
17536285Sbrian      modem_Offline(dl->physical);
17636285Sbrian      chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
17736285Sbrian    } else {
17836285Sbrian      if (dl->physical->type == PHYS_DEDICATED)
17936285Sbrian        /* force a redial timeout */
18036285Sbrian        modem_Close(dl->physical);
18136285Sbrian      datalink_HangupDone(dl);
18236285Sbrian    }
18336285Sbrian  } else {
18436285Sbrian    dl->dial_tries = -1;
18536285Sbrian
18636285Sbrian    hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
18736285Sbrian    async_Init(&dl->physical->async);
18836285Sbrian
18936285Sbrian    lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
19036285Sbrian              0 : dl->physical->link.lcp.cfg.openmode);
19136285Sbrian    ccp_Setup(&dl->physical->link.ccp);
19236285Sbrian
19336285Sbrian    datalink_NewState(dl, DATALINK_LCP);
19436285Sbrian    fsm_Up(&dl->physical->link.lcp.fsm);
19536285Sbrian    fsm_Open(&dl->physical->link.lcp.fsm);
19636285Sbrian  }
19736285Sbrian}
19836285Sbrian
19936285Sbrianstatic int
20036285Sbriandatalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
20136285Sbrian                   int *n)
20236285Sbrian{
20336285Sbrian  struct datalink *dl = descriptor2datalink(d);
20436285Sbrian  int result;
20536285Sbrian
20636285Sbrian  result = 0;
20736285Sbrian  switch (dl->state) {
20836285Sbrian    case DATALINK_CLOSED:
20936465Sbrian      if ((dl->physical->type &
21036465Sbrian           (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|PHYS_DDIAL)) &&
21136285Sbrian          !bundle_IsDead(dl->bundle))
21236285Sbrian        /*
21336465Sbrian         * Our first time in - DEDICATED & DDIAL never come down, and
21436465Sbrian         * DIRECT & BACKGROUND get deleted when they enter DATALINK_CLOSED.
21536465Sbrian         * Go to DATALINK_OPENING via datalink_Up() and fall through.
21636285Sbrian         */
21736285Sbrian        datalink_Up(dl, 1, 1);
21836285Sbrian      else
21936285Sbrian        break;
22036285Sbrian      /* fall through */
22136285Sbrian
22236285Sbrian    case DATALINK_OPENING:
22336285Sbrian      if (dl->dial_timer.state != TIMER_RUNNING) {
22436285Sbrian        if (--dl->dial_tries < 0)
22536285Sbrian          dl->dial_tries = 0;
22636285Sbrian        if (modem_Open(dl->physical, dl->bundle) >= 0) {
22736285Sbrian          if (dl->script.run) {
22836285Sbrian            datalink_NewState(dl, DATALINK_DIAL);
22936285Sbrian            chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial, 1,
23036285Sbrian                      datalink_ChoosePhoneNumber(dl));
23136465Sbrian            if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
23236285Sbrian                dl->cfg.dial.max)
23336285Sbrian              log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
23436285Sbrian                        dl->name, dl->cfg.dial.max - dl->dial_tries,
23536285Sbrian                        dl->cfg.dial.max);
23636285Sbrian            return datalink_UpdateSet(d, r, w, e, n);
23736285Sbrian          } else
23836285Sbrian            datalink_LoginDone(dl);
23936285Sbrian        } else {
24036465Sbrian          if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
24136285Sbrian              dl->cfg.dial.max)
24236285Sbrian            log_Printf(LogCHAT, "Failed to open modem (attempt %u of %d)\n",
24336285Sbrian                      dl->cfg.dial.max - dl->dial_tries, dl->cfg.dial.max);
24436285Sbrian          else
24536285Sbrian            log_Printf(LogCHAT, "Failed to open modem\n");
24636285Sbrian
24736285Sbrian          if (dl->bundle->CleaningUp ||
24836465Sbrian              (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
24936285Sbrian               dl->cfg.dial.max && dl->dial_tries == 0)) {
25036285Sbrian            datalink_NewState(dl, DATALINK_CLOSED);
25136285Sbrian            dl->reconnect_tries = 0;
25236285Sbrian            dl->dial_tries = -1;
25336285Sbrian            bundle_LinkClosed(dl->bundle, dl);
25436285Sbrian          }
25536285Sbrian          if (!dl->bundle->CleaningUp)
25636285Sbrian            datalink_StartDialTimer(dl, dl->cfg.dial.timeout);
25736285Sbrian        }
25836285Sbrian      }
25936285Sbrian      break;
26036285Sbrian
26136285Sbrian    case DATALINK_HANGUP:
26236285Sbrian    case DATALINK_DIAL:
26336285Sbrian    case DATALINK_LOGIN:
26436285Sbrian      result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
26536285Sbrian      switch (dl->chat.state) {
26636285Sbrian        case CHAT_DONE:
26736285Sbrian          /* script succeeded */
26836285Sbrian          chat_Destroy(&dl->chat);
26936285Sbrian          switch(dl->state) {
27036285Sbrian            case DATALINK_HANGUP:
27136285Sbrian              datalink_HangupDone(dl);
27236285Sbrian              break;
27336285Sbrian            case DATALINK_DIAL:
27436285Sbrian              datalink_NewState(dl, DATALINK_LOGIN);
27536285Sbrian              chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, 0, NULL);
27636285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
27736285Sbrian            case DATALINK_LOGIN:
27836285Sbrian              datalink_LoginDone(dl);
27936285Sbrian              break;
28036285Sbrian          }
28136285Sbrian          break;
28236285Sbrian        case CHAT_FAILED:
28336285Sbrian          /* Going down - script failed */
28436285Sbrian          log_Printf(LogWARN, "Chat script failed\n");
28536285Sbrian          chat_Destroy(&dl->chat);
28636285Sbrian          switch(dl->state) {
28736285Sbrian            case DATALINK_HANGUP:
28836285Sbrian              datalink_HangupDone(dl);
28936285Sbrian              break;
29036285Sbrian            case DATALINK_DIAL:
29136285Sbrian            case DATALINK_LOGIN:
29236285Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
29336285Sbrian              modem_Offline(dl->physical);
29436285Sbrian              chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
29536285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
29636285Sbrian          }
29736285Sbrian          break;
29836285Sbrian      }
29936285Sbrian      break;
30036285Sbrian
30136285Sbrian    case DATALINK_READY:
30236285Sbrian    case DATALINK_LCP:
30336285Sbrian    case DATALINK_AUTH:
30436285Sbrian    case DATALINK_OPEN:
30536285Sbrian      result = descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
30636285Sbrian      break;
30736285Sbrian  }
30836285Sbrian  return result;
30936285Sbrian}
31036285Sbrian
31136285Sbrianint
31236285Sbriandatalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
31336285Sbrian{
31436285Sbrian  return physical_RemoveFromSet(dl->physical, r, w, e);
31536285Sbrian}
31636285Sbrian
31736285Sbrianstatic int
31836285Sbriandatalink_IsSet(struct descriptor *d, const fd_set *fdset)
31936285Sbrian{
32036285Sbrian  struct datalink *dl = descriptor2datalink(d);
32136285Sbrian
32236285Sbrian  switch (dl->state) {
32336285Sbrian    case DATALINK_CLOSED:
32436285Sbrian    case DATALINK_OPENING:
32536285Sbrian      break;
32636285Sbrian
32736285Sbrian    case DATALINK_HANGUP:
32836285Sbrian    case DATALINK_DIAL:
32936285Sbrian    case DATALINK_LOGIN:
33036285Sbrian      return descriptor_IsSet(&dl->chat.desc, fdset);
33136285Sbrian
33236285Sbrian    case DATALINK_READY:
33336285Sbrian    case DATALINK_LCP:
33436285Sbrian    case DATALINK_AUTH:
33536285Sbrian    case DATALINK_OPEN:
33636285Sbrian      return descriptor_IsSet(&dl->physical->desc, fdset);
33736285Sbrian  }
33836285Sbrian  return 0;
33936285Sbrian}
34036285Sbrian
34136285Sbrianstatic void
34236285Sbriandatalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
34336285Sbrian{
34436285Sbrian  struct datalink *dl = descriptor2datalink(d);
34536285Sbrian
34636285Sbrian  switch (dl->state) {
34736285Sbrian    case DATALINK_CLOSED:
34836285Sbrian    case DATALINK_OPENING:
34936285Sbrian      break;
35036285Sbrian
35136285Sbrian    case DATALINK_HANGUP:
35236285Sbrian    case DATALINK_DIAL:
35336285Sbrian    case DATALINK_LOGIN:
35436285Sbrian      descriptor_Read(&dl->chat.desc, bundle, fdset);
35536285Sbrian      break;
35636285Sbrian
35736285Sbrian    case DATALINK_READY:
35836285Sbrian    case DATALINK_LCP:
35936285Sbrian    case DATALINK_AUTH:
36036285Sbrian    case DATALINK_OPEN:
36136285Sbrian      descriptor_Read(&dl->physical->desc, bundle, fdset);
36236285Sbrian      break;
36336285Sbrian  }
36436285Sbrian}
36536285Sbrian
36636285Sbrianstatic void
36736285Sbriandatalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
36836285Sbrian{
36936285Sbrian  struct datalink *dl = descriptor2datalink(d);
37036285Sbrian
37136285Sbrian  switch (dl->state) {
37236285Sbrian    case DATALINK_CLOSED:
37336285Sbrian    case DATALINK_OPENING:
37436285Sbrian      break;
37536285Sbrian
37636285Sbrian    case DATALINK_HANGUP:
37736285Sbrian    case DATALINK_DIAL:
37836285Sbrian    case DATALINK_LOGIN:
37936285Sbrian      descriptor_Write(&dl->chat.desc, bundle, fdset);
38036285Sbrian      break;
38136285Sbrian
38236285Sbrian    case DATALINK_READY:
38336285Sbrian    case DATALINK_LCP:
38436285Sbrian    case DATALINK_AUTH:
38536285Sbrian    case DATALINK_OPEN:
38636285Sbrian      descriptor_Write(&dl->physical->desc, bundle, fdset);
38736285Sbrian      break;
38836285Sbrian  }
38936285Sbrian}
39036285Sbrian
39136285Sbrianstatic void
39237007Sbriandatalink_ComeDown(struct datalink *dl, int how)
39336285Sbrian{
39437007Sbrian  if (how != CLOSE_NORMAL) {
39536285Sbrian    dl->dial_tries = -1;
39636285Sbrian    dl->reconnect_tries = 0;
39737012Sbrian    if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
39837007Sbrian      dl->stayonline = 1;
39936285Sbrian  }
40036285Sbrian
40137012Sbrian  if (dl->state >= DATALINK_READY && dl->stayonline) {
40237007Sbrian    dl->stayonline = 0;
40337007Sbrian    datalink_NewState(dl, DATALINK_READY);
40437007Sbrian  } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
40536285Sbrian    modem_Offline(dl->physical);
40636285Sbrian    if (dl->script.run && dl->state != DATALINK_OPENING) {
40736285Sbrian      datalink_NewState(dl, DATALINK_HANGUP);
40836285Sbrian      chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
40936285Sbrian    } else
41036285Sbrian      datalink_HangupDone(dl);
41136285Sbrian  }
41236285Sbrian}
41336285Sbrian
41436285Sbrianstatic void
41536285Sbriandatalink_LayerStart(void *v, struct fsm *fp)
41636285Sbrian{
41736285Sbrian  /* The given FSM is about to start up ! */
41836285Sbrian  struct datalink *dl = (struct datalink *)v;
41936285Sbrian
42036285Sbrian  if (fp->proto == PROTO_LCP)
42136285Sbrian    (*dl->parent->LayerStart)(dl->parent->object, fp);
42236285Sbrian}
42336285Sbrian
42436285Sbrianstatic void
42536285Sbriandatalink_LayerUp(void *v, struct fsm *fp)
42636285Sbrian{
42736285Sbrian  /* The given fsm is now up */
42836285Sbrian  struct datalink *dl = (struct datalink *)v;
42936285Sbrian
43036285Sbrian  if (fp->proto == PROTO_LCP) {
43136285Sbrian    datalink_GotAuthname(dl, "", 0);
43236285Sbrian    dl->physical->link.lcp.auth_ineed = dl->physical->link.lcp.want_auth;
43336285Sbrian    dl->physical->link.lcp.auth_iwait = dl->physical->link.lcp.his_auth;
43436285Sbrian    if (dl->physical->link.lcp.his_auth || dl->physical->link.lcp.want_auth) {
43536285Sbrian      if (bundle_Phase(dl->bundle) == PHASE_ESTABLISH)
43636285Sbrian        bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
43736285Sbrian      log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
43836285Sbrian                Auth2Nam(dl->physical->link.lcp.his_auth),
43936285Sbrian                Auth2Nam(dl->physical->link.lcp.want_auth));
44036285Sbrian      if (dl->physical->link.lcp.his_auth == PROTO_PAP)
44136285Sbrian        auth_StartChallenge(&dl->pap, dl->physical, pap_SendChallenge);
44236285Sbrian      if (dl->physical->link.lcp.want_auth == PROTO_CHAP)
44336285Sbrian        auth_StartChallenge(&dl->chap.auth, dl->physical, chap_SendChallenge);
44436285Sbrian    } else
44536285Sbrian      datalink_AuthOk(dl);
44636285Sbrian  }
44736285Sbrian}
44836285Sbrian
44936285Sbrianvoid
45036285Sbriandatalink_GotAuthname(struct datalink *dl, const char *name, int len)
45136285Sbrian{
45236285Sbrian  if (len >= sizeof dl->peer.authname)
45336285Sbrian    len = sizeof dl->peer.authname - 1;
45436285Sbrian  strncpy(dl->peer.authname, name, len);
45536285Sbrian  dl->peer.authname[len] = '\0';
45636285Sbrian}
45736285Sbrian
45836285Sbrianvoid
45936285Sbriandatalink_AuthOk(struct datalink *dl)
46036285Sbrian{
46136310Sbrian  ccp_SetOpenMode(&dl->physical->link.ccp);
46236310Sbrian
46336285Sbrian  if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
46436285Sbrian    /* we've authenticated in multilink mode ! */
46536285Sbrian    switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
46636285Sbrian      case MP_LINKSENT:
46736285Sbrian        /* We've handed the link off to another ppp (well, we will soon) ! */
46836285Sbrian        return;
46936285Sbrian      case MP_UP:
47036285Sbrian        /* First link in the bundle */
47136285Sbrian        auth_Select(dl->bundle, dl->peer.authname, dl->physical);
47236285Sbrian        /* fall through */
47336285Sbrian      case MP_ADDED:
47436285Sbrian        /* We're in multilink mode ! */
47536310Sbrian        dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
47636285Sbrian        break;
47736285Sbrian      case MP_FAILED:
47836285Sbrian        datalink_AuthNotOk(dl);
47936285Sbrian        return;
48036285Sbrian    }
48136285Sbrian  } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
48236285Sbrian    log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
48336285Sbrian    datalink_AuthNotOk(dl);
48436285Sbrian    return;
48536285Sbrian  } else {
48636285Sbrian    dl->bundle->ncp.mp.peer = dl->peer;
48736285Sbrian    ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
48836285Sbrian    auth_Select(dl->bundle, dl->peer.authname, dl->physical);
48936285Sbrian  }
49036285Sbrian
49136285Sbrian  fsm_Up(&dl->physical->link.ccp.fsm);
49236285Sbrian  fsm_Open(&dl->physical->link.ccp.fsm);
49336285Sbrian  datalink_NewState(dl, DATALINK_OPEN);
49436285Sbrian  bundle_NewPhase(dl->bundle, PHASE_NETWORK);
49536285Sbrian  (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
49636285Sbrian}
49736285Sbrian
49836285Sbrianvoid
49936285Sbriandatalink_AuthNotOk(struct datalink *dl)
50036285Sbrian{
50136285Sbrian  datalink_NewState(dl, DATALINK_LCP);
50236285Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
50336285Sbrian}
50436285Sbrian
50536285Sbrianstatic void
50636285Sbriandatalink_LayerDown(void *v, struct fsm *fp)
50736285Sbrian{
50836285Sbrian  /* The given FSM has been told to come down */
50936285Sbrian  struct datalink *dl = (struct datalink *)v;
51036285Sbrian
51136285Sbrian  if (fp->proto == PROTO_LCP) {
51236285Sbrian    switch (dl->state) {
51336285Sbrian      case DATALINK_OPEN:
51436285Sbrian        peerid_Init(&dl->peer);
51536285Sbrian        fsm_Down(&dl->physical->link.ccp.fsm);
51636285Sbrian        fsm_Close(&dl->physical->link.ccp.fsm);
51736928Sbrian        datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
51836285Sbrian        (*dl->parent->LayerDown)(dl->parent->object, fp);
51936285Sbrian        /* fall through */
52036285Sbrian
52136285Sbrian      case DATALINK_AUTH:
52236285Sbrian        timer_Stop(&dl->pap.authtimer);
52336285Sbrian        timer_Stop(&dl->chap.auth.authtimer);
52436285Sbrian    }
52536285Sbrian    datalink_NewState(dl, DATALINK_LCP);
52636285Sbrian  }
52736285Sbrian}
52836285Sbrian
52936285Sbrianstatic void
53036285Sbriandatalink_LayerFinish(void *v, struct fsm *fp)
53136285Sbrian{
53236285Sbrian  /* The given fsm is now down */
53336285Sbrian  struct datalink *dl = (struct datalink *)v;
53436285Sbrian
53536285Sbrian  if (fp->proto == PROTO_LCP) {
53636345Sbrian    if (fp->state == ST_STOPPED)
53736345Sbrian      fsm_Close(fp);			/* back to CLOSED */
53836345Sbrian    fsm_Down(fp);			/* Bring us to INITIAL or STARTING */
53936285Sbrian    (*dl->parent->LayerFinish)(dl->parent->object, fp);
54037007Sbrian    datalink_ComeDown(dl, CLOSE_NORMAL);
54136285Sbrian  } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
54236285Sbrian    fsm_Open(fp);		/* CCP goes to ST_STOPPED */
54336285Sbrian}
54436285Sbrian
54536285Sbrianstruct datalink *
54636285Sbriandatalink_Create(const char *name, struct bundle *bundle, int type)
54736285Sbrian{
54836285Sbrian  struct datalink *dl;
54936285Sbrian
55036285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
55136285Sbrian  if (dl == NULL)
55236285Sbrian    return dl;
55336285Sbrian
55436285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
55536285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
55636285Sbrian  dl->desc.IsSet = datalink_IsSet;
55736285Sbrian  dl->desc.Read = datalink_Read;
55836285Sbrian  dl->desc.Write = datalink_Write;
55936285Sbrian
56036285Sbrian  dl->state = DATALINK_CLOSED;
56136285Sbrian
56236285Sbrian  *dl->cfg.script.dial = '\0';
56336285Sbrian  *dl->cfg.script.login = '\0';
56436285Sbrian  *dl->cfg.script.hangup = '\0';
56536285Sbrian  *dl->cfg.phone.list = '\0';
56636285Sbrian  *dl->phone.list = '\0';
56736285Sbrian  dl->phone.next = NULL;
56836285Sbrian  dl->phone.alt = NULL;
56936285Sbrian  dl->phone.chosen = "N/A";
57037007Sbrian  dl->stayonline = 0;
57136285Sbrian  dl->script.run = 1;
57236285Sbrian  dl->script.packetmode = 1;
57336285Sbrian  mp_linkInit(&dl->mp);
57436285Sbrian
57536285Sbrian  dl->bundle = bundle;
57636285Sbrian  dl->next = NULL;
57736285Sbrian
57836285Sbrian  memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
57936285Sbrian
58036285Sbrian  dl->dial_tries = 0;
58136285Sbrian  dl->cfg.dial.max = 1;
58236285Sbrian  dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
58336285Sbrian  dl->cfg.dial.timeout = DIAL_TIMEOUT;
58436285Sbrian
58536285Sbrian  dl->reconnect_tries = 0;
58636285Sbrian  dl->cfg.reconnect.max = 0;
58736285Sbrian  dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
58836285Sbrian
58936285Sbrian  dl->name = strdup(name);
59036285Sbrian  peerid_Init(&dl->peer);
59136285Sbrian  dl->parent = &bundle->fsm;
59236285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
59336285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
59436285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
59536285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
59636285Sbrian  dl->fsmp.object = dl;
59736285Sbrian
59836285Sbrian  auth_Init(&dl->pap);
59936285Sbrian  auth_Init(&dl->chap.auth);
60036285Sbrian
60136285Sbrian  if ((dl->physical = modem_Create(dl, type)) == NULL) {
60236285Sbrian    free(dl->name);
60336285Sbrian    free(dl);
60436285Sbrian    return NULL;
60536285Sbrian  }
60636285Sbrian  chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
60736285Sbrian
60836285Sbrian  log_Printf(LogPHASE, "%s: Created in %s state\n",
60936285Sbrian             dl->name, datalink_State(dl));
61036285Sbrian
61136285Sbrian  return dl;
61236285Sbrian}
61336285Sbrian
61436285Sbrianstruct datalink *
61536285Sbriandatalink_Clone(struct datalink *odl, const char *name)
61636285Sbrian{
61736285Sbrian  struct datalink *dl;
61836285Sbrian
61936285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
62036285Sbrian  if (dl == NULL)
62136285Sbrian    return dl;
62236285Sbrian
62336285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
62436285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
62536285Sbrian  dl->desc.IsSet = datalink_IsSet;
62636285Sbrian  dl->desc.Read = datalink_Read;
62736285Sbrian  dl->desc.Write = datalink_Write;
62836285Sbrian
62936285Sbrian  dl->state = DATALINK_CLOSED;
63036285Sbrian
63136285Sbrian  memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
63236285Sbrian  mp_linkInit(&dl->mp);
63336285Sbrian  *dl->phone.list = '\0';
63436285Sbrian  dl->phone.next = NULL;
63536285Sbrian  dl->phone.alt = NULL;
63636285Sbrian  dl->phone.chosen = "N/A";
63736285Sbrian  dl->bundle = odl->bundle;
63836285Sbrian  dl->next = NULL;
63936285Sbrian  memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
64036285Sbrian  dl->dial_tries = 0;
64136285Sbrian  dl->reconnect_tries = 0;
64236285Sbrian  dl->name = strdup(name);
64336285Sbrian  peerid_Init(&dl->peer);
64436285Sbrian  dl->parent = odl->parent;
64536285Sbrian  memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
64636285Sbrian  dl->fsmp.object = dl;
64736285Sbrian  auth_Init(&dl->pap);
64836285Sbrian  dl->pap.cfg.fsmretry = odl->pap.cfg.fsmretry;
64936285Sbrian
65036285Sbrian  auth_Init(&dl->chap.auth);
65136285Sbrian  dl->chap.auth.cfg.fsmretry = odl->chap.auth.cfg.fsmretry;
65236285Sbrian
65336465Sbrian  if ((dl->physical = modem_Create(dl, PHYS_INTERACTIVE)) == NULL) {
65436285Sbrian    free(dl->name);
65536285Sbrian    free(dl);
65636285Sbrian    return NULL;
65736285Sbrian  }
65836285Sbrian  memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
65936285Sbrian  memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
66036285Sbrian         sizeof dl->physical->link.lcp.cfg);
66136285Sbrian  memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
66236285Sbrian         sizeof dl->physical->link.ccp.cfg);
66336285Sbrian  memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
66436285Sbrian         sizeof dl->physical->async.cfg);
66536285Sbrian
66636285Sbrian  chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
66736285Sbrian
66836285Sbrian  log_Printf(LogPHASE, "%s: Cloned in %s state\n",
66936285Sbrian             dl->name, datalink_State(dl));
67036285Sbrian
67136285Sbrian  return dl;
67236285Sbrian}
67336285Sbrian
67436285Sbrianstruct datalink *
67536285Sbriandatalink_Destroy(struct datalink *dl)
67636285Sbrian{
67736285Sbrian  struct datalink *result;
67836285Sbrian
67936285Sbrian  if (dl->state != DATALINK_CLOSED) {
68036285Sbrian    log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
68136285Sbrian              datalink_State(dl));
68236285Sbrian    switch (dl->state) {
68336285Sbrian      case DATALINK_HANGUP:
68436285Sbrian      case DATALINK_DIAL:
68536285Sbrian      case DATALINK_LOGIN:
68636285Sbrian        chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
68736285Sbrian        break;
68836285Sbrian    }
68936285Sbrian  }
69036285Sbrian
69136285Sbrian  result = dl->next;
69236285Sbrian  modem_Destroy(dl->physical);
69336285Sbrian  free(dl->name);
69436285Sbrian  free(dl);
69536285Sbrian
69636285Sbrian  return result;
69736285Sbrian}
69836285Sbrian
69936285Sbrianvoid
70036285Sbriandatalink_Up(struct datalink *dl, int runscripts, int packetmode)
70136285Sbrian{
70236285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
70336285Sbrian    /* Ignore scripts */
70436285Sbrian    runscripts = 0;
70536285Sbrian
70636285Sbrian  switch (dl->state) {
70736285Sbrian    case DATALINK_CLOSED:
70836285Sbrian      if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
70936285Sbrian          bundle_Phase(dl->bundle) == PHASE_TERMINATE)
71036285Sbrian        bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
71136285Sbrian      datalink_NewState(dl, DATALINK_OPENING);
71236285Sbrian      dl->reconnect_tries =
71336285Sbrian        dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
71436285Sbrian      dl->dial_tries = dl->cfg.dial.max;
71536285Sbrian      dl->script.run = runscripts;
71636285Sbrian      dl->script.packetmode = packetmode;
71736285Sbrian      break;
71836285Sbrian
71936285Sbrian    case DATALINK_OPENING:
72036285Sbrian      if (!dl->script.run && runscripts)
72136285Sbrian        dl->script.run = 1;
72236285Sbrian      /* fall through */
72336285Sbrian
72436285Sbrian    case DATALINK_DIAL:
72536285Sbrian    case DATALINK_LOGIN:
72636285Sbrian    case DATALINK_READY:
72736285Sbrian      if (!dl->script.packetmode && packetmode) {
72836285Sbrian        dl->script.packetmode = 1;
72936285Sbrian        if (dl->state == DATALINK_READY)
73036285Sbrian          datalink_LoginDone(dl);
73136285Sbrian      }
73236285Sbrian      break;
73336285Sbrian  }
73436285Sbrian}
73536285Sbrian
73636285Sbrianvoid
73737007Sbriandatalink_Close(struct datalink *dl, int how)
73836285Sbrian{
73936285Sbrian  /* Please close */
74036285Sbrian  switch (dl->state) {
74136285Sbrian    case DATALINK_OPEN:
74236285Sbrian      peerid_Init(&dl->peer);
74336285Sbrian      fsm_Down(&dl->physical->link.ccp.fsm);
74436285Sbrian      fsm_Close(&dl->physical->link.ccp.fsm);
74536285Sbrian      /* fall through */
74636285Sbrian
74736285Sbrian    case DATALINK_AUTH:
74836285Sbrian    case DATALINK_LCP:
74936285Sbrian      fsm_Close(&dl->physical->link.lcp.fsm);
75037007Sbrian      if (how != CLOSE_NORMAL) {
75136285Sbrian        dl->dial_tries = -1;
75236285Sbrian        dl->reconnect_tries = 0;
75337007Sbrian        if (how == CLOSE_LCP)
75437007Sbrian          dl->stayonline = 1;
75536285Sbrian      }
75636285Sbrian      break;
75736285Sbrian
75836285Sbrian    default:
75937007Sbrian      datalink_ComeDown(dl, how);
76036285Sbrian  }
76136285Sbrian}
76236285Sbrian
76336285Sbrianvoid
76437007Sbriandatalink_Down(struct datalink *dl, int how)
76536285Sbrian{
76636285Sbrian  /* Carrier is lost */
76736285Sbrian  switch (dl->state) {
76836285Sbrian    case DATALINK_OPEN:
76936285Sbrian      peerid_Init(&dl->peer);
77036285Sbrian      fsm_Down(&dl->physical->link.ccp.fsm);
77136285Sbrian      fsm_Close(&dl->physical->link.ccp.fsm);
77236285Sbrian      /* fall through */
77336285Sbrian
77436285Sbrian    case DATALINK_AUTH:
77536285Sbrian    case DATALINK_LCP:
77636345Sbrian      if (dl->physical->link.lcp.fsm.state == ST_STOPPED)
77736345Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);		/* back to CLOSED */
77836285Sbrian      fsm_Down(&dl->physical->link.lcp.fsm);
77937007Sbrian      if (how != CLOSE_NORMAL)
78036285Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
78136285Sbrian      else
78236285Sbrian        fsm_Open(&dl->physical->link.ccp.fsm);
78336285Sbrian      /* fall through */
78436285Sbrian
78536285Sbrian    default:
78637007Sbrian      datalink_ComeDown(dl, how);
78736285Sbrian  }
78836285Sbrian}
78936285Sbrian
79036285Sbrianvoid
79136285Sbriandatalink_StayDown(struct datalink *dl)
79236285Sbrian{
79336285Sbrian  dl->reconnect_tries = 0;
79436285Sbrian}
79536285Sbrian
79637007Sbrianvoid
79737007Sbriandatalink_DontHangup(struct datalink *dl)
79837007Sbrian{
79937012Sbrian  if (dl->state >= DATALINK_LCP)
80037012Sbrian    dl->stayonline = 1;
80137007Sbrian}
80237007Sbrian
80336285Sbrianint
80436285Sbriandatalink_Show(struct cmdargs const *arg)
80536285Sbrian{
80636285Sbrian  prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
80736285Sbrian  prompt_Printf(arg->prompt, " State:            %s\n",
80836285Sbrian                datalink_State(arg->cx));
80936285Sbrian  prompt_Printf(arg->prompt, " CHAP Encryption:  %s\n",
81036285Sbrian                arg->cx->chap.using_MSChap ? "MSChap" : "MD5" );
81136285Sbrian  prompt_Printf(arg->prompt, " Peer name:        ");
81236285Sbrian  if (*arg->cx->peer.authname)
81336285Sbrian    prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
81436285Sbrian  else if (arg->cx->state == DATALINK_OPEN)
81536285Sbrian    prompt_Printf(arg->prompt, "None requested\n");
81636285Sbrian  else
81736285Sbrian    prompt_Printf(arg->prompt, "N/A\n");
81836285Sbrian  prompt_Printf(arg->prompt, " Discriminator:    %s\n",
81936285Sbrian                mp_Enddisc(arg->cx->peer.enddisc.class,
82036285Sbrian                           arg->cx->peer.enddisc.address,
82136285Sbrian                           arg->cx->peer.enddisc.len));
82236285Sbrian
82336285Sbrian  prompt_Printf(arg->prompt, "\nDefaults:\n");
82436285Sbrian  prompt_Printf(arg->prompt, " Phone List:       %s\n",
82536285Sbrian                arg->cx->cfg.phone.list);
82636285Sbrian  if (arg->cx->cfg.dial.max)
82736285Sbrian    prompt_Printf(arg->prompt, " Dial tries:       %d, delay ",
82836285Sbrian                  arg->cx->cfg.dial.max);
82936285Sbrian  else
83036285Sbrian    prompt_Printf(arg->prompt, " Dial tries:       infinite, delay ");
83136285Sbrian  if (arg->cx->cfg.dial.next_timeout > 0)
83236285Sbrian    prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
83336285Sbrian  else
83436285Sbrian    prompt_Printf(arg->prompt, "random/");
83536285Sbrian  if (arg->cx->cfg.dial.timeout > 0)
83636285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
83736285Sbrian  else
83836285Sbrian    prompt_Printf(arg->prompt, "random\n");
83936285Sbrian  prompt_Printf(arg->prompt, " Reconnect tries:  %d, delay ",
84036285Sbrian                arg->cx->cfg.reconnect.max);
84136285Sbrian  if (arg->cx->cfg.reconnect.timeout > 0)
84236285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
84336285Sbrian  else
84436285Sbrian    prompt_Printf(arg->prompt, "random\n");
84536285Sbrian  prompt_Printf(arg->prompt, " Dial Script:      %s\n",
84636285Sbrian                arg->cx->cfg.script.dial);
84736285Sbrian  prompt_Printf(arg->prompt, " Login Script:     %s\n",
84836285Sbrian                arg->cx->cfg.script.login);
84936285Sbrian  prompt_Printf(arg->prompt, " Hangup Script:    %s\n",
85036285Sbrian                arg->cx->cfg.script.hangup);
85136285Sbrian  return 0;
85236285Sbrian}
85336285Sbrian
85436285Sbrianint
85536285Sbriandatalink_SetReconnect(struct cmdargs const *arg)
85636285Sbrian{
85736285Sbrian  if (arg->argc == arg->argn+2) {
85836285Sbrian    arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
85936285Sbrian    arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
86036285Sbrian    return 0;
86136285Sbrian  }
86236285Sbrian  return -1;
86336285Sbrian}
86436285Sbrian
86536285Sbrianint
86636285Sbriandatalink_SetRedial(struct cmdargs const *arg)
86736285Sbrian{
86836285Sbrian  int timeout;
86936285Sbrian  int tries;
87036285Sbrian  char *dot;
87136285Sbrian
87236285Sbrian  if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
87336285Sbrian    if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
87436285Sbrian	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
87536285Sbrian      arg->cx->cfg.dial.timeout = -1;
87636285Sbrian      randinit();
87736285Sbrian    } else {
87836285Sbrian      timeout = atoi(arg->argv[arg->argn]);
87936285Sbrian
88036285Sbrian      if (timeout >= 0)
88136285Sbrian	arg->cx->cfg.dial.timeout = timeout;
88236285Sbrian      else {
88336285Sbrian	log_Printf(LogWARN, "Invalid redial timeout\n");
88436285Sbrian	return -1;
88536285Sbrian      }
88636285Sbrian    }
88736285Sbrian
88836285Sbrian    dot = strchr(arg->argv[arg->argn], '.');
88936285Sbrian    if (dot) {
89036285Sbrian      if (strcasecmp(++dot, "random") == 0) {
89136285Sbrian	arg->cx->cfg.dial.next_timeout = -1;
89236285Sbrian	randinit();
89336285Sbrian      } else {
89436285Sbrian	timeout = atoi(dot);
89536285Sbrian	if (timeout >= 0)
89636285Sbrian	  arg->cx->cfg.dial.next_timeout = timeout;
89736285Sbrian	else {
89836285Sbrian	  log_Printf(LogWARN, "Invalid next redial timeout\n");
89936285Sbrian	  return -1;
90036285Sbrian	}
90136285Sbrian      }
90236285Sbrian    } else
90336285Sbrian      /* Default next timeout */
90436285Sbrian      arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
90536285Sbrian
90636285Sbrian    if (arg->argc == arg->argn+2) {
90736285Sbrian      tries = atoi(arg->argv[arg->argn+1]);
90836285Sbrian
90936285Sbrian      if (tries >= 0) {
91036285Sbrian	arg->cx->cfg.dial.max = tries;
91136285Sbrian      } else {
91236285Sbrian	log_Printf(LogWARN, "Invalid retry value\n");
91336285Sbrian	return 1;
91436285Sbrian      }
91536285Sbrian    }
91636285Sbrian    return 0;
91736285Sbrian  }
91836285Sbrian  return -1;
91936285Sbrian}
92036285Sbrian
92136285Sbrianstatic const char *states[] = {
92236285Sbrian  "closed",
92336285Sbrian  "opening",
92436285Sbrian  "hangup",
92536285Sbrian  "dial",
92636285Sbrian  "login",
92736285Sbrian  "ready",
92836285Sbrian  "lcp",
92936285Sbrian  "auth",
93036285Sbrian  "open"
93136285Sbrian};
93236285Sbrian
93336285Sbrianconst char *
93436285Sbriandatalink_State(struct datalink *dl)
93536285Sbrian{
93636285Sbrian  if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
93736285Sbrian    return "unknown";
93836285Sbrian  return states[dl->state];
93936285Sbrian}
94036285Sbrian
94136285Sbrianstatic void
94236285Sbriandatalink_NewState(struct datalink *dl, int state)
94336285Sbrian{
94436285Sbrian  if (state != dl->state) {
94536285Sbrian    if (state >= 0 && state < sizeof states / sizeof states[0]) {
94636285Sbrian      log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
94736285Sbrian                 states[state]);
94836285Sbrian      dl->state = state;
94936285Sbrian    } else
95036285Sbrian      log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
95136285Sbrian  }
95236285Sbrian}
95336285Sbrian
95436285Sbrianstruct datalink *
95536285Sbrianiov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
95636285Sbrian             int fd)
95736285Sbrian{
95836285Sbrian  struct datalink *dl, *cdl;
95936285Sbrian  u_int retry;
96036285Sbrian  char *oname;
96136285Sbrian
96236285Sbrian  dl = (struct datalink *)iov[(*niov)++].iov_base;
96336285Sbrian  dl->name = iov[*niov].iov_base;
96436285Sbrian
96536285Sbrian  if (dl->name[DATALINK_MAXNAME-1]) {
96636285Sbrian    dl->name[DATALINK_MAXNAME-1] = '\0';
96736285Sbrian    if (strlen(dl->name) == DATALINK_MAXNAME - 1)
96836285Sbrian      log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
96936285Sbrian  }
97036285Sbrian
97136285Sbrian  /* Make sure the name is unique ! */
97236285Sbrian  oname = NULL;
97336285Sbrian  do {
97436285Sbrian    for (cdl = bundle->links; cdl; cdl = cdl->next)
97536285Sbrian      if (!strcasecmp(dl->name, cdl->name)) {
97636285Sbrian        if (oname)
97736285Sbrian          free(datalink_NextName(dl));
97836285Sbrian        else
97936285Sbrian          oname = datalink_NextName(dl);
98036285Sbrian        break;	/* Keep renaming 'till we have no conflicts */
98136285Sbrian      }
98236285Sbrian  } while (cdl);
98336285Sbrian
98436285Sbrian  if (oname) {
98536285Sbrian    log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
98636285Sbrian    free(oname);
98736285Sbrian  } else {
98836285Sbrian    dl->name = strdup(dl->name);
98936285Sbrian    free(iov[*niov].iov_base);
99036285Sbrian  }
99136285Sbrian  (*niov)++;
99236285Sbrian
99336285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
99436285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
99536285Sbrian  dl->desc.IsSet = datalink_IsSet;
99636285Sbrian  dl->desc.Read = datalink_Read;
99736285Sbrian  dl->desc.Write = datalink_Write;
99836285Sbrian
99936285Sbrian  mp_linkInit(&dl->mp);
100036285Sbrian  *dl->phone.list = '\0';
100136285Sbrian  dl->phone.next = NULL;
100236285Sbrian  dl->phone.alt = NULL;
100336285Sbrian  dl->phone.chosen = "N/A";
100436285Sbrian
100536285Sbrian  dl->bundle = bundle;
100636285Sbrian  dl->next = NULL;
100736285Sbrian  memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
100836285Sbrian  dl->dial_tries = 0;
100936285Sbrian  dl->reconnect_tries = 0;
101036285Sbrian  dl->parent = &bundle->fsm;
101136285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
101236285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
101336285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
101436285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
101536285Sbrian  dl->fsmp.object = dl;
101636285Sbrian
101736285Sbrian  retry = dl->pap.cfg.fsmretry;
101836285Sbrian  auth_Init(&dl->pap);
101936285Sbrian  dl->pap.cfg.fsmretry = retry;
102036285Sbrian
102136285Sbrian  retry = dl->chap.auth.cfg.fsmretry;
102236285Sbrian  auth_Init(&dl->chap.auth);
102336285Sbrian  dl->chap.auth.cfg.fsmretry = retry;
102436285Sbrian
102536285Sbrian  dl->physical = iov2modem(dl, iov, niov, maxiov, fd);
102636285Sbrian
102736285Sbrian  if (!dl->physical) {
102836285Sbrian    free(dl->name);
102936285Sbrian    free(dl);
103036285Sbrian    dl = NULL;
103136285Sbrian  } else {
103236285Sbrian    chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
103336285Sbrian
103436285Sbrian    log_Printf(LogPHASE, "%s: Transferred in %s state\n",
103536285Sbrian              dl->name, datalink_State(dl));
103636285Sbrian  }
103736285Sbrian
103836285Sbrian  return dl;
103936285Sbrian}
104036285Sbrian
104136285Sbrianint
104236450Sbriandatalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
104336450Sbrian             pid_t newpid)
104436285Sbrian{
104536285Sbrian  /* If `dl' is NULL, we're allocating before a Fromiov() */
104636285Sbrian  int link_fd;
104736285Sbrian
104836285Sbrian  if (dl) {
104936285Sbrian    timer_Stop(&dl->dial_timer);
105036285Sbrian    timer_Stop(&dl->pap.authtimer);
105136285Sbrian    timer_Stop(&dl->chap.auth.authtimer);
105236285Sbrian  }
105336285Sbrian
105436285Sbrian  if (*niov >= maxiov - 1) {
105536285Sbrian    log_Printf(LogERROR, "Toiov: No room for datalink !\n");
105636285Sbrian    if (dl) {
105736285Sbrian      free(dl->name);
105836285Sbrian      free(dl);
105936285Sbrian    }
106036285Sbrian    return -1;
106136285Sbrian  }
106236285Sbrian
106336285Sbrian  iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl);
106436285Sbrian  iov[(*niov)++].iov_len = sizeof *dl;
106536285Sbrian  iov[*niov].iov_base =
106636285Sbrian    dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
106736285Sbrian  iov[(*niov)++].iov_len = DATALINK_MAXNAME;
106836285Sbrian
106936450Sbrian  link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid);
107036285Sbrian
107136285Sbrian  if (link_fd == -1 && dl) {
107236285Sbrian    free(dl->name);
107336285Sbrian    free(dl);
107436285Sbrian  }
107536285Sbrian
107636285Sbrian  return link_fd;
107736285Sbrian}
107836285Sbrian
107936285Sbrianvoid
108036285Sbriandatalink_Rename(struct datalink *dl, const char *name)
108136285Sbrian{
108236285Sbrian  free(dl->name);
108336285Sbrian  dl->physical->link.name = dl->name = strdup(name);
108436285Sbrian}
108536285Sbrian
108636285Sbrianchar *
108736285Sbriandatalink_NextName(struct datalink *dl)
108836285Sbrian{
108936285Sbrian  int f, n;
109036285Sbrian  char *name, *oname;
109136285Sbrian
109236285Sbrian  n = strlen(dl->name);
109336285Sbrian  name = (char *)malloc(n+3);
109436285Sbrian  for (f = n - 1; f >= 0; f--)
109536285Sbrian    if (!isdigit(dl->name[f]))
109636285Sbrian      break;
109736285Sbrian  n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
109836285Sbrian  sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
109936285Sbrian  oname = dl->name;
110036345Sbrian  dl->name = name;
110136345Sbrian  /* our physical link name isn't updated (it probably isn't created yet) */
110236285Sbrian  return oname;
110336285Sbrian}
110436285Sbrian
110536285Sbrianint
110636285Sbriandatalink_SetMode(struct datalink *dl, int mode)
110736285Sbrian{
110836285Sbrian  if (!physical_SetMode(dl->physical, mode))
110936285Sbrian    return 0;
111036285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
111136285Sbrian    dl->script.run = 0;
111236285Sbrian  if (dl->physical->type == PHYS_DIRECT)
111336285Sbrian    dl->reconnect_tries = 0;
111436465Sbrian  if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY)
111536285Sbrian    datalink_Up(dl, 1, 1);
111636285Sbrian  return 1;
111736285Sbrian}
1118