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$
2736285Sbrian */
2836285Sbrian
2943313Sbrian#include <sys/param.h>
3036285Sbrian#include <netinet/in.h>
3136285Sbrian#include <netinet/in_systm.h>
3236285Sbrian#include <netinet/ip.h>
3381634Sbrian#include <sys/socket.h>
3436285Sbrian#include <sys/un.h>
3536285Sbrian
3636285Sbrian#include <ctype.h>
37102500Sbrian#include <stdarg.h>
3836285Sbrian#include <stdio.h>
3936285Sbrian#include <stdlib.h>
4036285Sbrian#include <string.h>
4136285Sbrian#include <sys/uio.h>
4236285Sbrian#include <termios.h>
4336285Sbrian
4446686Sbrian#include "layer.h"
4536285Sbrian#include "mbuf.h"
4636285Sbrian#include "log.h"
4736285Sbrian#include "defs.h"
4836285Sbrian#include "timer.h"
4936285Sbrian#include "fsm.h"
5036285Sbrian#include "descriptor.h"
5136285Sbrian#include "lqr.h"
5236285Sbrian#include "hdlc.h"
5363484Sbrian#include "lcp.h"
5436285Sbrian#include "async.h"
5536285Sbrian#include "throughput.h"
5636285Sbrian#include "ccp.h"
5736285Sbrian#include "link.h"
5836285Sbrian#include "physical.h"
5936285Sbrian#include "iplist.h"
6036285Sbrian#include "slcompress.h"
6181634Sbrian#include "ncpaddr.h"
6236285Sbrian#include "ipcp.h"
6336285Sbrian#include "filter.h"
6436285Sbrian#include "mp.h"
6543313Sbrian#ifndef NORADIUS
6643313Sbrian#include "radius.h"
6743313Sbrian#endif
6881634Sbrian#include "ipv6cp.h"
6981634Sbrian#include "ncp.h"
7036285Sbrian#include "bundle.h"
7136285Sbrian#include "chat.h"
7236285Sbrian#include "auth.h"
7336285Sbrian#include "prompt.h"
7446686Sbrian#include "proto.h"
7536285Sbrian#include "pap.h"
7636285Sbrian#include "chap.h"
7736285Sbrian#include "command.h"
7838174Sbrian#include "cbcp.h"
7936285Sbrian#include "datalink.h"
8036285Sbrian
8136285Sbrianstatic void datalink_LoginDone(struct datalink *);
82134789Sbrianstatic void datalink_NewState(struct datalink *, unsigned);
83136375Sbrianstatic char *datalink_NextName(struct datalink *);
8436285Sbrian
8536285Sbrianstatic void
8636285Sbriandatalink_OpenTimeout(void *v)
8736285Sbrian{
8836285Sbrian  struct datalink *dl = (struct datalink *)v;
8936285Sbrian
9044468Sbrian  timer_Stop(&dl->dial.timer);
9136285Sbrian  if (dl->state == DATALINK_OPENING)
9259084Sbrian    log_Printf(LogCHAT, "%s: Redial timer expired.\n", dl->name);
9336285Sbrian}
9436285Sbrian
9544261Sbrianstatic int
9636285Sbriandatalink_StartDialTimer(struct datalink *dl, int Timeout)
9736285Sbrian{
9844261Sbrian  int result = Timeout;
9944261Sbrian
10044468Sbrian  timer_Stop(&dl->dial.timer);
10185362Sbrian  if (Timeout < 0)
10285362Sbrian    result = (random() % DIAL_TIMEOUT) + 1;
10385362Sbrian  dl->dial.timer.load = result ? result * SECTICKS : 1;
10485362Sbrian  dl->dial.timer.func = datalink_OpenTimeout;
10585362Sbrian  dl->dial.timer.name = "dial";
10685362Sbrian  dl->dial.timer.arg = dl;
10785362Sbrian  timer_Start(&dl->dial.timer);
10885362Sbrian  if (dl->state == DATALINK_OPENING)
10985362Sbrian    log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
11085362Sbrian               dl->name, result);
11144261Sbrian  return result;
11236285Sbrian}
11336285Sbrian
11436285Sbrianstatic void
11536285Sbriandatalink_HangupDone(struct datalink *dl)
11636285Sbrian{
11736285Sbrian  if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
11846686Sbrian      dl->physical->fd != -1) {
11946686Sbrian    /* Don't close our device if the link is dedicated */
12036285Sbrian    datalink_LoginDone(dl);
12136285Sbrian    return;
12236285Sbrian  }
12336285Sbrian
12454055Sbrian  chat_Finish(&dl->chat);
12546686Sbrian  physical_Close(dl->physical);
12636285Sbrian  dl->phone.chosen = "N/A";
12736285Sbrian
12838174Sbrian  if (dl->cbcp.required) {
12938174Sbrian    log_Printf(LogPHASE, "Call peer back on %s\n", dl->cbcp.fsm.phone);
13038174Sbrian    dl->cfg.callback.opmask = 0;
13138174Sbrian    strncpy(dl->cfg.phone.list, dl->cbcp.fsm.phone,
13238174Sbrian            sizeof dl->cfg.phone.list - 1);
13338174Sbrian    dl->cfg.phone.list[sizeof dl->cfg.phone.list - 1] = '\0';
13438174Sbrian    dl->phone.alt = dl->phone.next = NULL;
13538174Sbrian    dl->reconnect_tries = dl->cfg.reconnect.max;
13644468Sbrian    dl->dial.tries = dl->cfg.dial.max;
13744468Sbrian    dl->dial.incs = 0;
13838174Sbrian    dl->script.run = 1;
13938174Sbrian    dl->script.packetmode = 1;
14038174Sbrian    if (!physical_SetMode(dl->physical, PHYS_BACKGROUND))
14138174Sbrian      log_Printf(LogERROR, "Oops - can't change mode to BACKGROUND (gulp) !\n");
14238174Sbrian    bundle_LinksRemoved(dl->bundle);
14344468Sbrian    /* if dial.timeout is < 0 (random), we don't override fsm.delay */
14438174Sbrian    if (dl->cbcp.fsm.delay < dl->cfg.dial.timeout)
14538174Sbrian      dl->cbcp.fsm.delay = dl->cfg.dial.timeout;
14638174Sbrian    datalink_StartDialTimer(dl, dl->cbcp.fsm.delay);
14738174Sbrian    cbcp_Down(&dl->cbcp);
14838174Sbrian    datalink_NewState(dl, DATALINK_OPENING);
14952412Sbrian    if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
15052412Sbrian        bundle_Phase(dl->bundle) == PHASE_TERMINATE)
15145385Sbrian      bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
15238174Sbrian  } else if (dl->bundle->CleaningUp ||
15336285Sbrian      (dl->physical->type == PHYS_DIRECT) ||
15444468Sbrian      ((!dl->dial.tries || (dl->dial.tries < 0 && !dl->reconnect_tries)) &&
15536465Sbrian       !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
15636285Sbrian    datalink_NewState(dl, DATALINK_CLOSED);
15744468Sbrian    dl->dial.tries = -1;
15844468Sbrian    dl->dial.incs = 0;
15936285Sbrian    dl->reconnect_tries = 0;
16036285Sbrian    bundle_LinkClosed(dl->bundle, dl);
16158455Sbrian    if (!dl->bundle->CleaningUp &&
16258455Sbrian        !(dl->physical->type & (PHYS_DIRECT|PHYS_BACKGROUND|PHYS_FOREGROUND)))
16344468Sbrian      datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
16436285Sbrian  } else {
16536285Sbrian    datalink_NewState(dl, DATALINK_OPENING);
16652412Sbrian    if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
16752412Sbrian        bundle_Phase(dl->bundle) == PHASE_TERMINATE)
16845385Sbrian      bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
16944468Sbrian    if (dl->dial.tries < 0) {
17036285Sbrian      datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
17144468Sbrian      dl->dial.tries = dl->cfg.dial.max;
17244468Sbrian      dl->dial.incs = 0;
17336285Sbrian      dl->reconnect_tries--;
17459084Sbrian      log_Printf(LogCHAT, "%s: Reconnect try %d of %d\n",
17559084Sbrian                 dl->name, dl->cfg.reconnect.max - dl->reconnect_tries,
17659084Sbrian                 dl->cfg.reconnect.max);
17759084Sbrian      bundle_Notify(dl->bundle, EX_RECONNECT);
17836285Sbrian    } else {
17936285Sbrian      if (dl->phone.next == NULL)
18044468Sbrian        datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
18136285Sbrian      else
18236285Sbrian        datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
18359084Sbrian      bundle_Notify(dl->bundle, EX_REDIAL);
18436285Sbrian    }
18536285Sbrian  }
18636285Sbrian}
18736285Sbrian
18849472Sbrianconst char *
18936285Sbriandatalink_ChoosePhoneNumber(struct datalink *dl)
19036285Sbrian{
19136285Sbrian  char *phone;
19236285Sbrian
19336285Sbrian  if (dl->phone.alt == NULL) {
19436285Sbrian    if (dl->phone.next == NULL) {
19536285Sbrian      strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
19636285Sbrian      dl->phone.list[sizeof dl->phone.list - 1] = '\0';
19748003Sbrian      if (*dl->phone.list == '\0')
19848003Sbrian        return "";
19936285Sbrian      dl->phone.next = dl->phone.list;
20036285Sbrian    }
20136285Sbrian    dl->phone.alt = strsep(&dl->phone.next, ":");
20236285Sbrian  }
20336285Sbrian  phone = strsep(&dl->phone.alt, "|");
20436285Sbrian  dl->phone.chosen = *phone ? phone : "[NONE]";
20536285Sbrian  if (*phone)
20659084Sbrian    log_Printf(LogCHAT, "Phone: %s\n", phone);
20736285Sbrian  return phone;
20836285Sbrian}
20936285Sbrian
21036285Sbrianstatic void
21136285Sbriandatalink_LoginDone(struct datalink *dl)
21236285Sbrian{
21354055Sbrian  chat_Finish(&dl->chat);
21452488Sbrian
21598243Sbrian  if (!dl->script.packetmode) {
21644468Sbrian    dl->dial.tries = -1;
21744468Sbrian    dl->dial.incs = 0;
21836285Sbrian    datalink_NewState(dl, DATALINK_READY);
21946686Sbrian  } else if (!physical_Raw(dl->physical)) {
22044468Sbrian    dl->dial.tries = 0;
22136285Sbrian    log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
22298243Sbrian    if (dl->script.run) {
22352488Sbrian      datalink_NewState(dl, DATALINK_LOGOUT);
22454914Sbrian      if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
22554914Sbrian        log_Printf(LogWARN, "Invalid logout script\n");
22636285Sbrian    } else {
22747061Sbrian      physical_StopDeviceTimer(dl->physical);
22836285Sbrian      if (dl->physical->type == PHYS_DEDICATED)
22936285Sbrian        /* force a redial timeout */
23046686Sbrian        physical_Close(dl->physical);
23136285Sbrian      datalink_HangupDone(dl);
23236285Sbrian    }
23336285Sbrian  } else {
23444468Sbrian    dl->dial.tries = -1;
23544468Sbrian    dl->dial.incs = 0;
23636285Sbrian
23736285Sbrian    hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
23891623Sbrian    async_Setup(&dl->physical->async);
23936285Sbrian
24036285Sbrian    lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
24136285Sbrian              0 : dl->physical->link.lcp.cfg.openmode);
24236285Sbrian    ccp_Setup(&dl->physical->link.ccp);
24336285Sbrian
24436285Sbrian    datalink_NewState(dl, DATALINK_LCP);
24536285Sbrian    fsm_Up(&dl->physical->link.lcp.fsm);
24636285Sbrian    fsm_Open(&dl->physical->link.lcp.fsm);
24736285Sbrian  }
24836285Sbrian}
24936285Sbrian
25036285Sbrianstatic int
25158028Sbriandatalink_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
25236285Sbrian                   int *n)
25336285Sbrian{
25436285Sbrian  struct datalink *dl = descriptor2datalink(d);
25536285Sbrian  int result;
25636285Sbrian
25736285Sbrian  result = 0;
25836285Sbrian  switch (dl->state) {
25936285Sbrian    case DATALINK_CLOSED:
26053830Sbrian      if ((dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|
26153830Sbrian                                 PHYS_FOREGROUND|PHYS_DDIAL)) &&
26247863Sbrian          !dl->bundle->CleaningUp)
26336285Sbrian        /*
26436465Sbrian         * Our first time in - DEDICATED & DDIAL never come down, and
26553830Sbrian         * DIRECT, FOREGROUND & BACKGROUND get deleted when they enter
26653830Sbrian         * DATALINK_CLOSED.  Go to DATALINK_OPENING via datalink_Up()
26753830Sbrian         * and fall through.
26836285Sbrian         */
26936285Sbrian        datalink_Up(dl, 1, 1);
27036285Sbrian      else
27136285Sbrian        break;
272102413Scharnier      /* FALLTHROUGH */
27336285Sbrian
27436285Sbrian    case DATALINK_OPENING:
27544468Sbrian      if (dl->dial.timer.state != TIMER_RUNNING) {
27644468Sbrian        if (--dl->dial.tries < 0)
27744468Sbrian          dl->dial.tries = 0;
278134789Sbrian        if (physical_Open(dl->physical) >= 0) {
27938200Sbrian          log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
28038200Sbrian                           "Type `~?' for help\r\n", dl->name,
28138200Sbrian                           dl->physical->name.full);
28236285Sbrian          if (dl->script.run) {
28336285Sbrian            datalink_NewState(dl, DATALINK_DIAL);
28454914Sbrian            if (!chat_Setup(&dl->chat, dl->cfg.script.dial,
28554914Sbrian                            *dl->cfg.script.dial ?
28654914Sbrian                            datalink_ChoosePhoneNumber(dl) : ""))
28754914Sbrian              log_Printf(LogWARN, "Invalid dial script\n");
28836465Sbrian            if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
28936285Sbrian                dl->cfg.dial.max)
29036285Sbrian              log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
29144468Sbrian                        dl->name, dl->cfg.dial.max - dl->dial.tries,
29236285Sbrian                        dl->cfg.dial.max);
29336285Sbrian          } else
29451978Sbrian            datalink_NewState(dl, DATALINK_CARRIER);
29541830Sbrian          return datalink_UpdateSet(d, r, w, e, n);
29636285Sbrian        } else {
29736465Sbrian          if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
29836285Sbrian              dl->cfg.dial.max)
29946686Sbrian            log_Printf(LogCHAT, "Failed to open device (attempt %u of %d)\n",
30044468Sbrian                       dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
30136285Sbrian          else
30246686Sbrian            log_Printf(LogCHAT, "Failed to open device\n");
30336285Sbrian
30436285Sbrian          if (dl->bundle->CleaningUp ||
30536465Sbrian              (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
30644468Sbrian               dl->cfg.dial.max && dl->dial.tries == 0)) {
30736285Sbrian            datalink_NewState(dl, DATALINK_CLOSED);
30836285Sbrian            dl->reconnect_tries = 0;
30944468Sbrian            dl->dial.tries = -1;
31038200Sbrian            log_WritePrompts(dl, "Failed to open %s\n",
31138200Sbrian                             dl->physical->name.full);
31236285Sbrian            bundle_LinkClosed(dl->bundle, dl);
31336285Sbrian          }
31438200Sbrian          if (!dl->bundle->CleaningUp) {
31544468Sbrian            int timeout;
31644468Sbrian
31744468Sbrian            timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
31859084Sbrian            bundle_Notify(dl->bundle, EX_REDIAL);
31938200Sbrian            log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
32044261Sbrian                             dl->physical->name.full, timeout);
32138200Sbrian          }
32236285Sbrian        }
32336285Sbrian      }
32436285Sbrian      break;
32536285Sbrian
32649472Sbrian    case DATALINK_CARRIER:
32749472Sbrian      /* Wait for carrier on the device */
32849472Sbrian      switch (physical_AwaitCarrier(dl->physical)) {
32949472Sbrian        case CARRIER_PENDING:
33049472Sbrian          log_Printf(LogDEBUG, "Waiting for carrier\n");
33149472Sbrian          return 0;	/* A device timer is running to wake us up again */
33249472Sbrian
33349472Sbrian        case CARRIER_OK:
33451978Sbrian          if (dl->script.run) {
33551978Sbrian            datalink_NewState(dl, DATALINK_LOGIN);
33654914Sbrian            if (!chat_Setup(&dl->chat, dl->cfg.script.login, NULL))
33754914Sbrian              log_Printf(LogWARN, "Invalid login script\n");
33851978Sbrian          } else
33951978Sbrian            datalink_LoginDone(dl);
34049472Sbrian          return datalink_UpdateSet(d, r, w, e, n);
34149472Sbrian
34249472Sbrian        case CARRIER_LOST:
34349472Sbrian          physical_Offline(dl->physical);	/* Is this required ? */
34498243Sbrian          if (dl->script.run) {
34551978Sbrian            datalink_NewState(dl, DATALINK_HANGUP);
34654914Sbrian            if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
34754914Sbrian              log_Printf(LogWARN, "Invalid hangup script\n");
34853070Sbrian            return datalink_UpdateSet(d, r, w, e, n);
34953070Sbrian          } else {
35051978Sbrian            datalink_HangupDone(dl);
35153070Sbrian            return 0;	/* Maybe bundle_CleanDatalinks() has something to do */
35253070Sbrian          }
35349472Sbrian      }
35449472Sbrian
35536285Sbrian    case DATALINK_HANGUP:
35636285Sbrian    case DATALINK_DIAL:
35752488Sbrian    case DATALINK_LOGOUT:
35836285Sbrian    case DATALINK_LOGIN:
35936285Sbrian      result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
36036285Sbrian      switch (dl->chat.state) {
36136285Sbrian        case CHAT_DONE:
36236285Sbrian          /* script succeeded */
36336285Sbrian          switch(dl->state) {
36436285Sbrian            case DATALINK_HANGUP:
36536285Sbrian              datalink_HangupDone(dl);
36636285Sbrian              break;
36736285Sbrian            case DATALINK_DIAL:
36849472Sbrian              datalink_NewState(dl, DATALINK_CARRIER);
36936285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
37052488Sbrian            case DATALINK_LOGOUT:
37152488Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
37252488Sbrian              physical_Offline(dl->physical);
37354914Sbrian              if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
37454914Sbrian                log_Printf(LogWARN, "Invalid hangup script\n");
37552488Sbrian              return datalink_UpdateSet(d, r, w, e, n);
37636285Sbrian            case DATALINK_LOGIN:
37742390Sbrian              dl->phone.alt = NULL;
37836285Sbrian              datalink_LoginDone(dl);
37942905Sbrian              return datalink_UpdateSet(d, r, w, e, n);
38036285Sbrian          }
38136285Sbrian          break;
38236285Sbrian        case CHAT_FAILED:
38336285Sbrian          /* Going down - script failed */
38436285Sbrian          log_Printf(LogWARN, "Chat script failed\n");
38536285Sbrian          switch(dl->state) {
38636285Sbrian            case DATALINK_HANGUP:
38736285Sbrian              datalink_HangupDone(dl);
38836285Sbrian              break;
38936285Sbrian            case DATALINK_DIAL:
39052488Sbrian            case DATALINK_LOGOUT:
39136285Sbrian            case DATALINK_LOGIN:
39236285Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
39352488Sbrian              physical_Offline(dl->physical);
39454914Sbrian              if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
39554914Sbrian                log_Printf(LogWARN, "Invalid hangup script\n");
39636285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
39736285Sbrian          }
39836285Sbrian          break;
39936285Sbrian      }
40036285Sbrian      break;
40136285Sbrian
40236285Sbrian    case DATALINK_READY:
40336285Sbrian    case DATALINK_LCP:
40436285Sbrian    case DATALINK_AUTH:
40538174Sbrian    case DATALINK_CBCP:
40636285Sbrian    case DATALINK_OPEN:
40743888Sbrian      result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
40843888Sbrian               descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
40936285Sbrian      break;
41036285Sbrian  }
41136285Sbrian  return result;
41236285Sbrian}
41336285Sbrian
41436285Sbrianint
41536285Sbriandatalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
41636285Sbrian{
41736285Sbrian  return physical_RemoveFromSet(dl->physical, r, w, e);
41836285Sbrian}
41936285Sbrian
42036285Sbrianstatic int
42158028Sbriandatalink_IsSet(struct fdescriptor *d, const fd_set *fdset)
42236285Sbrian{
42336285Sbrian  struct datalink *dl = descriptor2datalink(d);
42436285Sbrian
42536285Sbrian  switch (dl->state) {
42636285Sbrian    case DATALINK_CLOSED:
42736285Sbrian    case DATALINK_OPENING:
42836285Sbrian      break;
42936285Sbrian
43036285Sbrian    case DATALINK_HANGUP:
43136285Sbrian    case DATALINK_DIAL:
43252488Sbrian    case DATALINK_LOGOUT:
43336285Sbrian    case DATALINK_LOGIN:
43436285Sbrian      return descriptor_IsSet(&dl->chat.desc, fdset);
43536285Sbrian
43636285Sbrian    case DATALINK_READY:
43736285Sbrian    case DATALINK_LCP:
43836285Sbrian    case DATALINK_AUTH:
43938174Sbrian    case DATALINK_CBCP:
44036285Sbrian    case DATALINK_OPEN:
44143888Sbrian      return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
44243888Sbrian             descriptor_IsSet(&dl->physical->desc, fdset);
44336285Sbrian  }
44436285Sbrian  return 0;
44536285Sbrian}
44636285Sbrian
44736285Sbrianstatic void
44858028Sbriandatalink_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
44936285Sbrian{
45036285Sbrian  struct datalink *dl = descriptor2datalink(d);
45136285Sbrian
45236285Sbrian  switch (dl->state) {
45336285Sbrian    case DATALINK_CLOSED:
45436285Sbrian    case DATALINK_OPENING:
45536285Sbrian      break;
45636285Sbrian
45736285Sbrian    case DATALINK_HANGUP:
45836285Sbrian    case DATALINK_DIAL:
45952488Sbrian    case DATALINK_LOGOUT:
46036285Sbrian    case DATALINK_LOGIN:
46136285Sbrian      descriptor_Read(&dl->chat.desc, bundle, fdset);
46236285Sbrian      break;
46336285Sbrian
46436285Sbrian    case DATALINK_READY:
46536285Sbrian    case DATALINK_LCP:
46636285Sbrian    case DATALINK_AUTH:
46738174Sbrian    case DATALINK_CBCP:
46836285Sbrian    case DATALINK_OPEN:
46943888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
47043888Sbrian        descriptor_Read(&dl->chap.desc, bundle, fdset);
47143888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
47243888Sbrian        descriptor_Read(&dl->physical->desc, bundle, fdset);
47336285Sbrian      break;
47436285Sbrian  }
47536285Sbrian}
47636285Sbrian
47737141Sbrianstatic int
47867912Sbriandatalink_Write(struct fdescriptor *d, struct bundle *bundle,
47967912Sbrian               const fd_set *fdset)
48036285Sbrian{
48136285Sbrian  struct datalink *dl = descriptor2datalink(d);
48237141Sbrian  int result = 0;
48336285Sbrian
48436285Sbrian  switch (dl->state) {
48536285Sbrian    case DATALINK_CLOSED:
48636285Sbrian    case DATALINK_OPENING:
48736285Sbrian      break;
48836285Sbrian
48936285Sbrian    case DATALINK_HANGUP:
49036285Sbrian    case DATALINK_DIAL:
49152488Sbrian    case DATALINK_LOGOUT:
49236285Sbrian    case DATALINK_LOGIN:
49393418Sbrian      if ((result = descriptor_Write(&dl->chat.desc, bundle, fdset)) == -1) {
49493418Sbrian        datalink_ComeDown(dl, CLOSE_NORMAL);
49593418Sbrian        result = 0;
49693418Sbrian      }
49736285Sbrian      break;
49836285Sbrian
49936285Sbrian    case DATALINK_READY:
50036285Sbrian    case DATALINK_LCP:
50136285Sbrian    case DATALINK_AUTH:
50238174Sbrian    case DATALINK_CBCP:
50336285Sbrian    case DATALINK_OPEN:
50443888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
50593418Sbrian        switch (descriptor_Write(&dl->chap.desc, bundle, fdset)) {
50693418Sbrian        case -1:
50793418Sbrian          datalink_ComeDown(dl, CLOSE_NORMAL);
50893418Sbrian          break;
50993418Sbrian        case 1:
51093418Sbrian          result++;
51193418Sbrian        }
51243888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
51393418Sbrian        switch (descriptor_Write(&dl->physical->desc, bundle, fdset)) {
51493418Sbrian        case -1:
51593418Sbrian          datalink_ComeDown(dl, CLOSE_NORMAL);
51693418Sbrian          break;
51793418Sbrian        case 1:
51893418Sbrian          result++;
51993418Sbrian        }
52036285Sbrian      break;
52136285Sbrian  }
52237141Sbrian
52337141Sbrian  return result;
52436285Sbrian}
52536285Sbrian
52693418Sbrianvoid
52737007Sbriandatalink_ComeDown(struct datalink *dl, int how)
52836285Sbrian{
52971970Sbrian  int stayonline;
53036285Sbrian
53171970Sbrian  if (how == CLOSE_LCP)
53271970Sbrian    datalink_DontHangup(dl);
53371970Sbrian  else if (how == CLOSE_STAYDOWN)
53471970Sbrian    datalink_StayDown(dl);
53571970Sbrian
53671970Sbrian  stayonline = dl->stayonline;
53771970Sbrian  dl->stayonline = 0;
53871970Sbrian
53971970Sbrian  if (dl->state >= DATALINK_READY && stayonline) {
54047061Sbrian    physical_StopDeviceTimer(dl->physical);
54137007Sbrian    datalink_NewState(dl, DATALINK_READY);
54237007Sbrian  } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
54346686Sbrian    physical_Offline(dl->physical);
54436285Sbrian    if (dl->script.run && dl->state != DATALINK_OPENING) {
54552488Sbrian      if (dl->state == DATALINK_LOGOUT) {
54652488Sbrian        datalink_NewState(dl, DATALINK_HANGUP);
54754914Sbrian        if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
54854914Sbrian          log_Printf(LogWARN, "Invalid hangup script\n");
54952488Sbrian      } else {
55052488Sbrian        datalink_NewState(dl, DATALINK_LOGOUT);
55154914Sbrian        if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
55254914Sbrian          log_Printf(LogWARN, "Invalid logout script\n");
55352488Sbrian      }
55436285Sbrian    } else
55536285Sbrian      datalink_HangupDone(dl);
55636285Sbrian  }
55736285Sbrian}
55836285Sbrian
55936285Sbrianstatic void
56036285Sbriandatalink_LayerStart(void *v, struct fsm *fp)
56136285Sbrian{
56236285Sbrian  /* The given FSM is about to start up ! */
56336285Sbrian  struct datalink *dl = (struct datalink *)v;
56436285Sbrian
56536285Sbrian  if (fp->proto == PROTO_LCP)
56636285Sbrian    (*dl->parent->LayerStart)(dl->parent->object, fp);
56736285Sbrian}
56836285Sbrian
56936285Sbrianstatic void
57036285Sbriandatalink_LayerUp(void *v, struct fsm *fp)
57136285Sbrian{
57236285Sbrian  /* The given fsm is now up */
57336285Sbrian  struct datalink *dl = (struct datalink *)v;
57444106Sbrian  struct lcp *lcp = &dl->physical->link.lcp;
57536285Sbrian
57636285Sbrian  if (fp->proto == PROTO_LCP) {
57743693Sbrian    datalink_GotAuthname(dl, "");
57844106Sbrian    lcp->auth_ineed = lcp->want_auth;
57944106Sbrian    lcp->auth_iwait = lcp->his_auth;
58044106Sbrian    if (lcp->his_auth || lcp->want_auth) {
58145350Sbrian      if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
58236285Sbrian        bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
58336285Sbrian      log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
58444106Sbrian                Auth2Nam(lcp->his_auth, lcp->his_authtype),
58544106Sbrian                Auth2Nam(lcp->want_auth, lcp->want_authtype));
58644106Sbrian      if (lcp->his_auth == PROTO_PAP)
58743693Sbrian        auth_StartReq(&dl->pap);
58844106Sbrian      if (lcp->want_auth == PROTO_CHAP)
58943693Sbrian        auth_StartReq(&dl->chap.auth);
59036285Sbrian    } else
59136285Sbrian      datalink_AuthOk(dl);
59279165Sbrian  } else if (fp->proto == PROTO_CCP)
59379165Sbrian    (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.ccp.fsm);
59436285Sbrian}
59536285Sbrian
59644094Sbrianstatic void
59744094Sbriandatalink_AuthReInit(struct datalink *dl)
59844094Sbrian{
59944094Sbrian  auth_StopTimer(&dl->pap);
60044094Sbrian  auth_StopTimer(&dl->chap.auth);
60144094Sbrian  chap_ReInit(&dl->chap);
60244094Sbrian}
60344094Sbrian
60436285Sbrianvoid
60543693Sbriandatalink_GotAuthname(struct datalink *dl, const char *name)
60636285Sbrian{
60743693Sbrian  strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
60843693Sbrian  dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
60936285Sbrian}
61036285Sbrian
61136285Sbrianvoid
61238174Sbriandatalink_NCPUp(struct datalink *dl)
61336285Sbrian{
61437320Sbrian  int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
61536310Sbrian
61636285Sbrian  if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
61736285Sbrian    /* we've authenticated in multilink mode ! */
61836285Sbrian    switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
61936285Sbrian      case MP_LINKSENT:
62036285Sbrian        /* We've handed the link off to another ppp (well, we will soon) ! */
62136285Sbrian        return;
62236285Sbrian      case MP_UP:
62336285Sbrian        /* First link in the bundle */
62438174Sbrian        auth_Select(dl->bundle, dl->peer.authname);
62549434Sbrian        bundle_CalculateBandwidth(dl->bundle);
626102413Scharnier        /* FALLTHROUGH */
62736285Sbrian      case MP_ADDED:
62836285Sbrian        /* We're in multilink mode ! */
62936310Sbrian        dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
63049434Sbrian        bundle_CalculateBandwidth(dl->bundle);
63136285Sbrian        break;
63236285Sbrian      case MP_FAILED:
63336285Sbrian        datalink_AuthNotOk(dl);
63436285Sbrian        return;
63536285Sbrian    }
63636285Sbrian  } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
63736285Sbrian    log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
63837160Sbrian    datalink_NewState(dl, DATALINK_OPEN);
63949434Sbrian    bundle_CalculateBandwidth(dl->bundle);
64037160Sbrian    (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
64136285Sbrian    return;
64236285Sbrian  } else {
64336285Sbrian    dl->bundle->ncp.mp.peer = dl->peer;
64481634Sbrian    ncp_SetLink(&dl->bundle->ncp, &dl->physical->link);
64538174Sbrian    auth_Select(dl->bundle, dl->peer.authname);
64636285Sbrian  }
64736285Sbrian
64837320Sbrian  if (ccpok) {
64937320Sbrian    fsm_Up(&dl->physical->link.ccp.fsm);
65037320Sbrian    fsm_Open(&dl->physical->link.ccp.fsm);
65137320Sbrian  }
65236285Sbrian  datalink_NewState(dl, DATALINK_OPEN);
65336285Sbrian  bundle_NewPhase(dl->bundle, PHASE_NETWORK);
65436285Sbrian  (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
65536285Sbrian}
65636285Sbrian
65736285Sbrianvoid
65838174Sbriandatalink_CBCPComplete(struct datalink *dl)
65938174Sbrian{
66038174Sbrian  datalink_NewState(dl, DATALINK_LCP);
66144094Sbrian  datalink_AuthReInit(dl);
66238174Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
66338174Sbrian}
66438174Sbrian
66538174Sbrianvoid
66638174Sbriandatalink_CBCPFailed(struct datalink *dl)
66738174Sbrian{
66838174Sbrian  cbcp_Down(&dl->cbcp);
66938174Sbrian  datalink_CBCPComplete(dl);
67038174Sbrian}
67138174Sbrian
67238174Sbrianvoid
67338174Sbriandatalink_AuthOk(struct datalink *dl)
67438174Sbrian{
67542600Sbrian  if ((dl->physical->link.lcp.his_callback.opmask &
67642600Sbrian       CALLBACK_BIT(CALLBACK_CBCP) ||
67742600Sbrian       dl->physical->link.lcp.want_callback.opmask &
67842600Sbrian       CALLBACK_BIT(CALLBACK_CBCP)) &&
67942600Sbrian      !(dl->physical->link.lcp.want_callback.opmask &
68042600Sbrian        CALLBACK_BIT(CALLBACK_AUTH))) {
68142600Sbrian    /* We must have agreed CBCP if AUTH isn't there any more */
68238174Sbrian    datalink_NewState(dl, DATALINK_CBCP);
68338174Sbrian    cbcp_Up(&dl->cbcp);
68438174Sbrian  } else if (dl->physical->link.lcp.want_callback.opmask) {
68542600Sbrian    /* It's not CBCP */
68638174Sbrian    log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
68738174Sbrian    datalink_NewState(dl, DATALINK_LCP);
68844094Sbrian    datalink_AuthReInit(dl);
68938174Sbrian    fsm_Close(&dl->physical->link.lcp.fsm);
69038174Sbrian  } else
69138174Sbrian    switch (dl->physical->link.lcp.his_callback.opmask) {
69238174Sbrian      case 0:
69338174Sbrian        datalink_NCPUp(dl);
69438174Sbrian        break;
69538174Sbrian
69638174Sbrian      case CALLBACK_BIT(CALLBACK_AUTH):
69738174Sbrian        auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
69838174Sbrian                          sizeof dl->cbcp.fsm.phone);
69938174Sbrian        if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
70038174Sbrian          log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
70138174Sbrian                     dl->peer.authname);
70238174Sbrian          *dl->cbcp.fsm.phone = '\0';
70338174Sbrian        } else {
70438174Sbrian          char *ptr = strchr(dl->cbcp.fsm.phone, ',');
70538174Sbrian          if (ptr)
70638174Sbrian            *ptr = '\0';	/* Call back on the first number */
70738174Sbrian          log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
70838174Sbrian                     dl->cbcp.fsm.phone);
70938174Sbrian          dl->cbcp.required = 1;
71038174Sbrian        }
71138174Sbrian        dl->cbcp.fsm.delay = 0;
71238174Sbrian        datalink_NewState(dl, DATALINK_LCP);
71344094Sbrian        datalink_AuthReInit(dl);
71438174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
71538174Sbrian        break;
71638174Sbrian
71738174Sbrian      case CALLBACK_BIT(CALLBACK_E164):
71838174Sbrian        strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
71938174Sbrian                sizeof dl->cbcp.fsm.phone - 1);
72038174Sbrian        dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
72138174Sbrian        log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
72238174Sbrian                   dl->cbcp.fsm.phone);
72338174Sbrian        dl->cbcp.required = 1;
72438174Sbrian        dl->cbcp.fsm.delay = 0;
72538174Sbrian        datalink_NewState(dl, DATALINK_LCP);
72644094Sbrian        datalink_AuthReInit(dl);
72738174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
72838174Sbrian        break;
72938174Sbrian
73038174Sbrian      default:
73138174Sbrian        log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
73238174Sbrian                   dl->name);
73338174Sbrian        datalink_NewState(dl, DATALINK_LCP);
73444094Sbrian        datalink_AuthReInit(dl);
73538174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
73638174Sbrian        break;
73738174Sbrian    }
73838174Sbrian}
73938174Sbrian
74038174Sbrianvoid
74136285Sbriandatalink_AuthNotOk(struct datalink *dl)
74236285Sbrian{
74336285Sbrian  datalink_NewState(dl, DATALINK_LCP);
74444094Sbrian  datalink_AuthReInit(dl);
74536285Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
74636285Sbrian}
74736285Sbrian
74836285Sbrianstatic void
74936285Sbriandatalink_LayerDown(void *v, struct fsm *fp)
75036285Sbrian{
75136285Sbrian  /* The given FSM has been told to come down */
75236285Sbrian  struct datalink *dl = (struct datalink *)v;
75336285Sbrian
75436285Sbrian  if (fp->proto == PROTO_LCP) {
75536285Sbrian    switch (dl->state) {
75636285Sbrian      case DATALINK_OPEN:
75736285Sbrian        peerid_Init(&dl->peer);
75837060Sbrian        fsm2initial(&dl->physical->link.ccp.fsm);
75936928Sbrian        datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
76036285Sbrian        (*dl->parent->LayerDown)(dl->parent->object, fp);
761102413Scharnier        /* FALLTHROUGH (just in case) */
76236285Sbrian
76338174Sbrian      case DATALINK_CBCP:
76438174Sbrian        if (!dl->cbcp.required)
76538174Sbrian          cbcp_Down(&dl->cbcp);
766102413Scharnier        /* FALLTHROUGH (just in case) */
76738174Sbrian
76836285Sbrian      case DATALINK_AUTH:
76936285Sbrian        timer_Stop(&dl->pap.authtimer);
77036285Sbrian        timer_Stop(&dl->chap.auth.authtimer);
77136285Sbrian    }
77236285Sbrian    datalink_NewState(dl, DATALINK_LCP);
77344094Sbrian    datalink_AuthReInit(dl);
77436285Sbrian  }
77536285Sbrian}
77636285Sbrian
77736285Sbrianstatic void
77836285Sbriandatalink_LayerFinish(void *v, struct fsm *fp)
77936285Sbrian{
78036285Sbrian  /* The given fsm is now down */
78136285Sbrian  struct datalink *dl = (struct datalink *)v;
78236285Sbrian
78336285Sbrian  if (fp->proto == PROTO_LCP) {
78437060Sbrian    fsm2initial(fp);
78536285Sbrian    (*dl->parent->LayerFinish)(dl->parent->object, fp);
78637007Sbrian    datalink_ComeDown(dl, CLOSE_NORMAL);
78736285Sbrian  } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
78836285Sbrian    fsm_Open(fp);		/* CCP goes to ST_STOPPED */
78936285Sbrian}
79036285Sbrian
79136285Sbrianstruct datalink *
79236285Sbriandatalink_Create(const char *name, struct bundle *bundle, int type)
79336285Sbrian{
79436285Sbrian  struct datalink *dl;
79536285Sbrian
79636285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
79736285Sbrian  if (dl == NULL)
79836285Sbrian    return dl;
79936285Sbrian
80036285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
80136285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
80236285Sbrian  dl->desc.IsSet = datalink_IsSet;
80336285Sbrian  dl->desc.Read = datalink_Read;
80436285Sbrian  dl->desc.Write = datalink_Write;
80536285Sbrian
80636285Sbrian  dl->state = DATALINK_CLOSED;
80736285Sbrian
80836285Sbrian  *dl->cfg.script.dial = '\0';
80936285Sbrian  *dl->cfg.script.login = '\0';
81052488Sbrian  *dl->cfg.script.logout = '\0';
81136285Sbrian  *dl->cfg.script.hangup = '\0';
81236285Sbrian  *dl->cfg.phone.list = '\0';
81336285Sbrian  *dl->phone.list = '\0';
81436285Sbrian  dl->phone.next = NULL;
81536285Sbrian  dl->phone.alt = NULL;
81636285Sbrian  dl->phone.chosen = "N/A";
81737007Sbrian  dl->stayonline = 0;
81836285Sbrian  dl->script.run = 1;
81936285Sbrian  dl->script.packetmode = 1;
82036285Sbrian  mp_linkInit(&dl->mp);
82136285Sbrian
82236285Sbrian  dl->bundle = bundle;
82336285Sbrian  dl->next = NULL;
82436285Sbrian
82544468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
82636285Sbrian
82744468Sbrian  dl->dial.tries = 0;
82836285Sbrian  dl->cfg.dial.max = 1;
82936285Sbrian  dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
83036285Sbrian  dl->cfg.dial.timeout = DIAL_TIMEOUT;
83144468Sbrian  dl->cfg.dial.inc = 0;
83244468Sbrian  dl->cfg.dial.maxinc = 10;
83336285Sbrian
83436285Sbrian  dl->reconnect_tries = 0;
83536285Sbrian  dl->cfg.reconnect.max = 0;
83636285Sbrian  dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
83736285Sbrian
83838174Sbrian  dl->cfg.callback.opmask = 0;
83938174Sbrian  dl->cfg.cbcp.delay = 0;
84038174Sbrian  *dl->cfg.cbcp.phone = '\0';
84138174Sbrian  dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
84238174Sbrian
84336285Sbrian  dl->name = strdup(name);
84436285Sbrian  peerid_Init(&dl->peer);
84536285Sbrian  dl->parent = &bundle->fsm;
84636285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
84736285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
84836285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
84936285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
85036285Sbrian  dl->fsmp.object = dl;
85136285Sbrian
85246686Sbrian  if ((dl->physical = physical_Create(dl, type)) == NULL) {
85336285Sbrian    free(dl->name);
85436285Sbrian    free(dl);
85536285Sbrian    return NULL;
85636285Sbrian  }
85743693Sbrian
85843693Sbrian  pap_Init(&dl->pap, dl->physical);
85943693Sbrian  chap_Init(&dl->chap, dl->physical);
86038174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
86136285Sbrian
86252488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
86354055Sbrian  chat_Init(&dl->chat, dl->physical);
86452488Sbrian
86536285Sbrian  log_Printf(LogPHASE, "%s: Created in %s state\n",
86636285Sbrian             dl->name, datalink_State(dl));
86736285Sbrian
86836285Sbrian  return dl;
86936285Sbrian}
87036285Sbrian
87136285Sbrianstruct datalink *
87236285Sbriandatalink_Clone(struct datalink *odl, const char *name)
87336285Sbrian{
87436285Sbrian  struct datalink *dl;
87536285Sbrian
87636285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
87736285Sbrian  if (dl == NULL)
87836285Sbrian    return dl;
87936285Sbrian
88036285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
88136285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
88236285Sbrian  dl->desc.IsSet = datalink_IsSet;
88336285Sbrian  dl->desc.Read = datalink_Read;
88436285Sbrian  dl->desc.Write = datalink_Write;
88536285Sbrian
88636285Sbrian  dl->state = DATALINK_CLOSED;
88736285Sbrian
88836285Sbrian  memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
88936285Sbrian  mp_linkInit(&dl->mp);
89036285Sbrian  *dl->phone.list = '\0';
89136285Sbrian  dl->phone.next = NULL;
89236285Sbrian  dl->phone.alt = NULL;
89336285Sbrian  dl->phone.chosen = "N/A";
89436285Sbrian  dl->bundle = odl->bundle;
89536285Sbrian  dl->next = NULL;
89644468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
89744468Sbrian  dl->dial.tries = 0;
89836285Sbrian  dl->reconnect_tries = 0;
89936285Sbrian  dl->name = strdup(name);
90036285Sbrian  peerid_Init(&dl->peer);
90136285Sbrian  dl->parent = odl->parent;
90236285Sbrian  memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
90336285Sbrian  dl->fsmp.object = dl;
90436285Sbrian
90546686Sbrian  if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
90636285Sbrian    free(dl->name);
90736285Sbrian    free(dl);
90836285Sbrian    return NULL;
90936285Sbrian  }
91043693Sbrian  pap_Init(&dl->pap, dl->physical);
91144305Sbrian  dl->pap.cfg = odl->pap.cfg;
91243693Sbrian
91343693Sbrian  chap_Init(&dl->chap, dl->physical);
91444305Sbrian  dl->chap.auth.cfg = odl->chap.auth.cfg;
91543693Sbrian
91636285Sbrian  memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
91736285Sbrian  memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
91836285Sbrian         sizeof dl->physical->link.lcp.cfg);
91936285Sbrian  memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
92036285Sbrian         sizeof dl->physical->link.ccp.cfg);
92136285Sbrian  memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
92236285Sbrian         sizeof dl->physical->async.cfg);
92336285Sbrian
92438174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
92536285Sbrian
92652488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
92754055Sbrian  chat_Init(&dl->chat, dl->physical);
92852488Sbrian
92936285Sbrian  log_Printf(LogPHASE, "%s: Cloned in %s state\n",
93036285Sbrian             dl->name, datalink_State(dl));
93136285Sbrian
93236285Sbrian  return dl;
93336285Sbrian}
93436285Sbrian
93536285Sbrianstruct datalink *
93636285Sbriandatalink_Destroy(struct datalink *dl)
93736285Sbrian{
93836285Sbrian  struct datalink *result;
93936285Sbrian
94036285Sbrian  if (dl->state != DATALINK_CLOSED) {
94136285Sbrian    log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
94236285Sbrian              datalink_State(dl));
94336285Sbrian    switch (dl->state) {
94436285Sbrian      case DATALINK_HANGUP:
94536285Sbrian      case DATALINK_DIAL:
94636285Sbrian      case DATALINK_LOGIN:
94754055Sbrian        chat_Finish(&dl->chat);		/* Gotta blat the timers ! */
94836285Sbrian        break;
94936285Sbrian    }
95036285Sbrian  }
95136285Sbrian
95254055Sbrian  chat_Destroy(&dl->chat);
95344468Sbrian  timer_Stop(&dl->dial.timer);
95436285Sbrian  result = dl->next;
95546686Sbrian  physical_Destroy(dl->physical);
95636285Sbrian  free(dl->name);
95736285Sbrian  free(dl);
95836285Sbrian
95936285Sbrian  return result;
96036285Sbrian}
96136285Sbrian
96236285Sbrianvoid
96336285Sbriandatalink_Up(struct datalink *dl, int runscripts, int packetmode)
96436285Sbrian{
965112659Sbrian  if (!Enabled(dl->bundle, OPT_FORCE_SCRIPTS) &&
966112659Sbrian      (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED)))
96736285Sbrian    /* Ignore scripts */
96836285Sbrian    runscripts = 0;
96936285Sbrian
97036285Sbrian  switch (dl->state) {
97136285Sbrian    case DATALINK_CLOSED:
97236285Sbrian      if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
97336285Sbrian          bundle_Phase(dl->bundle) == PHASE_TERMINATE)
97436285Sbrian        bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
97536285Sbrian      datalink_NewState(dl, DATALINK_OPENING);
97636285Sbrian      dl->reconnect_tries =
97736285Sbrian        dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
97844468Sbrian      dl->dial.tries = dl->cfg.dial.max;
97936285Sbrian      dl->script.run = runscripts;
98036285Sbrian      dl->script.packetmode = packetmode;
98136285Sbrian      break;
98236285Sbrian
98336285Sbrian    case DATALINK_OPENING:
98436285Sbrian      if (!dl->script.run && runscripts)
98536285Sbrian        dl->script.run = 1;
986102413Scharnier      /* FALLTHROUGH */
98736285Sbrian
98836285Sbrian    case DATALINK_DIAL:
98936285Sbrian    case DATALINK_LOGIN:
99036285Sbrian    case DATALINK_READY:
99136285Sbrian      if (!dl->script.packetmode && packetmode) {
99236285Sbrian        dl->script.packetmode = 1;
99360945Sbrian        if (dl->state == DATALINK_READY) {
99460945Sbrian          dl->script.run = 0;
99560945Sbrian          datalink_NewState(dl, DATALINK_CARRIER);
99660945Sbrian        }
99736285Sbrian      }
99836285Sbrian      break;
99936285Sbrian  }
100036285Sbrian}
100136285Sbrian
100236285Sbrianvoid
100337007Sbriandatalink_Close(struct datalink *dl, int how)
100436285Sbrian{
100536285Sbrian  /* Please close */
100636285Sbrian  switch (dl->state) {
100736285Sbrian    case DATALINK_OPEN:
100836285Sbrian      peerid_Init(&dl->peer);
100937060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
1010102413Scharnier      /* FALLTHROUGH */
101136285Sbrian
101238174Sbrian    case DATALINK_CBCP:
101336285Sbrian    case DATALINK_AUTH:
101436285Sbrian    case DATALINK_LCP:
101544094Sbrian      datalink_AuthReInit(dl);
101671970Sbrian      if (how == CLOSE_LCP)
101771970Sbrian        datalink_DontHangup(dl);
101871970Sbrian      else if (how == CLOSE_STAYDOWN)
101971970Sbrian        datalink_StayDown(dl);
102036285Sbrian      fsm_Close(&dl->physical->link.lcp.fsm);
102152029Sbrian      break;
102236285Sbrian
102336285Sbrian    default:
102437007Sbrian      datalink_ComeDown(dl, how);
102536285Sbrian  }
102636285Sbrian}
102736285Sbrian
102836285Sbrianvoid
102937007Sbriandatalink_Down(struct datalink *dl, int how)
103036285Sbrian{
103136285Sbrian  /* Carrier is lost */
103236285Sbrian  switch (dl->state) {
103336285Sbrian    case DATALINK_OPEN:
103436285Sbrian      peerid_Init(&dl->peer);
103537060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
1036102413Scharnier      /* FALLTHROUGH */
103736285Sbrian
103838174Sbrian    case DATALINK_CBCP:
103936285Sbrian    case DATALINK_AUTH:
104036285Sbrian    case DATALINK_LCP:
104137060Sbrian      fsm2initial(&dl->physical->link.lcp.fsm);
104256656Sbrian      if (dl->state == DATALINK_OPENING)
104356656Sbrian        return;			/* we're doing a callback... */
1044102413Scharnier      /* FALLTHROUGH */
104536285Sbrian
104636285Sbrian    default:
104737007Sbrian      datalink_ComeDown(dl, how);
104836285Sbrian  }
104936285Sbrian}
105036285Sbrian
105136285Sbrianvoid
105236285Sbriandatalink_StayDown(struct datalink *dl)
105336285Sbrian{
105471970Sbrian  dl->dial.tries = -1;
105536285Sbrian  dl->reconnect_tries = 0;
105671970Sbrian  dl->stayonline = 0;
105736285Sbrian}
105836285Sbrian
105937007Sbrianvoid
106037007Sbriandatalink_DontHangup(struct datalink *dl)
106137007Sbrian{
106271970Sbrian  dl->dial.tries = -1;
106371970Sbrian  dl->reconnect_tries = 0;
106471970Sbrian  dl->stayonline = dl->state >= DATALINK_LCP ? 1 : 0;
106537007Sbrian}
106637007Sbrian
106736285Sbrianint
106836285Sbriandatalink_Show(struct cmdargs const *arg)
106936285Sbrian{
107036285Sbrian  prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
107138174Sbrian  prompt_Printf(arg->prompt, " State:              %s\n",
107236285Sbrian                datalink_State(arg->cx));
107338174Sbrian  prompt_Printf(arg->prompt, " Peer name:          ");
107436285Sbrian  if (*arg->cx->peer.authname)
107536285Sbrian    prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
107636285Sbrian  else if (arg->cx->state == DATALINK_OPEN)
107736285Sbrian    prompt_Printf(arg->prompt, "None requested\n");
107836285Sbrian  else
107936285Sbrian    prompt_Printf(arg->prompt, "N/A\n");
108038174Sbrian  prompt_Printf(arg->prompt, " Discriminator:      %s\n",
108136285Sbrian                mp_Enddisc(arg->cx->peer.enddisc.class,
108236285Sbrian                           arg->cx->peer.enddisc.address,
108336285Sbrian                           arg->cx->peer.enddisc.len));
108436285Sbrian
108536285Sbrian  prompt_Printf(arg->prompt, "\nDefaults:\n");
108638174Sbrian  prompt_Printf(arg->prompt, " Phone List:         %s\n",
108736285Sbrian                arg->cx->cfg.phone.list);
108836285Sbrian  if (arg->cx->cfg.dial.max)
108938174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
109036285Sbrian                  arg->cx->cfg.dial.max);
109136285Sbrian  else
109238174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
109344261Sbrian  if (arg->cx->cfg.dial.next_timeout >= 0)
109436285Sbrian    prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
109536285Sbrian  else
109636285Sbrian    prompt_Printf(arg->prompt, "random/");
109744261Sbrian  if (arg->cx->cfg.dial.timeout >= 0)
109836285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
109936285Sbrian  else
110036285Sbrian    prompt_Printf(arg->prompt, "random\n");
110138174Sbrian  prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
110236285Sbrian                arg->cx->cfg.reconnect.max);
110336285Sbrian  if (arg->cx->cfg.reconnect.timeout > 0)
110436285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
110536285Sbrian  else
110636285Sbrian    prompt_Printf(arg->prompt, "random\n");
110738174Sbrian  prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
110838174Sbrian                PHYS_DIRECT ?  "accepted: " : "requested:");
110938174Sbrian  if (!arg->cx->cfg.callback.opmask)
111038174Sbrian    prompt_Printf(arg->prompt, "none\n");
111138174Sbrian  else {
111238174Sbrian    int comma = 0;
111338174Sbrian
111438174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
111538174Sbrian      prompt_Printf(arg->prompt, "none");
111638174Sbrian      comma = 1;
111738174Sbrian    }
111838174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
111938174Sbrian      prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
112038174Sbrian      comma = 1;
112138174Sbrian    }
112238174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
112338174Sbrian      prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
112438174Sbrian      if (arg->cx->physical->type != PHYS_DIRECT)
112538174Sbrian        prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
112638174Sbrian      comma = 1;
112738174Sbrian    }
112838174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
112938174Sbrian      prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
113038174Sbrian      prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
113138174Sbrian                    arg->cx->cfg.cbcp.delay);
113240483Sbrian      prompt_Printf(arg->prompt, "                     phone: ");
113340483Sbrian      if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
113440483Sbrian        if (arg->cx->physical->type & PHYS_DIRECT)
113540483Sbrian          prompt_Printf(arg->prompt, "Caller decides\n");
113640483Sbrian        else
113740483Sbrian          prompt_Printf(arg->prompt, "Dialback server decides\n");
113840483Sbrian      } else
113940483Sbrian        prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
114038174Sbrian      prompt_Printf(arg->prompt, "                     timeout: %lds\n",
114138174Sbrian                    arg->cx->cfg.cbcp.fsmretry);
114238174Sbrian    } else
114338174Sbrian      prompt_Printf(arg->prompt, "\n");
114438174Sbrian  }
114538174Sbrian
114638174Sbrian  prompt_Printf(arg->prompt, " Dial Script:        %s\n",
114736285Sbrian                arg->cx->cfg.script.dial);
114838174Sbrian  prompt_Printf(arg->prompt, " Login Script:       %s\n",
114936285Sbrian                arg->cx->cfg.script.login);
115052488Sbrian  prompt_Printf(arg->prompt, " Logout Script:      %s\n",
115152488Sbrian                arg->cx->cfg.script.logout);
115238174Sbrian  prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
115336285Sbrian                arg->cx->cfg.script.hangup);
115436285Sbrian  return 0;
115536285Sbrian}
115636285Sbrian
115736285Sbrianint
115836285Sbriandatalink_SetReconnect(struct cmdargs const *arg)
115936285Sbrian{
116036285Sbrian  if (arg->argc == arg->argn+2) {
116136285Sbrian    arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
116236285Sbrian    arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
116336285Sbrian    return 0;
116436285Sbrian  }
116536285Sbrian  return -1;
116636285Sbrian}
116736285Sbrian
116836285Sbrianint
116936285Sbriandatalink_SetRedial(struct cmdargs const *arg)
117036285Sbrian{
117144468Sbrian  const char *sep, *osep;
117244468Sbrian  int timeout, inc, maxinc, tries;
117336285Sbrian
117436285Sbrian  if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
117536285Sbrian    if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
117636285Sbrian	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
117736285Sbrian      arg->cx->cfg.dial.timeout = -1;
117836285Sbrian      randinit();
117936285Sbrian    } else {
118036285Sbrian      timeout = atoi(arg->argv[arg->argn]);
118136285Sbrian
118236285Sbrian      if (timeout >= 0)
118336285Sbrian	arg->cx->cfg.dial.timeout = timeout;
118436285Sbrian      else {
118536285Sbrian	log_Printf(LogWARN, "Invalid redial timeout\n");
118636285Sbrian	return -1;
118736285Sbrian      }
118836285Sbrian    }
118936285Sbrian
119044468Sbrian    sep = strchr(arg->argv[arg->argn], '+');
119144468Sbrian    if (sep) {
119244468Sbrian      inc = atoi(++sep);
119344468Sbrian      osep = sep;
119444468Sbrian      if (inc >= 0)
119544468Sbrian        arg->cx->cfg.dial.inc = inc;
119644468Sbrian      else {
119744468Sbrian        log_Printf(LogWARN, "Invalid timeout increment\n");
119844468Sbrian        return -1;
119944468Sbrian      }
120044468Sbrian      sep = strchr(sep, '-');
120144468Sbrian      if (sep) {
120244468Sbrian        maxinc = atoi(++sep);
120344468Sbrian        if (maxinc >= 0)
120444468Sbrian          arg->cx->cfg.dial.maxinc = maxinc;
120544468Sbrian        else {
120644468Sbrian          log_Printf(LogWARN, "Invalid maximum timeout increments\n");
120744468Sbrian          return -1;
120844468Sbrian        }
120944468Sbrian      } else {
121044468Sbrian        /* Default timeout increment */
121144468Sbrian        arg->cx->cfg.dial.maxinc = 10;
121244468Sbrian        sep = osep;
121344468Sbrian      }
121444468Sbrian    } else {
121544468Sbrian      /* Default timeout increment & max increment */
121644468Sbrian      arg->cx->cfg.dial.inc = 0;
121744468Sbrian      arg->cx->cfg.dial.maxinc = 10;
121844468Sbrian      sep = arg->argv[arg->argn];
121944468Sbrian    }
122044468Sbrian
122144468Sbrian    sep = strchr(sep, '.');
122244468Sbrian    if (sep) {
122344468Sbrian      if (strcasecmp(++sep, "random") == 0) {
122436285Sbrian	arg->cx->cfg.dial.next_timeout = -1;
122536285Sbrian	randinit();
122636285Sbrian      } else {
122744468Sbrian	timeout = atoi(sep);
122836285Sbrian	if (timeout >= 0)
122936285Sbrian	  arg->cx->cfg.dial.next_timeout = timeout;
123036285Sbrian	else {
123136285Sbrian	  log_Printf(LogWARN, "Invalid next redial timeout\n");
123236285Sbrian	  return -1;
123336285Sbrian	}
123436285Sbrian      }
123536285Sbrian    } else
123636285Sbrian      /* Default next timeout */
123736285Sbrian      arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
123836285Sbrian
123936285Sbrian    if (arg->argc == arg->argn+2) {
124036285Sbrian      tries = atoi(arg->argv[arg->argn+1]);
124136285Sbrian
124236285Sbrian      if (tries >= 0) {
124336285Sbrian	arg->cx->cfg.dial.max = tries;
124436285Sbrian      } else {
124536285Sbrian	log_Printf(LogWARN, "Invalid retry value\n");
124636285Sbrian	return 1;
124736285Sbrian      }
124836285Sbrian    }
124936285Sbrian    return 0;
125036285Sbrian  }
125144468Sbrian
125236285Sbrian  return -1;
125336285Sbrian}
125436285Sbrian
125555146Sbrianstatic const char * const states[] = {
125636285Sbrian  "closed",
125736285Sbrian  "opening",
125836285Sbrian  "hangup",
125936285Sbrian  "dial",
126049472Sbrian  "carrier",
126152488Sbrian  "logout",
126236285Sbrian  "login",
126336285Sbrian  "ready",
126436285Sbrian  "lcp",
126536285Sbrian  "auth",
126638174Sbrian  "cbcp",
126736285Sbrian  "open"
126836285Sbrian};
126936285Sbrian
127036285Sbrianconst char *
127136285Sbriandatalink_State(struct datalink *dl)
127236285Sbrian{
1273134789Sbrian  if (dl->state >= sizeof states / sizeof states[0])
127436285Sbrian    return "unknown";
127536285Sbrian  return states[dl->state];
127636285Sbrian}
127736285Sbrian
127836285Sbrianstatic void
1279134789Sbriandatalink_NewState(struct datalink *dl, unsigned state)
128036285Sbrian{
128136285Sbrian  if (state != dl->state) {
1282134789Sbrian    if (state < sizeof states / sizeof states[0]) {
128336285Sbrian      log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
128436285Sbrian                 states[state]);
128536285Sbrian      dl->state = state;
128636285Sbrian    } else
128736285Sbrian      log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
128836285Sbrian  }
128936285Sbrian}
129036285Sbrian
129136285Sbrianstruct datalink *
129236285Sbrianiov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
129352942Sbrian             int fd, int *auxfd, int *nauxfd)
129436285Sbrian{
129536285Sbrian  struct datalink *dl, *cdl;
129644305Sbrian  struct fsm_retry copy;
1297136375Sbrian  char *oname, *pname;
129836285Sbrian
129936285Sbrian  dl = (struct datalink *)iov[(*niov)++].iov_base;
130036285Sbrian  dl->name = iov[*niov].iov_base;
130136285Sbrian
130236285Sbrian  if (dl->name[DATALINK_MAXNAME-1]) {
130336285Sbrian    dl->name[DATALINK_MAXNAME-1] = '\0';
130436285Sbrian    if (strlen(dl->name) == DATALINK_MAXNAME - 1)
130536285Sbrian      log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
130636285Sbrian  }
130736285Sbrian
130836285Sbrian  /* Make sure the name is unique ! */
130936285Sbrian  oname = NULL;
131036285Sbrian  do {
131136285Sbrian    for (cdl = bundle->links; cdl; cdl = cdl->next)
131236285Sbrian      if (!strcasecmp(dl->name, cdl->name)) {
1313136375Sbrian        if ((pname = datalink_NextName(dl)) == NULL) {
1314136375Sbrian	  for ((*niov)--; *niov < maxiov; (*niov)++)
1315136375Sbrian	    free(iov[*niov].iov_base);
1316136375Sbrian	  return NULL;
1317136375Sbrian	} else if (oname)
1318136375Sbrian          free(pname);
131936285Sbrian        else
1320136375Sbrian          oname = pname;
132136285Sbrian        break;	/* Keep renaming 'till we have no conflicts */
132236285Sbrian      }
132336285Sbrian  } while (cdl);
132436285Sbrian
132536285Sbrian  if (oname) {
132636285Sbrian    log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
132736285Sbrian    free(oname);
132836285Sbrian  } else {
132936285Sbrian    dl->name = strdup(dl->name);
133036285Sbrian    free(iov[*niov].iov_base);
133136285Sbrian  }
133236285Sbrian  (*niov)++;
133336285Sbrian
133436285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
133536285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
133636285Sbrian  dl->desc.IsSet = datalink_IsSet;
133736285Sbrian  dl->desc.Read = datalink_Read;
133836285Sbrian  dl->desc.Write = datalink_Write;
133936285Sbrian
134036285Sbrian  mp_linkInit(&dl->mp);
134136285Sbrian  *dl->phone.list = '\0';
134236285Sbrian  dl->phone.next = NULL;
134336285Sbrian  dl->phone.alt = NULL;
134436285Sbrian  dl->phone.chosen = "N/A";
134536285Sbrian
134636285Sbrian  dl->bundle = bundle;
134736285Sbrian  dl->next = NULL;
134844468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
134944468Sbrian  dl->dial.tries = 0;
135036285Sbrian  dl->reconnect_tries = 0;
135136285Sbrian  dl->parent = &bundle->fsm;
135236285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
135336285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
135436285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
135536285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
135636285Sbrian  dl->fsmp.object = dl;
135736285Sbrian
135852942Sbrian  dl->physical = iov2physical(dl, iov, niov, maxiov, fd, auxfd, nauxfd);
135936285Sbrian
136036285Sbrian  if (!dl->physical) {
136136285Sbrian    free(dl->name);
136236285Sbrian    free(dl);
136336285Sbrian    dl = NULL;
136436285Sbrian  } else {
136544305Sbrian    copy = dl->pap.cfg.fsm;
136643693Sbrian    pap_Init(&dl->pap, dl->physical);
136744305Sbrian    dl->pap.cfg.fsm = copy;
136843693Sbrian
136944305Sbrian    copy = dl->chap.auth.cfg.fsm;
137043693Sbrian    chap_Init(&dl->chap, dl->physical);
137144305Sbrian    dl->chap.auth.cfg.fsm = copy;
137243693Sbrian
137338174Sbrian    cbcp_Init(&dl->cbcp, dl->physical);
137436285Sbrian
137552488Sbrian    memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
137654055Sbrian    chat_Init(&dl->chat, dl->physical);
137752488Sbrian
137836285Sbrian    log_Printf(LogPHASE, "%s: Transferred in %s state\n",
137936285Sbrian              dl->name, datalink_State(dl));
138036285Sbrian  }
138136285Sbrian
138236285Sbrian  return dl;
138336285Sbrian}
138436285Sbrian
138536285Sbrianint
138636450Sbriandatalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
138753684Sbrian             int *auxfd, int *nauxfd)
138836285Sbrian{
138936285Sbrian  /* If `dl' is NULL, we're allocating before a Fromiov() */
139036285Sbrian  int link_fd;
139136285Sbrian
139236285Sbrian  if (dl) {
139344468Sbrian    timer_Stop(&dl->dial.timer);
139438174Sbrian    /* The following is purely for the sake of paranoia */
139538174Sbrian    cbcp_Down(&dl->cbcp);
139636285Sbrian    timer_Stop(&dl->pap.authtimer);
139736285Sbrian    timer_Stop(&dl->chap.auth.authtimer);
139836285Sbrian  }
139936285Sbrian
140036285Sbrian  if (*niov >= maxiov - 1) {
140136285Sbrian    log_Printf(LogERROR, "Toiov: No room for datalink !\n");
140236285Sbrian    if (dl) {
140336285Sbrian      free(dl->name);
140436285Sbrian      free(dl);
140536285Sbrian    }
140636285Sbrian    return -1;
140736285Sbrian  }
140836285Sbrian
140953684Sbrian  iov[*niov].iov_base = (void *)dl;
141036285Sbrian  iov[(*niov)++].iov_len = sizeof *dl;
141153684Sbrian  iov[*niov].iov_base = dl ? realloc(dl->name, DATALINK_MAXNAME) : NULL;
141236285Sbrian  iov[(*niov)++].iov_len = DATALINK_MAXNAME;
141336285Sbrian
141452942Sbrian  link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, auxfd,
141553684Sbrian                         nauxfd);
141636285Sbrian
141736285Sbrian  if (link_fd == -1 && dl) {
141836285Sbrian    free(dl->name);
141936285Sbrian    free(dl);
142036285Sbrian  }
142136285Sbrian
142236285Sbrian  return link_fd;
142336285Sbrian}
142436285Sbrian
142536285Sbrianvoid
142636285Sbriandatalink_Rename(struct datalink *dl, const char *name)
142736285Sbrian{
142836285Sbrian  free(dl->name);
142936285Sbrian  dl->physical->link.name = dl->name = strdup(name);
143036285Sbrian}
143136285Sbrian
1432136375Sbrianstatic char *
143336285Sbriandatalink_NextName(struct datalink *dl)
143436285Sbrian{
143536285Sbrian  int f, n;
143636285Sbrian  char *name, *oname;
143736285Sbrian
143836285Sbrian  n = strlen(dl->name);
1439136375Sbrian  if ((name = (char *)malloc(n+3)) == NULL) {
1440136375Sbrian    log_Printf(LogERROR, "datalink_NextName: Out of memory !\n");
1441136375Sbrian    return NULL;
1442136375Sbrian  }
144336285Sbrian  for (f = n - 1; f >= 0; f--)
144436285Sbrian    if (!isdigit(dl->name[f]))
144536285Sbrian      break;
144636285Sbrian  n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
144736285Sbrian  sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
144836285Sbrian  oname = dl->name;
144936345Sbrian  dl->name = name;
145036345Sbrian  /* our physical link name isn't updated (it probably isn't created yet) */
145136285Sbrian  return oname;
145236285Sbrian}
145336285Sbrian
145436285Sbrianint
145536285Sbriandatalink_SetMode(struct datalink *dl, int mode)
145636285Sbrian{
145736285Sbrian  if (!physical_SetMode(dl->physical, mode))
145836285Sbrian    return 0;
145936285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
146036285Sbrian    dl->script.run = 0;
146136285Sbrian  if (dl->physical->type == PHYS_DIRECT)
146236285Sbrian    dl->reconnect_tries = 0;
146353830Sbrian  if (mode & (PHYS_DDIAL|PHYS_BACKGROUND|PHYS_FOREGROUND) &&
146453830Sbrian      dl->state <= DATALINK_READY)
146536285Sbrian    datalink_Up(dl, 1, 1);
146636285Sbrian  return 1;
146736285Sbrian}
146844468Sbrian
146944468Sbrianint
147044468Sbriandatalink_GetDialTimeout(struct datalink *dl)
147144468Sbrian{
147244468Sbrian  int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
147344468Sbrian
147444468Sbrian  if (dl->dial.incs < dl->cfg.dial.maxinc)
147544468Sbrian    dl->dial.incs++;
147644468Sbrian
147744468Sbrian  return result;
147844468Sbrian}
1479