datalink.c revision 53830
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 *
2650479Speter * $FreeBSD: head/usr.sbin/ppp/datalink.c 53830 1999-11-28 15:50:08Z brian $
2736285Sbrian */
2836285Sbrian
2943313Sbrian#include <sys/param.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
4246686Sbrian#include "layer.h"
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"
6243313Sbrian#ifndef NORADIUS
6343313Sbrian#include "radius.h"
6443313Sbrian#endif
6536285Sbrian#include "bundle.h"
6636285Sbrian#include "chat.h"
6736285Sbrian#include "auth.h"
6836285Sbrian#include "prompt.h"
6946686Sbrian#include "proto.h"
7036285Sbrian#include "pap.h"
7136285Sbrian#include "chap.h"
7236285Sbrian#include "command.h"
7338174Sbrian#include "cbcp.h"
7436285Sbrian#include "datalink.h"
7536285Sbrian
7636285Sbrianstatic void datalink_LoginDone(struct datalink *);
7736285Sbrianstatic void datalink_NewState(struct datalink *, int);
7836285Sbrian
7936285Sbrianstatic void
8036285Sbriandatalink_OpenTimeout(void *v)
8136285Sbrian{
8236285Sbrian  struct datalink *dl = (struct datalink *)v;
8336285Sbrian
8444468Sbrian  timer_Stop(&dl->dial.timer);
8536285Sbrian  if (dl->state == DATALINK_OPENING)
8636285Sbrian    log_Printf(LogPHASE, "%s: Redial timer expired.\n", dl->name);
8736285Sbrian}
8836285Sbrian
8944261Sbrianstatic int
9036285Sbriandatalink_StartDialTimer(struct datalink *dl, int Timeout)
9136285Sbrian{
9244261Sbrian  int result = Timeout;
9344261Sbrian
9444468Sbrian  timer_Stop(&dl->dial.timer);
9538174Sbrian  if (Timeout) {
9636285Sbrian    if (Timeout > 0)
9744468Sbrian      dl->dial.timer.load = Timeout * SECTICKS;
9844261Sbrian    else {
9944261Sbrian      result = (random() % DIAL_TIMEOUT) + 1;
10044468Sbrian      dl->dial.timer.load = result * SECTICKS;
10144261Sbrian    }
10244468Sbrian    dl->dial.timer.func = datalink_OpenTimeout;
10344468Sbrian    dl->dial.timer.name = "dial";
10444468Sbrian    dl->dial.timer.arg = dl;
10544468Sbrian    timer_Start(&dl->dial.timer);
10636285Sbrian    if (dl->state == DATALINK_OPENING)
10736285Sbrian      log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
10836285Sbrian                dl->name, Timeout);
10936285Sbrian  }
11044261Sbrian  return result;
11136285Sbrian}
11236285Sbrian
11336285Sbrianstatic void
11436285Sbriandatalink_HangupDone(struct datalink *dl)
11536285Sbrian{
11636285Sbrian  if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
11746686Sbrian      dl->physical->fd != -1) {
11846686Sbrian    /* Don't close our device if the link is dedicated */
11936285Sbrian    datalink_LoginDone(dl);
12036285Sbrian    return;
12136285Sbrian  }
12236285Sbrian
12352488Sbrian  chat_Destroy(&dl->chat);
12446686Sbrian  physical_Close(dl->physical);
12536285Sbrian  dl->phone.chosen = "N/A";
12636285Sbrian
12738174Sbrian  if (dl->cbcp.required) {
12838174Sbrian    log_Printf(LogPHASE, "Call peer back on %s\n", dl->cbcp.fsm.phone);
12938174Sbrian    dl->cfg.callback.opmask = 0;
13038174Sbrian    strncpy(dl->cfg.phone.list, dl->cbcp.fsm.phone,
13138174Sbrian            sizeof dl->cfg.phone.list - 1);
13238174Sbrian    dl->cfg.phone.list[sizeof dl->cfg.phone.list - 1] = '\0';
13338174Sbrian    dl->phone.alt = dl->phone.next = NULL;
13438174Sbrian    dl->reconnect_tries = dl->cfg.reconnect.max;
13544468Sbrian    dl->dial.tries = dl->cfg.dial.max;
13644468Sbrian    dl->dial.incs = 0;
13738174Sbrian    dl->script.run = 1;
13838174Sbrian    dl->script.packetmode = 1;
13938174Sbrian    if (!physical_SetMode(dl->physical, PHYS_BACKGROUND))
14038174Sbrian      log_Printf(LogERROR, "Oops - can't change mode to BACKGROUND (gulp) !\n");
14138174Sbrian    bundle_LinksRemoved(dl->bundle);
14244468Sbrian    /* if dial.timeout is < 0 (random), we don't override fsm.delay */
14338174Sbrian    if (dl->cbcp.fsm.delay < dl->cfg.dial.timeout)
14438174Sbrian      dl->cbcp.fsm.delay = dl->cfg.dial.timeout;
14538174Sbrian    datalink_StartDialTimer(dl, dl->cbcp.fsm.delay);
14638174Sbrian    cbcp_Down(&dl->cbcp);
14738174Sbrian    datalink_NewState(dl, DATALINK_OPENING);
14852412Sbrian    if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
14952412Sbrian        bundle_Phase(dl->bundle) == PHASE_TERMINATE)
15045385Sbrian      bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
15138174Sbrian  } else if (dl->bundle->CleaningUp ||
15236285Sbrian      (dl->physical->type == PHYS_DIRECT) ||
15344468Sbrian      ((!dl->dial.tries || (dl->dial.tries < 0 && !dl->reconnect_tries)) &&
15436465Sbrian       !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
15536285Sbrian    datalink_NewState(dl, DATALINK_CLOSED);
15644468Sbrian    dl->dial.tries = -1;
15744468Sbrian    dl->dial.incs = 0;
15836285Sbrian    dl->reconnect_tries = 0;
15936285Sbrian    bundle_LinkClosed(dl->bundle, dl);
16036285Sbrian    if (!dl->bundle->CleaningUp)
16144468Sbrian      datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
16236285Sbrian  } else {
16336285Sbrian    datalink_NewState(dl, DATALINK_OPENING);
16452412Sbrian    if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
16552412Sbrian        bundle_Phase(dl->bundle) == PHASE_TERMINATE)
16645385Sbrian      bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
16744468Sbrian    if (dl->dial.tries < 0) {
16836285Sbrian      datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
16944468Sbrian      dl->dial.tries = dl->cfg.dial.max;
17044468Sbrian      dl->dial.incs = 0;
17136285Sbrian      dl->reconnect_tries--;
17236285Sbrian    } else {
17336285Sbrian      if (dl->phone.next == NULL)
17444468Sbrian        datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
17536285Sbrian      else
17636285Sbrian        datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
17736285Sbrian    }
17836285Sbrian  }
17936285Sbrian}
18036285Sbrian
18149472Sbrianconst char *
18236285Sbriandatalink_ChoosePhoneNumber(struct datalink *dl)
18336285Sbrian{
18436285Sbrian  char *phone;
18536285Sbrian
18636285Sbrian  if (dl->phone.alt == NULL) {
18736285Sbrian    if (dl->phone.next == NULL) {
18836285Sbrian      strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
18936285Sbrian      dl->phone.list[sizeof dl->phone.list - 1] = '\0';
19048003Sbrian      if (*dl->phone.list == '\0')
19148003Sbrian        return "";
19236285Sbrian      dl->phone.next = dl->phone.list;
19336285Sbrian    }
19436285Sbrian    dl->phone.alt = strsep(&dl->phone.next, ":");
19536285Sbrian  }
19636285Sbrian  phone = strsep(&dl->phone.alt, "|");
19736285Sbrian  dl->phone.chosen = *phone ? phone : "[NONE]";
19836285Sbrian  if (*phone)
19936285Sbrian    log_Printf(LogPHASE, "Phone: %s\n", phone);
20036285Sbrian  return phone;
20136285Sbrian}
20236285Sbrian
20336285Sbrianstatic void
20436285Sbriandatalink_LoginDone(struct datalink *dl)
20536285Sbrian{
20652488Sbrian  chat_Destroy(&dl->chat);
20752488Sbrian
20836285Sbrian  if (!dl->script.packetmode) {
20944468Sbrian    dl->dial.tries = -1;
21044468Sbrian    dl->dial.incs = 0;
21136285Sbrian    datalink_NewState(dl, DATALINK_READY);
21246686Sbrian  } else if (!physical_Raw(dl->physical)) {
21344468Sbrian    dl->dial.tries = 0;
21436285Sbrian    log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
21536285Sbrian    if (dl->script.run) {
21652488Sbrian      datalink_NewState(dl, DATALINK_LOGOUT);
21752488Sbrian      chat_Init(&dl->chat, dl->physical, dl->cfg.script.logout, NULL);
21836285Sbrian    } else {
21947061Sbrian      physical_StopDeviceTimer(dl->physical);
22036285Sbrian      if (dl->physical->type == PHYS_DEDICATED)
22136285Sbrian        /* force a redial timeout */
22246686Sbrian        physical_Close(dl->physical);
22336285Sbrian      datalink_HangupDone(dl);
22436285Sbrian    }
22536285Sbrian  } else {
22644468Sbrian    dl->dial.tries = -1;
22744468Sbrian    dl->dial.incs = 0;
22836285Sbrian
22936285Sbrian    hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
23036285Sbrian    async_Init(&dl->physical->async);
23136285Sbrian
23236285Sbrian    lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
23336285Sbrian              0 : dl->physical->link.lcp.cfg.openmode);
23436285Sbrian    ccp_Setup(&dl->physical->link.ccp);
23536285Sbrian
23636285Sbrian    datalink_NewState(dl, DATALINK_LCP);
23736285Sbrian    fsm_Up(&dl->physical->link.lcp.fsm);
23836285Sbrian    fsm_Open(&dl->physical->link.lcp.fsm);
23936285Sbrian  }
24036285Sbrian}
24136285Sbrian
24236285Sbrianstatic int
24336285Sbriandatalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
24436285Sbrian                   int *n)
24536285Sbrian{
24636285Sbrian  struct datalink *dl = descriptor2datalink(d);
24736285Sbrian  int result;
24836285Sbrian
24936285Sbrian  result = 0;
25036285Sbrian  switch (dl->state) {
25136285Sbrian    case DATALINK_CLOSED:
25253830Sbrian      if ((dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|
25353830Sbrian                                 PHYS_FOREGROUND|PHYS_DDIAL)) &&
25447863Sbrian          !dl->bundle->CleaningUp)
25536285Sbrian        /*
25636465Sbrian         * Our first time in - DEDICATED & DDIAL never come down, and
25753830Sbrian         * DIRECT, FOREGROUND & BACKGROUND get deleted when they enter
25853830Sbrian         * DATALINK_CLOSED.  Go to DATALINK_OPENING via datalink_Up()
25953830Sbrian         * and fall through.
26036285Sbrian         */
26136285Sbrian        datalink_Up(dl, 1, 1);
26236285Sbrian      else
26336285Sbrian        break;
26436285Sbrian      /* fall through */
26536285Sbrian
26636285Sbrian    case DATALINK_OPENING:
26744468Sbrian      if (dl->dial.timer.state != TIMER_RUNNING) {
26844468Sbrian        if (--dl->dial.tries < 0)
26944468Sbrian          dl->dial.tries = 0;
27046686Sbrian        if (physical_Open(dl->physical, dl->bundle) >= 0) {
27138200Sbrian          log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
27238200Sbrian                           "Type `~?' for help\r\n", dl->name,
27338200Sbrian                           dl->physical->name.full);
27436285Sbrian          if (dl->script.run) {
27536285Sbrian            datalink_NewState(dl, DATALINK_DIAL);
27652488Sbrian            chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial,
27749472Sbrian                      *dl->cfg.script.dial ?
27849472Sbrian                      datalink_ChoosePhoneNumber(dl) : "");
27936465Sbrian            if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
28036285Sbrian                dl->cfg.dial.max)
28136285Sbrian              log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
28244468Sbrian                        dl->name, dl->cfg.dial.max - dl->dial.tries,
28336285Sbrian                        dl->cfg.dial.max);
28436285Sbrian          } else
28551978Sbrian            datalink_NewState(dl, DATALINK_CARRIER);
28641830Sbrian          return datalink_UpdateSet(d, r, w, e, n);
28736285Sbrian        } else {
28836465Sbrian          if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
28936285Sbrian              dl->cfg.dial.max)
29046686Sbrian            log_Printf(LogCHAT, "Failed to open device (attempt %u of %d)\n",
29144468Sbrian                       dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
29236285Sbrian          else
29346686Sbrian            log_Printf(LogCHAT, "Failed to open device\n");
29436285Sbrian
29536285Sbrian          if (dl->bundle->CleaningUp ||
29636465Sbrian              (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
29744468Sbrian               dl->cfg.dial.max && dl->dial.tries == 0)) {
29836285Sbrian            datalink_NewState(dl, DATALINK_CLOSED);
29936285Sbrian            dl->reconnect_tries = 0;
30044468Sbrian            dl->dial.tries = -1;
30138200Sbrian            log_WritePrompts(dl, "Failed to open %s\n",
30238200Sbrian                             dl->physical->name.full);
30336285Sbrian            bundle_LinkClosed(dl->bundle, dl);
30436285Sbrian          }
30538200Sbrian          if (!dl->bundle->CleaningUp) {
30644468Sbrian            int timeout;
30744468Sbrian
30844468Sbrian            timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
30938200Sbrian            log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
31044261Sbrian                             dl->physical->name.full, timeout);
31138200Sbrian          }
31236285Sbrian        }
31336285Sbrian      }
31436285Sbrian      break;
31536285Sbrian
31649472Sbrian    case DATALINK_CARRIER:
31749472Sbrian      /* Wait for carrier on the device */
31849472Sbrian      switch (physical_AwaitCarrier(dl->physical)) {
31949472Sbrian        case CARRIER_PENDING:
32049472Sbrian          log_Printf(LogDEBUG, "Waiting for carrier\n");
32149472Sbrian          return 0;	/* A device timer is running to wake us up again */
32249472Sbrian
32349472Sbrian        case CARRIER_OK:
32451978Sbrian          if (dl->script.run) {
32551978Sbrian            datalink_NewState(dl, DATALINK_LOGIN);
32652488Sbrian            chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, NULL);
32751978Sbrian          } else
32851978Sbrian            datalink_LoginDone(dl);
32949472Sbrian          return datalink_UpdateSet(d, r, w, e, n);
33049472Sbrian
33149472Sbrian        case CARRIER_LOST:
33249472Sbrian          physical_Offline(dl->physical);	/* Is this required ? */
33351978Sbrian          if (dl->script.run) {
33451978Sbrian            datalink_NewState(dl, DATALINK_HANGUP);
33552488Sbrian            chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, NULL);
33653070Sbrian            return datalink_UpdateSet(d, r, w, e, n);
33753070Sbrian          } else {
33851978Sbrian            datalink_HangupDone(dl);
33953070Sbrian            return 0;	/* Maybe bundle_CleanDatalinks() has something to do */
34053070Sbrian          }
34149472Sbrian      }
34249472Sbrian
34336285Sbrian    case DATALINK_HANGUP:
34436285Sbrian    case DATALINK_DIAL:
34552488Sbrian    case DATALINK_LOGOUT:
34636285Sbrian    case DATALINK_LOGIN:
34736285Sbrian      result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
34836285Sbrian      switch (dl->chat.state) {
34936285Sbrian        case CHAT_DONE:
35036285Sbrian          /* script succeeded */
35136285Sbrian          switch(dl->state) {
35236285Sbrian            case DATALINK_HANGUP:
35336285Sbrian              datalink_HangupDone(dl);
35436285Sbrian              break;
35536285Sbrian            case DATALINK_DIAL:
35649472Sbrian              datalink_NewState(dl, DATALINK_CARRIER);
35736285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
35852488Sbrian            case DATALINK_LOGOUT:
35952488Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
36052488Sbrian              physical_Offline(dl->physical);
36152488Sbrian              chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, NULL);
36252488Sbrian              return datalink_UpdateSet(d, r, w, e, n);
36336285Sbrian            case DATALINK_LOGIN:
36442390Sbrian              dl->phone.alt = NULL;
36536285Sbrian              datalink_LoginDone(dl);
36642905Sbrian              return datalink_UpdateSet(d, r, w, e, n);
36736285Sbrian          }
36836285Sbrian          break;
36936285Sbrian        case CHAT_FAILED:
37036285Sbrian          /* Going down - script failed */
37136285Sbrian          log_Printf(LogWARN, "Chat script failed\n");
37236285Sbrian          switch(dl->state) {
37336285Sbrian            case DATALINK_HANGUP:
37436285Sbrian              datalink_HangupDone(dl);
37536285Sbrian              break;
37636285Sbrian            case DATALINK_DIAL:
37752488Sbrian            case DATALINK_LOGOUT:
37836285Sbrian            case DATALINK_LOGIN:
37936285Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
38052488Sbrian              physical_Offline(dl->physical);
38152488Sbrian              chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, NULL);
38236285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
38336285Sbrian          }
38436285Sbrian          break;
38536285Sbrian      }
38636285Sbrian      break;
38736285Sbrian
38836285Sbrian    case DATALINK_READY:
38936285Sbrian    case DATALINK_LCP:
39036285Sbrian    case DATALINK_AUTH:
39138174Sbrian    case DATALINK_CBCP:
39236285Sbrian    case DATALINK_OPEN:
39343888Sbrian      result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
39443888Sbrian               descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
39536285Sbrian      break;
39636285Sbrian  }
39736285Sbrian  return result;
39836285Sbrian}
39936285Sbrian
40036285Sbrianint
40136285Sbriandatalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
40236285Sbrian{
40336285Sbrian  return physical_RemoveFromSet(dl->physical, r, w, e);
40436285Sbrian}
40536285Sbrian
40636285Sbrianstatic int
40736285Sbriandatalink_IsSet(struct descriptor *d, const fd_set *fdset)
40836285Sbrian{
40936285Sbrian  struct datalink *dl = descriptor2datalink(d);
41036285Sbrian
41136285Sbrian  switch (dl->state) {
41236285Sbrian    case DATALINK_CLOSED:
41336285Sbrian    case DATALINK_OPENING:
41436285Sbrian      break;
41536285Sbrian
41636285Sbrian    case DATALINK_HANGUP:
41736285Sbrian    case DATALINK_DIAL:
41852488Sbrian    case DATALINK_LOGOUT:
41936285Sbrian    case DATALINK_LOGIN:
42036285Sbrian      return descriptor_IsSet(&dl->chat.desc, fdset);
42136285Sbrian
42236285Sbrian    case DATALINK_READY:
42336285Sbrian    case DATALINK_LCP:
42436285Sbrian    case DATALINK_AUTH:
42538174Sbrian    case DATALINK_CBCP:
42636285Sbrian    case DATALINK_OPEN:
42743888Sbrian      return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
42843888Sbrian             descriptor_IsSet(&dl->physical->desc, fdset);
42936285Sbrian  }
43036285Sbrian  return 0;
43136285Sbrian}
43236285Sbrian
43336285Sbrianstatic void
43436285Sbriandatalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
43536285Sbrian{
43636285Sbrian  struct datalink *dl = descriptor2datalink(d);
43736285Sbrian
43836285Sbrian  switch (dl->state) {
43936285Sbrian    case DATALINK_CLOSED:
44036285Sbrian    case DATALINK_OPENING:
44136285Sbrian      break;
44236285Sbrian
44336285Sbrian    case DATALINK_HANGUP:
44436285Sbrian    case DATALINK_DIAL:
44552488Sbrian    case DATALINK_LOGOUT:
44636285Sbrian    case DATALINK_LOGIN:
44736285Sbrian      descriptor_Read(&dl->chat.desc, bundle, fdset);
44836285Sbrian      break;
44936285Sbrian
45036285Sbrian    case DATALINK_READY:
45136285Sbrian    case DATALINK_LCP:
45236285Sbrian    case DATALINK_AUTH:
45338174Sbrian    case DATALINK_CBCP:
45436285Sbrian    case DATALINK_OPEN:
45543888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
45643888Sbrian        descriptor_Read(&dl->chap.desc, bundle, fdset);
45743888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
45843888Sbrian        descriptor_Read(&dl->physical->desc, bundle, fdset);
45936285Sbrian      break;
46036285Sbrian  }
46136285Sbrian}
46236285Sbrian
46337141Sbrianstatic int
46436285Sbriandatalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
46536285Sbrian{
46636285Sbrian  struct datalink *dl = descriptor2datalink(d);
46737141Sbrian  int result = 0;
46836285Sbrian
46936285Sbrian  switch (dl->state) {
47036285Sbrian    case DATALINK_CLOSED:
47136285Sbrian    case DATALINK_OPENING:
47236285Sbrian      break;
47336285Sbrian
47436285Sbrian    case DATALINK_HANGUP:
47536285Sbrian    case DATALINK_DIAL:
47652488Sbrian    case DATALINK_LOGOUT:
47736285Sbrian    case DATALINK_LOGIN:
47837141Sbrian      result = descriptor_Write(&dl->chat.desc, bundle, fdset);
47936285Sbrian      break;
48036285Sbrian
48136285Sbrian    case DATALINK_READY:
48236285Sbrian    case DATALINK_LCP:
48336285Sbrian    case DATALINK_AUTH:
48438174Sbrian    case DATALINK_CBCP:
48536285Sbrian    case DATALINK_OPEN:
48643888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
48743888Sbrian        result += descriptor_Write(&dl->chap.desc, bundle, fdset);
48843888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
48943888Sbrian        result += descriptor_Write(&dl->physical->desc, bundle, fdset);
49036285Sbrian      break;
49136285Sbrian  }
49237141Sbrian
49337141Sbrian  return result;
49436285Sbrian}
49536285Sbrian
49636285Sbrianstatic void
49737007Sbriandatalink_ComeDown(struct datalink *dl, int how)
49836285Sbrian{
49937007Sbrian  if (how != CLOSE_NORMAL) {
50044468Sbrian    dl->dial.tries = -1;
50136285Sbrian    dl->reconnect_tries = 0;
50237012Sbrian    if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
50337007Sbrian      dl->stayonline = 1;
50436285Sbrian  }
50536285Sbrian
50637012Sbrian  if (dl->state >= DATALINK_READY && dl->stayonline) {
50737007Sbrian    dl->stayonline = 0;
50847061Sbrian    physical_StopDeviceTimer(dl->physical);
50937007Sbrian    datalink_NewState(dl, DATALINK_READY);
51037007Sbrian  } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
51146686Sbrian    physical_Offline(dl->physical);
51236285Sbrian    if (dl->script.run && dl->state != DATALINK_OPENING) {
51352488Sbrian      if (dl->state == DATALINK_LOGOUT) {
51452488Sbrian        datalink_NewState(dl, DATALINK_HANGUP);
51552488Sbrian        chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, NULL);
51652488Sbrian      } else {
51752488Sbrian        datalink_NewState(dl, DATALINK_LOGOUT);
51852488Sbrian        chat_Init(&dl->chat, dl->physical, dl->cfg.script.logout, NULL);
51952488Sbrian      }
52036285Sbrian    } else
52136285Sbrian      datalink_HangupDone(dl);
52236285Sbrian  }
52336285Sbrian}
52436285Sbrian
52536285Sbrianstatic void
52636285Sbriandatalink_LayerStart(void *v, struct fsm *fp)
52736285Sbrian{
52836285Sbrian  /* The given FSM is about to start up ! */
52936285Sbrian  struct datalink *dl = (struct datalink *)v;
53036285Sbrian
53136285Sbrian  if (fp->proto == PROTO_LCP)
53236285Sbrian    (*dl->parent->LayerStart)(dl->parent->object, fp);
53336285Sbrian}
53436285Sbrian
53536285Sbrianstatic void
53636285Sbriandatalink_LayerUp(void *v, struct fsm *fp)
53736285Sbrian{
53836285Sbrian  /* The given fsm is now up */
53936285Sbrian  struct datalink *dl = (struct datalink *)v;
54044106Sbrian  struct lcp *lcp = &dl->physical->link.lcp;
54136285Sbrian
54236285Sbrian  if (fp->proto == PROTO_LCP) {
54343693Sbrian    datalink_GotAuthname(dl, "");
54444106Sbrian    lcp->auth_ineed = lcp->want_auth;
54544106Sbrian    lcp->auth_iwait = lcp->his_auth;
54644106Sbrian    if (lcp->his_auth || lcp->want_auth) {
54745350Sbrian      if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
54836285Sbrian        bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
54936285Sbrian      log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
55044106Sbrian                Auth2Nam(lcp->his_auth, lcp->his_authtype),
55144106Sbrian                Auth2Nam(lcp->want_auth, lcp->want_authtype));
55244106Sbrian      if (lcp->his_auth == PROTO_PAP)
55343693Sbrian        auth_StartReq(&dl->pap);
55444106Sbrian      if (lcp->want_auth == PROTO_CHAP)
55543693Sbrian        auth_StartReq(&dl->chap.auth);
55636285Sbrian    } else
55736285Sbrian      datalink_AuthOk(dl);
55836285Sbrian  }
55936285Sbrian}
56036285Sbrian
56144094Sbrianstatic void
56244094Sbriandatalink_AuthReInit(struct datalink *dl)
56344094Sbrian{
56444094Sbrian  auth_StopTimer(&dl->pap);
56544094Sbrian  auth_StopTimer(&dl->chap.auth);
56644094Sbrian  chap_ReInit(&dl->chap);
56744094Sbrian}
56844094Sbrian
56936285Sbrianvoid
57043693Sbriandatalink_GotAuthname(struct datalink *dl, const char *name)
57136285Sbrian{
57243693Sbrian  strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
57343693Sbrian  dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
57436285Sbrian}
57536285Sbrian
57636285Sbrianvoid
57738174Sbriandatalink_NCPUp(struct datalink *dl)
57836285Sbrian{
57937320Sbrian  int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
58036310Sbrian
58136285Sbrian  if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
58236285Sbrian    /* we've authenticated in multilink mode ! */
58336285Sbrian    switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
58436285Sbrian      case MP_LINKSENT:
58536285Sbrian        /* We've handed the link off to another ppp (well, we will soon) ! */
58636285Sbrian        return;
58736285Sbrian      case MP_UP:
58836285Sbrian        /* First link in the bundle */
58938174Sbrian        auth_Select(dl->bundle, dl->peer.authname);
59049434Sbrian        bundle_CalculateBandwidth(dl->bundle);
59136285Sbrian        /* fall through */
59236285Sbrian      case MP_ADDED:
59336285Sbrian        /* We're in multilink mode ! */
59436310Sbrian        dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
59549434Sbrian        bundle_CalculateBandwidth(dl->bundle);
59636285Sbrian        break;
59736285Sbrian      case MP_FAILED:
59836285Sbrian        datalink_AuthNotOk(dl);
59936285Sbrian        return;
60036285Sbrian    }
60136285Sbrian  } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
60236285Sbrian    log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
60337160Sbrian    datalink_NewState(dl, DATALINK_OPEN);
60449434Sbrian    bundle_CalculateBandwidth(dl->bundle);
60537160Sbrian    (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
60636285Sbrian    return;
60736285Sbrian  } else {
60836285Sbrian    dl->bundle->ncp.mp.peer = dl->peer;
60936285Sbrian    ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
61038174Sbrian    auth_Select(dl->bundle, dl->peer.authname);
61136285Sbrian  }
61236285Sbrian
61337320Sbrian  if (ccpok) {
61437320Sbrian    fsm_Up(&dl->physical->link.ccp.fsm);
61537320Sbrian    fsm_Open(&dl->physical->link.ccp.fsm);
61637320Sbrian  }
61736285Sbrian  datalink_NewState(dl, DATALINK_OPEN);
61836285Sbrian  bundle_NewPhase(dl->bundle, PHASE_NETWORK);
61936285Sbrian  (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
62036285Sbrian}
62136285Sbrian
62236285Sbrianvoid
62338174Sbriandatalink_CBCPComplete(struct datalink *dl)
62438174Sbrian{
62538174Sbrian  datalink_NewState(dl, DATALINK_LCP);
62644094Sbrian  datalink_AuthReInit(dl);
62738174Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
62838174Sbrian}
62938174Sbrian
63038174Sbrianvoid
63138174Sbriandatalink_CBCPFailed(struct datalink *dl)
63238174Sbrian{
63338174Sbrian  cbcp_Down(&dl->cbcp);
63438174Sbrian  datalink_CBCPComplete(dl);
63538174Sbrian}
63638174Sbrian
63738174Sbrianvoid
63838174Sbriandatalink_AuthOk(struct datalink *dl)
63938174Sbrian{
64042600Sbrian  if ((dl->physical->link.lcp.his_callback.opmask &
64142600Sbrian       CALLBACK_BIT(CALLBACK_CBCP) ||
64242600Sbrian       dl->physical->link.lcp.want_callback.opmask &
64342600Sbrian       CALLBACK_BIT(CALLBACK_CBCP)) &&
64442600Sbrian      !(dl->physical->link.lcp.want_callback.opmask &
64542600Sbrian        CALLBACK_BIT(CALLBACK_AUTH))) {
64642600Sbrian    /* We must have agreed CBCP if AUTH isn't there any more */
64738174Sbrian    datalink_NewState(dl, DATALINK_CBCP);
64838174Sbrian    cbcp_Up(&dl->cbcp);
64938174Sbrian  } else if (dl->physical->link.lcp.want_callback.opmask) {
65042600Sbrian    /* It's not CBCP */
65138174Sbrian    log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
65238174Sbrian    datalink_NewState(dl, DATALINK_LCP);
65344094Sbrian    datalink_AuthReInit(dl);
65438174Sbrian    fsm_Close(&dl->physical->link.lcp.fsm);
65538174Sbrian  } else
65638174Sbrian    switch (dl->physical->link.lcp.his_callback.opmask) {
65738174Sbrian      case 0:
65838174Sbrian        datalink_NCPUp(dl);
65938174Sbrian        break;
66038174Sbrian
66138174Sbrian      case CALLBACK_BIT(CALLBACK_AUTH):
66238174Sbrian        auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
66338174Sbrian                          sizeof dl->cbcp.fsm.phone);
66438174Sbrian        if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
66538174Sbrian          log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
66638174Sbrian                     dl->peer.authname);
66738174Sbrian          *dl->cbcp.fsm.phone = '\0';
66838174Sbrian        } else {
66938174Sbrian          char *ptr = strchr(dl->cbcp.fsm.phone, ',');
67038174Sbrian          if (ptr)
67138174Sbrian            *ptr = '\0';	/* Call back on the first number */
67238174Sbrian          log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
67338174Sbrian                     dl->cbcp.fsm.phone);
67438174Sbrian          dl->cbcp.required = 1;
67538174Sbrian        }
67638174Sbrian        dl->cbcp.fsm.delay = 0;
67738174Sbrian        datalink_NewState(dl, DATALINK_LCP);
67844094Sbrian        datalink_AuthReInit(dl);
67938174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
68038174Sbrian        break;
68138174Sbrian
68238174Sbrian      case CALLBACK_BIT(CALLBACK_E164):
68338174Sbrian        strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
68438174Sbrian                sizeof dl->cbcp.fsm.phone - 1);
68538174Sbrian        dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
68638174Sbrian        log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
68738174Sbrian                   dl->cbcp.fsm.phone);
68838174Sbrian        dl->cbcp.required = 1;
68938174Sbrian        dl->cbcp.fsm.delay = 0;
69038174Sbrian        datalink_NewState(dl, DATALINK_LCP);
69144094Sbrian        datalink_AuthReInit(dl);
69238174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
69338174Sbrian        break;
69438174Sbrian
69538174Sbrian      default:
69638174Sbrian        log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
69738174Sbrian                   dl->name);
69838174Sbrian        datalink_NewState(dl, DATALINK_LCP);
69944094Sbrian        datalink_AuthReInit(dl);
70038174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
70138174Sbrian        break;
70238174Sbrian    }
70338174Sbrian}
70438174Sbrian
70538174Sbrianvoid
70636285Sbriandatalink_AuthNotOk(struct datalink *dl)
70736285Sbrian{
70836285Sbrian  datalink_NewState(dl, DATALINK_LCP);
70944094Sbrian  datalink_AuthReInit(dl);
71036285Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
71136285Sbrian}
71236285Sbrian
71336285Sbrianstatic void
71436285Sbriandatalink_LayerDown(void *v, struct fsm *fp)
71536285Sbrian{
71636285Sbrian  /* The given FSM has been told to come down */
71736285Sbrian  struct datalink *dl = (struct datalink *)v;
71836285Sbrian
71936285Sbrian  if (fp->proto == PROTO_LCP) {
72036285Sbrian    switch (dl->state) {
72136285Sbrian      case DATALINK_OPEN:
72236285Sbrian        peerid_Init(&dl->peer);
72337060Sbrian        fsm2initial(&dl->physical->link.ccp.fsm);
72436928Sbrian        datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
72536285Sbrian        (*dl->parent->LayerDown)(dl->parent->object, fp);
72638174Sbrian        /* fall through (just in case) */
72736285Sbrian
72838174Sbrian      case DATALINK_CBCP:
72938174Sbrian        if (!dl->cbcp.required)
73038174Sbrian          cbcp_Down(&dl->cbcp);
73138174Sbrian        /* fall through (just in case) */
73238174Sbrian
73336285Sbrian      case DATALINK_AUTH:
73436285Sbrian        timer_Stop(&dl->pap.authtimer);
73536285Sbrian        timer_Stop(&dl->chap.auth.authtimer);
73636285Sbrian    }
73736285Sbrian    datalink_NewState(dl, DATALINK_LCP);
73844094Sbrian    datalink_AuthReInit(dl);
73936285Sbrian  }
74036285Sbrian}
74136285Sbrian
74236285Sbrianstatic void
74336285Sbriandatalink_LayerFinish(void *v, struct fsm *fp)
74436285Sbrian{
74536285Sbrian  /* The given fsm is now down */
74636285Sbrian  struct datalink *dl = (struct datalink *)v;
74736285Sbrian
74836285Sbrian  if (fp->proto == PROTO_LCP) {
74937060Sbrian    fsm2initial(fp);
75036285Sbrian    (*dl->parent->LayerFinish)(dl->parent->object, fp);
75137007Sbrian    datalink_ComeDown(dl, CLOSE_NORMAL);
75236285Sbrian  } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
75336285Sbrian    fsm_Open(fp);		/* CCP goes to ST_STOPPED */
75436285Sbrian}
75536285Sbrian
75636285Sbrianstruct datalink *
75736285Sbriandatalink_Create(const char *name, struct bundle *bundle, int type)
75836285Sbrian{
75936285Sbrian  struct datalink *dl;
76036285Sbrian
76136285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
76236285Sbrian  if (dl == NULL)
76336285Sbrian    return dl;
76436285Sbrian
76536285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
76636285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
76736285Sbrian  dl->desc.IsSet = datalink_IsSet;
76836285Sbrian  dl->desc.Read = datalink_Read;
76936285Sbrian  dl->desc.Write = datalink_Write;
77036285Sbrian
77136285Sbrian  dl->state = DATALINK_CLOSED;
77236285Sbrian
77336285Sbrian  *dl->cfg.script.dial = '\0';
77436285Sbrian  *dl->cfg.script.login = '\0';
77552488Sbrian  *dl->cfg.script.logout = '\0';
77636285Sbrian  *dl->cfg.script.hangup = '\0';
77736285Sbrian  *dl->cfg.phone.list = '\0';
77836285Sbrian  *dl->phone.list = '\0';
77936285Sbrian  dl->phone.next = NULL;
78036285Sbrian  dl->phone.alt = NULL;
78136285Sbrian  dl->phone.chosen = "N/A";
78237007Sbrian  dl->stayonline = 0;
78336285Sbrian  dl->script.run = 1;
78436285Sbrian  dl->script.packetmode = 1;
78536285Sbrian  mp_linkInit(&dl->mp);
78636285Sbrian
78736285Sbrian  dl->bundle = bundle;
78836285Sbrian  dl->next = NULL;
78936285Sbrian
79044468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
79136285Sbrian
79244468Sbrian  dl->dial.tries = 0;
79336285Sbrian  dl->cfg.dial.max = 1;
79436285Sbrian  dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
79536285Sbrian  dl->cfg.dial.timeout = DIAL_TIMEOUT;
79644468Sbrian  dl->cfg.dial.inc = 0;
79744468Sbrian  dl->cfg.dial.maxinc = 10;
79836285Sbrian
79936285Sbrian  dl->reconnect_tries = 0;
80036285Sbrian  dl->cfg.reconnect.max = 0;
80136285Sbrian  dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
80236285Sbrian
80338174Sbrian  dl->cfg.callback.opmask = 0;
80438174Sbrian  dl->cfg.cbcp.delay = 0;
80538174Sbrian  *dl->cfg.cbcp.phone = '\0';
80638174Sbrian  dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
80738174Sbrian
80836285Sbrian  dl->name = strdup(name);
80936285Sbrian  peerid_Init(&dl->peer);
81036285Sbrian  dl->parent = &bundle->fsm;
81136285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
81236285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
81336285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
81436285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
81536285Sbrian  dl->fsmp.object = dl;
81636285Sbrian
81746686Sbrian  if ((dl->physical = physical_Create(dl, type)) == NULL) {
81836285Sbrian    free(dl->name);
81936285Sbrian    free(dl);
82036285Sbrian    return NULL;
82136285Sbrian  }
82243693Sbrian
82343693Sbrian  pap_Init(&dl->pap, dl->physical);
82443693Sbrian  chap_Init(&dl->chap, dl->physical);
82538174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
82636285Sbrian
82752488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
82852488Sbrian  chat_Init(&dl->chat, dl->physical, NULL, NULL);
82952488Sbrian
83036285Sbrian  log_Printf(LogPHASE, "%s: Created in %s state\n",
83136285Sbrian             dl->name, datalink_State(dl));
83236285Sbrian
83336285Sbrian  return dl;
83436285Sbrian}
83536285Sbrian
83636285Sbrianstruct datalink *
83736285Sbriandatalink_Clone(struct datalink *odl, const char *name)
83836285Sbrian{
83936285Sbrian  struct datalink *dl;
84036285Sbrian
84136285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
84236285Sbrian  if (dl == NULL)
84336285Sbrian    return dl;
84436285Sbrian
84536285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
84636285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
84736285Sbrian  dl->desc.IsSet = datalink_IsSet;
84836285Sbrian  dl->desc.Read = datalink_Read;
84936285Sbrian  dl->desc.Write = datalink_Write;
85036285Sbrian
85136285Sbrian  dl->state = DATALINK_CLOSED;
85236285Sbrian
85336285Sbrian  memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
85436285Sbrian  mp_linkInit(&dl->mp);
85536285Sbrian  *dl->phone.list = '\0';
85636285Sbrian  dl->phone.next = NULL;
85736285Sbrian  dl->phone.alt = NULL;
85836285Sbrian  dl->phone.chosen = "N/A";
85936285Sbrian  dl->bundle = odl->bundle;
86036285Sbrian  dl->next = NULL;
86144468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
86244468Sbrian  dl->dial.tries = 0;
86336285Sbrian  dl->reconnect_tries = 0;
86436285Sbrian  dl->name = strdup(name);
86536285Sbrian  peerid_Init(&dl->peer);
86636285Sbrian  dl->parent = odl->parent;
86736285Sbrian  memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
86836285Sbrian  dl->fsmp.object = dl;
86936285Sbrian
87046686Sbrian  if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
87136285Sbrian    free(dl->name);
87236285Sbrian    free(dl);
87336285Sbrian    return NULL;
87436285Sbrian  }
87543693Sbrian  pap_Init(&dl->pap, dl->physical);
87644305Sbrian  dl->pap.cfg = odl->pap.cfg;
87743693Sbrian
87843693Sbrian  chap_Init(&dl->chap, dl->physical);
87944305Sbrian  dl->chap.auth.cfg = odl->chap.auth.cfg;
88043693Sbrian
88136285Sbrian  memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
88236285Sbrian  memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
88336285Sbrian         sizeof dl->physical->link.lcp.cfg);
88436285Sbrian  memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
88536285Sbrian         sizeof dl->physical->link.ccp.cfg);
88636285Sbrian  memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
88736285Sbrian         sizeof dl->physical->async.cfg);
88836285Sbrian
88938174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
89036285Sbrian
89152488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
89252488Sbrian  chat_Init(&dl->chat, dl->physical, NULL, NULL);
89352488Sbrian
89436285Sbrian  log_Printf(LogPHASE, "%s: Cloned in %s state\n",
89536285Sbrian             dl->name, datalink_State(dl));
89636285Sbrian
89736285Sbrian  return dl;
89836285Sbrian}
89936285Sbrian
90036285Sbrianstruct datalink *
90136285Sbriandatalink_Destroy(struct datalink *dl)
90236285Sbrian{
90336285Sbrian  struct datalink *result;
90436285Sbrian
90536285Sbrian  if (dl->state != DATALINK_CLOSED) {
90636285Sbrian    log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
90736285Sbrian              datalink_State(dl));
90836285Sbrian    switch (dl->state) {
90936285Sbrian      case DATALINK_HANGUP:
91036285Sbrian      case DATALINK_DIAL:
91136285Sbrian      case DATALINK_LOGIN:
91236285Sbrian        chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
91336285Sbrian        break;
91436285Sbrian    }
91536285Sbrian  }
91636285Sbrian
91744468Sbrian  timer_Stop(&dl->dial.timer);
91836285Sbrian  result = dl->next;
91946686Sbrian  physical_Destroy(dl->physical);
92036285Sbrian  free(dl->name);
92136285Sbrian  free(dl);
92236285Sbrian
92336285Sbrian  return result;
92436285Sbrian}
92536285Sbrian
92636285Sbrianvoid
92736285Sbriandatalink_Up(struct datalink *dl, int runscripts, int packetmode)
92836285Sbrian{
92936285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
93036285Sbrian    /* Ignore scripts */
93136285Sbrian    runscripts = 0;
93236285Sbrian
93336285Sbrian  switch (dl->state) {
93436285Sbrian    case DATALINK_CLOSED:
93536285Sbrian      if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
93636285Sbrian          bundle_Phase(dl->bundle) == PHASE_TERMINATE)
93736285Sbrian        bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
93836285Sbrian      datalink_NewState(dl, DATALINK_OPENING);
93936285Sbrian      dl->reconnect_tries =
94036285Sbrian        dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
94144468Sbrian      dl->dial.tries = dl->cfg.dial.max;
94236285Sbrian      dl->script.run = runscripts;
94336285Sbrian      dl->script.packetmode = packetmode;
94436285Sbrian      break;
94536285Sbrian
94636285Sbrian    case DATALINK_OPENING:
94736285Sbrian      if (!dl->script.run && runscripts)
94836285Sbrian        dl->script.run = 1;
94936285Sbrian      /* fall through */
95036285Sbrian
95136285Sbrian    case DATALINK_DIAL:
95236285Sbrian    case DATALINK_LOGIN:
95336285Sbrian    case DATALINK_READY:
95436285Sbrian      if (!dl->script.packetmode && packetmode) {
95536285Sbrian        dl->script.packetmode = 1;
95636285Sbrian        if (dl->state == DATALINK_READY)
95736285Sbrian          datalink_LoginDone(dl);
95836285Sbrian      }
95936285Sbrian      break;
96036285Sbrian  }
96136285Sbrian}
96236285Sbrian
96336285Sbrianvoid
96437007Sbriandatalink_Close(struct datalink *dl, int how)
96536285Sbrian{
96636285Sbrian  /* Please close */
96736285Sbrian  switch (dl->state) {
96836285Sbrian    case DATALINK_OPEN:
96936285Sbrian      peerid_Init(&dl->peer);
97037060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
97136285Sbrian      /* fall through */
97236285Sbrian
97338174Sbrian    case DATALINK_CBCP:
97436285Sbrian    case DATALINK_AUTH:
97536285Sbrian    case DATALINK_LCP:
97644094Sbrian      datalink_AuthReInit(dl);
97736285Sbrian      fsm_Close(&dl->physical->link.lcp.fsm);
97837007Sbrian      if (how != CLOSE_NORMAL) {
97944468Sbrian        dl->dial.tries = -1;
98036285Sbrian        dl->reconnect_tries = 0;
98137007Sbrian        if (how == CLOSE_LCP)
98237007Sbrian          dl->stayonline = 1;
98336285Sbrian      }
98452029Sbrian      break;
98536285Sbrian
98636285Sbrian    default:
98737007Sbrian      datalink_ComeDown(dl, how);
98836285Sbrian  }
98936285Sbrian}
99036285Sbrian
99136285Sbrianvoid
99237007Sbriandatalink_Down(struct datalink *dl, int how)
99336285Sbrian{
99436285Sbrian  /* Carrier is lost */
99536285Sbrian  switch (dl->state) {
99636285Sbrian    case DATALINK_OPEN:
99736285Sbrian      peerid_Init(&dl->peer);
99837060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
99936285Sbrian      /* fall through */
100036285Sbrian
100138174Sbrian    case DATALINK_CBCP:
100236285Sbrian    case DATALINK_AUTH:
100336285Sbrian    case DATALINK_LCP:
100437060Sbrian      fsm2initial(&dl->physical->link.lcp.fsm);
100536285Sbrian      /* fall through */
100636285Sbrian
100736285Sbrian    default:
100837007Sbrian      datalink_ComeDown(dl, how);
100936285Sbrian  }
101036285Sbrian}
101136285Sbrian
101236285Sbrianvoid
101336285Sbriandatalink_StayDown(struct datalink *dl)
101436285Sbrian{
101536285Sbrian  dl->reconnect_tries = 0;
101636285Sbrian}
101736285Sbrian
101837007Sbrianvoid
101937007Sbriandatalink_DontHangup(struct datalink *dl)
102037007Sbrian{
102137012Sbrian  if (dl->state >= DATALINK_LCP)
102237012Sbrian    dl->stayonline = 1;
102337007Sbrian}
102437007Sbrian
102536285Sbrianint
102636285Sbriandatalink_Show(struct cmdargs const *arg)
102736285Sbrian{
102836285Sbrian  prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
102938174Sbrian  prompt_Printf(arg->prompt, " State:              %s\n",
103036285Sbrian                datalink_State(arg->cx));
103138174Sbrian  prompt_Printf(arg->prompt, " Peer name:          ");
103236285Sbrian  if (*arg->cx->peer.authname)
103336285Sbrian    prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
103436285Sbrian  else if (arg->cx->state == DATALINK_OPEN)
103536285Sbrian    prompt_Printf(arg->prompt, "None requested\n");
103636285Sbrian  else
103736285Sbrian    prompt_Printf(arg->prompt, "N/A\n");
103838174Sbrian  prompt_Printf(arg->prompt, " Discriminator:      %s\n",
103936285Sbrian                mp_Enddisc(arg->cx->peer.enddisc.class,
104036285Sbrian                           arg->cx->peer.enddisc.address,
104136285Sbrian                           arg->cx->peer.enddisc.len));
104236285Sbrian
104336285Sbrian  prompt_Printf(arg->prompt, "\nDefaults:\n");
104438174Sbrian  prompt_Printf(arg->prompt, " Phone List:         %s\n",
104536285Sbrian                arg->cx->cfg.phone.list);
104636285Sbrian  if (arg->cx->cfg.dial.max)
104738174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
104836285Sbrian                  arg->cx->cfg.dial.max);
104936285Sbrian  else
105038174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
105144261Sbrian  if (arg->cx->cfg.dial.next_timeout >= 0)
105236285Sbrian    prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
105336285Sbrian  else
105436285Sbrian    prompt_Printf(arg->prompt, "random/");
105544261Sbrian  if (arg->cx->cfg.dial.timeout >= 0)
105636285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
105736285Sbrian  else
105836285Sbrian    prompt_Printf(arg->prompt, "random\n");
105938174Sbrian  prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
106036285Sbrian                arg->cx->cfg.reconnect.max);
106136285Sbrian  if (arg->cx->cfg.reconnect.timeout > 0)
106236285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
106336285Sbrian  else
106436285Sbrian    prompt_Printf(arg->prompt, "random\n");
106538174Sbrian  prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
106638174Sbrian                PHYS_DIRECT ?  "accepted: " : "requested:");
106738174Sbrian  if (!arg->cx->cfg.callback.opmask)
106838174Sbrian    prompt_Printf(arg->prompt, "none\n");
106938174Sbrian  else {
107038174Sbrian    int comma = 0;
107138174Sbrian
107238174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
107338174Sbrian      prompt_Printf(arg->prompt, "none");
107438174Sbrian      comma = 1;
107538174Sbrian    }
107638174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
107738174Sbrian      prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
107838174Sbrian      comma = 1;
107938174Sbrian    }
108038174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
108138174Sbrian      prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
108238174Sbrian      if (arg->cx->physical->type != PHYS_DIRECT)
108338174Sbrian        prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
108438174Sbrian      comma = 1;
108538174Sbrian    }
108638174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
108738174Sbrian      prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
108838174Sbrian      prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
108938174Sbrian                    arg->cx->cfg.cbcp.delay);
109040483Sbrian      prompt_Printf(arg->prompt, "                     phone: ");
109140483Sbrian      if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
109240483Sbrian        if (arg->cx->physical->type & PHYS_DIRECT)
109340483Sbrian          prompt_Printf(arg->prompt, "Caller decides\n");
109440483Sbrian        else
109540483Sbrian          prompt_Printf(arg->prompt, "Dialback server decides\n");
109640483Sbrian      } else
109740483Sbrian        prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
109838174Sbrian      prompt_Printf(arg->prompt, "                     timeout: %lds\n",
109938174Sbrian                    arg->cx->cfg.cbcp.fsmretry);
110038174Sbrian    } else
110138174Sbrian      prompt_Printf(arg->prompt, "\n");
110238174Sbrian  }
110338174Sbrian
110438174Sbrian  prompt_Printf(arg->prompt, " Dial Script:        %s\n",
110536285Sbrian                arg->cx->cfg.script.dial);
110638174Sbrian  prompt_Printf(arg->prompt, " Login Script:       %s\n",
110736285Sbrian                arg->cx->cfg.script.login);
110852488Sbrian  prompt_Printf(arg->prompt, " Logout Script:      %s\n",
110952488Sbrian                arg->cx->cfg.script.logout);
111038174Sbrian  prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
111136285Sbrian                arg->cx->cfg.script.hangup);
111236285Sbrian  return 0;
111336285Sbrian}
111436285Sbrian
111536285Sbrianint
111636285Sbriandatalink_SetReconnect(struct cmdargs const *arg)
111736285Sbrian{
111836285Sbrian  if (arg->argc == arg->argn+2) {
111936285Sbrian    arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
112036285Sbrian    arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
112136285Sbrian    return 0;
112236285Sbrian  }
112336285Sbrian  return -1;
112436285Sbrian}
112536285Sbrian
112636285Sbrianint
112736285Sbriandatalink_SetRedial(struct cmdargs const *arg)
112836285Sbrian{
112944468Sbrian  const char *sep, *osep;
113044468Sbrian  int timeout, inc, maxinc, tries;
113136285Sbrian
113236285Sbrian  if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
113336285Sbrian    if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
113436285Sbrian	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
113536285Sbrian      arg->cx->cfg.dial.timeout = -1;
113636285Sbrian      randinit();
113736285Sbrian    } else {
113836285Sbrian      timeout = atoi(arg->argv[arg->argn]);
113936285Sbrian
114036285Sbrian      if (timeout >= 0)
114136285Sbrian	arg->cx->cfg.dial.timeout = timeout;
114236285Sbrian      else {
114336285Sbrian	log_Printf(LogWARN, "Invalid redial timeout\n");
114436285Sbrian	return -1;
114536285Sbrian      }
114636285Sbrian    }
114736285Sbrian
114844468Sbrian    sep = strchr(arg->argv[arg->argn], '+');
114944468Sbrian    if (sep) {
115044468Sbrian      inc = atoi(++sep);
115144468Sbrian      osep = sep;
115244468Sbrian      if (inc >= 0)
115344468Sbrian        arg->cx->cfg.dial.inc = inc;
115444468Sbrian      else {
115544468Sbrian        log_Printf(LogWARN, "Invalid timeout increment\n");
115644468Sbrian        return -1;
115744468Sbrian      }
115844468Sbrian      sep = strchr(sep, '-');
115944468Sbrian      if (sep) {
116044468Sbrian        maxinc = atoi(++sep);
116144468Sbrian        if (maxinc >= 0)
116244468Sbrian          arg->cx->cfg.dial.maxinc = maxinc;
116344468Sbrian        else {
116444468Sbrian          log_Printf(LogWARN, "Invalid maximum timeout increments\n");
116544468Sbrian          return -1;
116644468Sbrian        }
116744468Sbrian      } else {
116844468Sbrian        /* Default timeout increment */
116944468Sbrian        arg->cx->cfg.dial.maxinc = 10;
117044468Sbrian        sep = osep;
117144468Sbrian      }
117244468Sbrian    } else {
117344468Sbrian      /* Default timeout increment & max increment */
117444468Sbrian      arg->cx->cfg.dial.inc = 0;
117544468Sbrian      arg->cx->cfg.dial.maxinc = 10;
117644468Sbrian      sep = arg->argv[arg->argn];
117744468Sbrian    }
117844468Sbrian
117944468Sbrian    sep = strchr(sep, '.');
118044468Sbrian    if (sep) {
118144468Sbrian      if (strcasecmp(++sep, "random") == 0) {
118236285Sbrian	arg->cx->cfg.dial.next_timeout = -1;
118336285Sbrian	randinit();
118436285Sbrian      } else {
118544468Sbrian	timeout = atoi(sep);
118636285Sbrian	if (timeout >= 0)
118736285Sbrian	  arg->cx->cfg.dial.next_timeout = timeout;
118836285Sbrian	else {
118936285Sbrian	  log_Printf(LogWARN, "Invalid next redial timeout\n");
119036285Sbrian	  return -1;
119136285Sbrian	}
119236285Sbrian      }
119336285Sbrian    } else
119436285Sbrian      /* Default next timeout */
119536285Sbrian      arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
119636285Sbrian
119736285Sbrian    if (arg->argc == arg->argn+2) {
119836285Sbrian      tries = atoi(arg->argv[arg->argn+1]);
119936285Sbrian
120036285Sbrian      if (tries >= 0) {
120136285Sbrian	arg->cx->cfg.dial.max = tries;
120236285Sbrian      } else {
120336285Sbrian	log_Printf(LogWARN, "Invalid retry value\n");
120436285Sbrian	return 1;
120536285Sbrian      }
120636285Sbrian    }
120736285Sbrian    return 0;
120836285Sbrian  }
120944468Sbrian
121036285Sbrian  return -1;
121136285Sbrian}
121236285Sbrian
121336285Sbrianstatic const char *states[] = {
121436285Sbrian  "closed",
121536285Sbrian  "opening",
121636285Sbrian  "hangup",
121736285Sbrian  "dial",
121849472Sbrian  "carrier",
121952488Sbrian  "logout",
122036285Sbrian  "login",
122136285Sbrian  "ready",
122236285Sbrian  "lcp",
122336285Sbrian  "auth",
122438174Sbrian  "cbcp",
122536285Sbrian  "open"
122636285Sbrian};
122736285Sbrian
122836285Sbrianconst char *
122936285Sbriandatalink_State(struct datalink *dl)
123036285Sbrian{
123136285Sbrian  if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
123236285Sbrian    return "unknown";
123336285Sbrian  return states[dl->state];
123436285Sbrian}
123536285Sbrian
123636285Sbrianstatic void
123736285Sbriandatalink_NewState(struct datalink *dl, int state)
123836285Sbrian{
123936285Sbrian  if (state != dl->state) {
124036285Sbrian    if (state >= 0 && state < sizeof states / sizeof states[0]) {
124136285Sbrian      log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
124236285Sbrian                 states[state]);
124336285Sbrian      dl->state = state;
124436285Sbrian    } else
124536285Sbrian      log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
124636285Sbrian  }
124736285Sbrian}
124836285Sbrian
124936285Sbrianstruct datalink *
125036285Sbrianiov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
125152942Sbrian             int fd, int *auxfd, int *nauxfd)
125236285Sbrian{
125336285Sbrian  struct datalink *dl, *cdl;
125444305Sbrian  struct fsm_retry copy;
125536285Sbrian  char *oname;
125636285Sbrian
125736285Sbrian  dl = (struct datalink *)iov[(*niov)++].iov_base;
125836285Sbrian  dl->name = iov[*niov].iov_base;
125936285Sbrian
126036285Sbrian  if (dl->name[DATALINK_MAXNAME-1]) {
126136285Sbrian    dl->name[DATALINK_MAXNAME-1] = '\0';
126236285Sbrian    if (strlen(dl->name) == DATALINK_MAXNAME - 1)
126336285Sbrian      log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
126436285Sbrian  }
126536285Sbrian
126636285Sbrian  /* Make sure the name is unique ! */
126736285Sbrian  oname = NULL;
126836285Sbrian  do {
126936285Sbrian    for (cdl = bundle->links; cdl; cdl = cdl->next)
127036285Sbrian      if (!strcasecmp(dl->name, cdl->name)) {
127136285Sbrian        if (oname)
127236285Sbrian          free(datalink_NextName(dl));
127336285Sbrian        else
127436285Sbrian          oname = datalink_NextName(dl);
127536285Sbrian        break;	/* Keep renaming 'till we have no conflicts */
127636285Sbrian      }
127736285Sbrian  } while (cdl);
127836285Sbrian
127936285Sbrian  if (oname) {
128036285Sbrian    log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
128136285Sbrian    free(oname);
128236285Sbrian  } else {
128336285Sbrian    dl->name = strdup(dl->name);
128436285Sbrian    free(iov[*niov].iov_base);
128536285Sbrian  }
128636285Sbrian  (*niov)++;
128736285Sbrian
128836285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
128936285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
129036285Sbrian  dl->desc.IsSet = datalink_IsSet;
129136285Sbrian  dl->desc.Read = datalink_Read;
129236285Sbrian  dl->desc.Write = datalink_Write;
129336285Sbrian
129436285Sbrian  mp_linkInit(&dl->mp);
129536285Sbrian  *dl->phone.list = '\0';
129636285Sbrian  dl->phone.next = NULL;
129736285Sbrian  dl->phone.alt = NULL;
129836285Sbrian  dl->phone.chosen = "N/A";
129936285Sbrian
130036285Sbrian  dl->bundle = bundle;
130136285Sbrian  dl->next = NULL;
130244468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
130344468Sbrian  dl->dial.tries = 0;
130436285Sbrian  dl->reconnect_tries = 0;
130536285Sbrian  dl->parent = &bundle->fsm;
130636285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
130736285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
130836285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
130936285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
131036285Sbrian  dl->fsmp.object = dl;
131136285Sbrian
131252942Sbrian  dl->physical = iov2physical(dl, iov, niov, maxiov, fd, auxfd, nauxfd);
131336285Sbrian
131436285Sbrian  if (!dl->physical) {
131536285Sbrian    free(dl->name);
131636285Sbrian    free(dl);
131736285Sbrian    dl = NULL;
131836285Sbrian  } else {
131944305Sbrian    copy = dl->pap.cfg.fsm;
132043693Sbrian    pap_Init(&dl->pap, dl->physical);
132144305Sbrian    dl->pap.cfg.fsm = copy;
132243693Sbrian
132344305Sbrian    copy = dl->chap.auth.cfg.fsm;
132443693Sbrian    chap_Init(&dl->chap, dl->physical);
132544305Sbrian    dl->chap.auth.cfg.fsm = copy;
132643693Sbrian
132738174Sbrian    cbcp_Init(&dl->cbcp, dl->physical);
132836285Sbrian
132952488Sbrian    memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
133052488Sbrian    chat_Init(&dl->chat, dl->physical, NULL, NULL);
133152488Sbrian
133236285Sbrian    log_Printf(LogPHASE, "%s: Transferred in %s state\n",
133336285Sbrian              dl->name, datalink_State(dl));
133436285Sbrian  }
133536285Sbrian
133636285Sbrian  return dl;
133736285Sbrian}
133836285Sbrian
133936285Sbrianint
134036450Sbriandatalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
134153684Sbrian             int *auxfd, int *nauxfd)
134236285Sbrian{
134336285Sbrian  /* If `dl' is NULL, we're allocating before a Fromiov() */
134436285Sbrian  int link_fd;
134536285Sbrian
134636285Sbrian  if (dl) {
134744468Sbrian    timer_Stop(&dl->dial.timer);
134838174Sbrian    /* The following is purely for the sake of paranoia */
134938174Sbrian    cbcp_Down(&dl->cbcp);
135036285Sbrian    timer_Stop(&dl->pap.authtimer);
135136285Sbrian    timer_Stop(&dl->chap.auth.authtimer);
135236285Sbrian  }
135336285Sbrian
135436285Sbrian  if (*niov >= maxiov - 1) {
135536285Sbrian    log_Printf(LogERROR, "Toiov: No room for datalink !\n");
135636285Sbrian    if (dl) {
135736285Sbrian      free(dl->name);
135836285Sbrian      free(dl);
135936285Sbrian    }
136036285Sbrian    return -1;
136136285Sbrian  }
136236285Sbrian
136353684Sbrian  iov[*niov].iov_base = (void *)dl;
136436285Sbrian  iov[(*niov)++].iov_len = sizeof *dl;
136553684Sbrian  iov[*niov].iov_base = dl ? realloc(dl->name, DATALINK_MAXNAME) : NULL;
136636285Sbrian  iov[(*niov)++].iov_len = DATALINK_MAXNAME;
136736285Sbrian
136852942Sbrian  link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, auxfd,
136953684Sbrian                         nauxfd);
137036285Sbrian
137136285Sbrian  if (link_fd == -1 && dl) {
137236285Sbrian    free(dl->name);
137336285Sbrian    free(dl);
137436285Sbrian  }
137536285Sbrian
137636285Sbrian  return link_fd;
137736285Sbrian}
137836285Sbrian
137936285Sbrianvoid
138036285Sbriandatalink_Rename(struct datalink *dl, const char *name)
138136285Sbrian{
138236285Sbrian  free(dl->name);
138336285Sbrian  dl->physical->link.name = dl->name = strdup(name);
138436285Sbrian}
138536285Sbrian
138636285Sbrianchar *
138736285Sbriandatalink_NextName(struct datalink *dl)
138836285Sbrian{
138936285Sbrian  int f, n;
139036285Sbrian  char *name, *oname;
139136285Sbrian
139236285Sbrian  n = strlen(dl->name);
139336285Sbrian  name = (char *)malloc(n+3);
139436285Sbrian  for (f = n - 1; f >= 0; f--)
139536285Sbrian    if (!isdigit(dl->name[f]))
139636285Sbrian      break;
139736285Sbrian  n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
139836285Sbrian  sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
139936285Sbrian  oname = dl->name;
140036345Sbrian  dl->name = name;
140136345Sbrian  /* our physical link name isn't updated (it probably isn't created yet) */
140236285Sbrian  return oname;
140336285Sbrian}
140436285Sbrian
140536285Sbrianint
140636285Sbriandatalink_SetMode(struct datalink *dl, int mode)
140736285Sbrian{
140836285Sbrian  if (!physical_SetMode(dl->physical, mode))
140936285Sbrian    return 0;
141036285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
141136285Sbrian    dl->script.run = 0;
141236285Sbrian  if (dl->physical->type == PHYS_DIRECT)
141336285Sbrian    dl->reconnect_tries = 0;
141453830Sbrian  if (mode & (PHYS_DDIAL|PHYS_BACKGROUND|PHYS_FOREGROUND) &&
141553830Sbrian      dl->state <= DATALINK_READY)
141636285Sbrian    datalink_Up(dl, 1, 1);
141736285Sbrian  return 1;
141836285Sbrian}
141944468Sbrian
142044468Sbrianint
142144468Sbriandatalink_GetDialTimeout(struct datalink *dl)
142244468Sbrian{
142344468Sbrian  int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
142444468Sbrian
142544468Sbrian  if (dl->dial.incs < dl->cfg.dial.maxinc)
142644468Sbrian    dl->dial.incs++;
142744468Sbrian
142844468Sbrian  return result;
142944468Sbrian}
1430