datalink.c revision 67912
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 67912 2000-10-30 00:15:29Z 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 "descriptor.h"
4936285Sbrian#include "lqr.h"
5036285Sbrian#include "hdlc.h"
5163484Sbrian#include "lcp.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)
8659084Sbrian    log_Printf(LogCHAT, "%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
12354055Sbrian  chat_Finish(&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);
16058455Sbrian    if (!dl->bundle->CleaningUp &&
16158455Sbrian        !(dl->physical->type & (PHYS_DIRECT|PHYS_BACKGROUND|PHYS_FOREGROUND)))
16244468Sbrian      datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
16336285Sbrian  } else {
16436285Sbrian    datalink_NewState(dl, DATALINK_OPENING);
16552412Sbrian    if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
16652412Sbrian        bundle_Phase(dl->bundle) == PHASE_TERMINATE)
16745385Sbrian      bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
16844468Sbrian    if (dl->dial.tries < 0) {
16936285Sbrian      datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
17044468Sbrian      dl->dial.tries = dl->cfg.dial.max;
17144468Sbrian      dl->dial.incs = 0;
17236285Sbrian      dl->reconnect_tries--;
17359084Sbrian      log_Printf(LogCHAT, "%s: Reconnect try %d of %d\n",
17459084Sbrian                 dl->name, dl->cfg.reconnect.max - dl->reconnect_tries,
17559084Sbrian                 dl->cfg.reconnect.max);
17659084Sbrian      bundle_Notify(dl->bundle, EX_RECONNECT);
17736285Sbrian    } else {
17836285Sbrian      if (dl->phone.next == NULL)
17944468Sbrian        datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
18036285Sbrian      else
18136285Sbrian        datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
18259084Sbrian      bundle_Notify(dl->bundle, EX_REDIAL);
18336285Sbrian    }
18436285Sbrian  }
18536285Sbrian}
18636285Sbrian
18749472Sbrianconst char *
18836285Sbriandatalink_ChoosePhoneNumber(struct datalink *dl)
18936285Sbrian{
19036285Sbrian  char *phone;
19136285Sbrian
19236285Sbrian  if (dl->phone.alt == NULL) {
19336285Sbrian    if (dl->phone.next == NULL) {
19436285Sbrian      strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
19536285Sbrian      dl->phone.list[sizeof dl->phone.list - 1] = '\0';
19648003Sbrian      if (*dl->phone.list == '\0')
19748003Sbrian        return "";
19836285Sbrian      dl->phone.next = dl->phone.list;
19936285Sbrian    }
20036285Sbrian    dl->phone.alt = strsep(&dl->phone.next, ":");
20136285Sbrian  }
20236285Sbrian  phone = strsep(&dl->phone.alt, "|");
20336285Sbrian  dl->phone.chosen = *phone ? phone : "[NONE]";
20436285Sbrian  if (*phone)
20559084Sbrian    log_Printf(LogCHAT, "Phone: %s\n", phone);
20636285Sbrian  return phone;
20736285Sbrian}
20836285Sbrian
20936285Sbrianstatic void
21036285Sbriandatalink_LoginDone(struct datalink *dl)
21136285Sbrian{
21254055Sbrian  chat_Finish(&dl->chat);
21352488Sbrian
21436285Sbrian  if (!dl->script.packetmode) {
21544468Sbrian    dl->dial.tries = -1;
21644468Sbrian    dl->dial.incs = 0;
21736285Sbrian    datalink_NewState(dl, DATALINK_READY);
21846686Sbrian  } else if (!physical_Raw(dl->physical)) {
21944468Sbrian    dl->dial.tries = 0;
22036285Sbrian    log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
22136285Sbrian    if (dl->script.run) {
22252488Sbrian      datalink_NewState(dl, DATALINK_LOGOUT);
22354914Sbrian      if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
22454914Sbrian        log_Printf(LogWARN, "Invalid logout script\n");
22536285Sbrian    } else {
22647061Sbrian      physical_StopDeviceTimer(dl->physical);
22736285Sbrian      if (dl->physical->type == PHYS_DEDICATED)
22836285Sbrian        /* force a redial timeout */
22946686Sbrian        physical_Close(dl->physical);
23036285Sbrian      datalink_HangupDone(dl);
23136285Sbrian    }
23236285Sbrian  } else {
23344468Sbrian    dl->dial.tries = -1;
23444468Sbrian    dl->dial.incs = 0;
23536285Sbrian
23636285Sbrian    hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
23736285Sbrian    async_Init(&dl->physical->async);
23836285Sbrian
23936285Sbrian    lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
24036285Sbrian              0 : dl->physical->link.lcp.cfg.openmode);
24136285Sbrian    ccp_Setup(&dl->physical->link.ccp);
24236285Sbrian
24336285Sbrian    datalink_NewState(dl, DATALINK_LCP);
24436285Sbrian    fsm_Up(&dl->physical->link.lcp.fsm);
24536285Sbrian    fsm_Open(&dl->physical->link.lcp.fsm);
24636285Sbrian  }
24736285Sbrian}
24836285Sbrian
24936285Sbrianstatic int
25058028Sbriandatalink_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
25136285Sbrian                   int *n)
25236285Sbrian{
25336285Sbrian  struct datalink *dl = descriptor2datalink(d);
25436285Sbrian  int result;
25536285Sbrian
25636285Sbrian  result = 0;
25736285Sbrian  switch (dl->state) {
25836285Sbrian    case DATALINK_CLOSED:
25953830Sbrian      if ((dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|
26053830Sbrian                                 PHYS_FOREGROUND|PHYS_DDIAL)) &&
26147863Sbrian          !dl->bundle->CleaningUp)
26236285Sbrian        /*
26336465Sbrian         * Our first time in - DEDICATED & DDIAL never come down, and
26453830Sbrian         * DIRECT, FOREGROUND & BACKGROUND get deleted when they enter
26553830Sbrian         * DATALINK_CLOSED.  Go to DATALINK_OPENING via datalink_Up()
26653830Sbrian         * and fall through.
26736285Sbrian         */
26836285Sbrian        datalink_Up(dl, 1, 1);
26936285Sbrian      else
27036285Sbrian        break;
27136285Sbrian      /* fall through */
27236285Sbrian
27336285Sbrian    case DATALINK_OPENING:
27444468Sbrian      if (dl->dial.timer.state != TIMER_RUNNING) {
27544468Sbrian        if (--dl->dial.tries < 0)
27644468Sbrian          dl->dial.tries = 0;
27746686Sbrian        if (physical_Open(dl->physical, dl->bundle) >= 0) {
27838200Sbrian          log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
27938200Sbrian                           "Type `~?' for help\r\n", dl->name,
28038200Sbrian                           dl->physical->name.full);
28136285Sbrian          if (dl->script.run) {
28236285Sbrian            datalink_NewState(dl, DATALINK_DIAL);
28354914Sbrian            if (!chat_Setup(&dl->chat, dl->cfg.script.dial,
28454914Sbrian                            *dl->cfg.script.dial ?
28554914Sbrian                            datalink_ChoosePhoneNumber(dl) : ""))
28654914Sbrian              log_Printf(LogWARN, "Invalid dial script\n");
28736465Sbrian            if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
28836285Sbrian                dl->cfg.dial.max)
28936285Sbrian              log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
29044468Sbrian                        dl->name, dl->cfg.dial.max - dl->dial.tries,
29136285Sbrian                        dl->cfg.dial.max);
29236285Sbrian          } else
29351978Sbrian            datalink_NewState(dl, DATALINK_CARRIER);
29441830Sbrian          return datalink_UpdateSet(d, r, w, e, n);
29536285Sbrian        } else {
29636465Sbrian          if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
29736285Sbrian              dl->cfg.dial.max)
29846686Sbrian            log_Printf(LogCHAT, "Failed to open device (attempt %u of %d)\n",
29944468Sbrian                       dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
30036285Sbrian          else
30146686Sbrian            log_Printf(LogCHAT, "Failed to open device\n");
30236285Sbrian
30336285Sbrian          if (dl->bundle->CleaningUp ||
30436465Sbrian              (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
30544468Sbrian               dl->cfg.dial.max && dl->dial.tries == 0)) {
30636285Sbrian            datalink_NewState(dl, DATALINK_CLOSED);
30736285Sbrian            dl->reconnect_tries = 0;
30844468Sbrian            dl->dial.tries = -1;
30938200Sbrian            log_WritePrompts(dl, "Failed to open %s\n",
31038200Sbrian                             dl->physical->name.full);
31136285Sbrian            bundle_LinkClosed(dl->bundle, dl);
31236285Sbrian          }
31338200Sbrian          if (!dl->bundle->CleaningUp) {
31444468Sbrian            int timeout;
31544468Sbrian
31644468Sbrian            timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
31759084Sbrian            bundle_Notify(dl->bundle, EX_REDIAL);
31838200Sbrian            log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
31944261Sbrian                             dl->physical->name.full, timeout);
32038200Sbrian          }
32136285Sbrian        }
32236285Sbrian      }
32336285Sbrian      break;
32436285Sbrian
32549472Sbrian    case DATALINK_CARRIER:
32649472Sbrian      /* Wait for carrier on the device */
32749472Sbrian      switch (physical_AwaitCarrier(dl->physical)) {
32849472Sbrian        case CARRIER_PENDING:
32949472Sbrian          log_Printf(LogDEBUG, "Waiting for carrier\n");
33049472Sbrian          return 0;	/* A device timer is running to wake us up again */
33149472Sbrian
33249472Sbrian        case CARRIER_OK:
33351978Sbrian          if (dl->script.run) {
33451978Sbrian            datalink_NewState(dl, DATALINK_LOGIN);
33554914Sbrian            if (!chat_Setup(&dl->chat, dl->cfg.script.login, NULL))
33654914Sbrian              log_Printf(LogWARN, "Invalid login script\n");
33751978Sbrian          } else
33851978Sbrian            datalink_LoginDone(dl);
33949472Sbrian          return datalink_UpdateSet(d, r, w, e, n);
34049472Sbrian
34149472Sbrian        case CARRIER_LOST:
34249472Sbrian          physical_Offline(dl->physical);	/* Is this required ? */
34351978Sbrian          if (dl->script.run) {
34451978Sbrian            datalink_NewState(dl, DATALINK_HANGUP);
34554914Sbrian            if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
34654914Sbrian              log_Printf(LogWARN, "Invalid hangup script\n");
34753070Sbrian            return datalink_UpdateSet(d, r, w, e, n);
34853070Sbrian          } else {
34951978Sbrian            datalink_HangupDone(dl);
35053070Sbrian            return 0;	/* Maybe bundle_CleanDatalinks() has something to do */
35153070Sbrian          }
35249472Sbrian      }
35349472Sbrian
35436285Sbrian    case DATALINK_HANGUP:
35536285Sbrian    case DATALINK_DIAL:
35652488Sbrian    case DATALINK_LOGOUT:
35736285Sbrian    case DATALINK_LOGIN:
35836285Sbrian      result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
35936285Sbrian      switch (dl->chat.state) {
36036285Sbrian        case CHAT_DONE:
36136285Sbrian          /* script succeeded */
36236285Sbrian          switch(dl->state) {
36336285Sbrian            case DATALINK_HANGUP:
36436285Sbrian              datalink_HangupDone(dl);
36536285Sbrian              break;
36636285Sbrian            case DATALINK_DIAL:
36749472Sbrian              datalink_NewState(dl, DATALINK_CARRIER);
36836285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
36952488Sbrian            case DATALINK_LOGOUT:
37052488Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
37152488Sbrian              physical_Offline(dl->physical);
37254914Sbrian              if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
37354914Sbrian                log_Printf(LogWARN, "Invalid hangup script\n");
37452488Sbrian              return datalink_UpdateSet(d, r, w, e, n);
37536285Sbrian            case DATALINK_LOGIN:
37642390Sbrian              dl->phone.alt = NULL;
37736285Sbrian              datalink_LoginDone(dl);
37842905Sbrian              return datalink_UpdateSet(d, r, w, e, n);
37936285Sbrian          }
38036285Sbrian          break;
38136285Sbrian        case CHAT_FAILED:
38236285Sbrian          /* Going down - script failed */
38336285Sbrian          log_Printf(LogWARN, "Chat script failed\n");
38436285Sbrian          switch(dl->state) {
38536285Sbrian            case DATALINK_HANGUP:
38636285Sbrian              datalink_HangupDone(dl);
38736285Sbrian              break;
38836285Sbrian            case DATALINK_DIAL:
38952488Sbrian            case DATALINK_LOGOUT:
39036285Sbrian            case DATALINK_LOGIN:
39136285Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
39252488Sbrian              physical_Offline(dl->physical);
39354914Sbrian              if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
39454914Sbrian                log_Printf(LogWARN, "Invalid hangup script\n");
39536285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
39636285Sbrian          }
39736285Sbrian          break;
39836285Sbrian      }
39936285Sbrian      break;
40036285Sbrian
40136285Sbrian    case DATALINK_READY:
40236285Sbrian    case DATALINK_LCP:
40336285Sbrian    case DATALINK_AUTH:
40438174Sbrian    case DATALINK_CBCP:
40536285Sbrian    case DATALINK_OPEN:
40643888Sbrian      result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
40743888Sbrian               descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
40836285Sbrian      break;
40936285Sbrian  }
41036285Sbrian  return result;
41136285Sbrian}
41236285Sbrian
41336285Sbrianint
41436285Sbriandatalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
41536285Sbrian{
41636285Sbrian  return physical_RemoveFromSet(dl->physical, r, w, e);
41736285Sbrian}
41836285Sbrian
41936285Sbrianstatic int
42058028Sbriandatalink_IsSet(struct fdescriptor *d, const fd_set *fdset)
42136285Sbrian{
42236285Sbrian  struct datalink *dl = descriptor2datalink(d);
42336285Sbrian
42436285Sbrian  switch (dl->state) {
42536285Sbrian    case DATALINK_CLOSED:
42636285Sbrian    case DATALINK_OPENING:
42736285Sbrian      break;
42836285Sbrian
42936285Sbrian    case DATALINK_HANGUP:
43036285Sbrian    case DATALINK_DIAL:
43152488Sbrian    case DATALINK_LOGOUT:
43236285Sbrian    case DATALINK_LOGIN:
43336285Sbrian      return descriptor_IsSet(&dl->chat.desc, fdset);
43436285Sbrian
43536285Sbrian    case DATALINK_READY:
43636285Sbrian    case DATALINK_LCP:
43736285Sbrian    case DATALINK_AUTH:
43838174Sbrian    case DATALINK_CBCP:
43936285Sbrian    case DATALINK_OPEN:
44043888Sbrian      return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
44143888Sbrian             descriptor_IsSet(&dl->physical->desc, fdset);
44236285Sbrian  }
44336285Sbrian  return 0;
44436285Sbrian}
44536285Sbrian
44636285Sbrianstatic void
44758028Sbriandatalink_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
44836285Sbrian{
44936285Sbrian  struct datalink *dl = descriptor2datalink(d);
45036285Sbrian
45136285Sbrian  switch (dl->state) {
45236285Sbrian    case DATALINK_CLOSED:
45336285Sbrian    case DATALINK_OPENING:
45436285Sbrian      break;
45536285Sbrian
45636285Sbrian    case DATALINK_HANGUP:
45736285Sbrian    case DATALINK_DIAL:
45852488Sbrian    case DATALINK_LOGOUT:
45936285Sbrian    case DATALINK_LOGIN:
46036285Sbrian      descriptor_Read(&dl->chat.desc, bundle, fdset);
46136285Sbrian      break;
46236285Sbrian
46336285Sbrian    case DATALINK_READY:
46436285Sbrian    case DATALINK_LCP:
46536285Sbrian    case DATALINK_AUTH:
46638174Sbrian    case DATALINK_CBCP:
46736285Sbrian    case DATALINK_OPEN:
46843888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
46943888Sbrian        descriptor_Read(&dl->chap.desc, bundle, fdset);
47043888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
47143888Sbrian        descriptor_Read(&dl->physical->desc, bundle, fdset);
47236285Sbrian      break;
47336285Sbrian  }
47436285Sbrian}
47536285Sbrian
47637141Sbrianstatic int
47767912Sbriandatalink_Write(struct fdescriptor *d, struct bundle *bundle,
47867912Sbrian               const fd_set *fdset)
47936285Sbrian{
48036285Sbrian  struct datalink *dl = descriptor2datalink(d);
48137141Sbrian  int result = 0;
48236285Sbrian
48336285Sbrian  switch (dl->state) {
48436285Sbrian    case DATALINK_CLOSED:
48536285Sbrian    case DATALINK_OPENING:
48636285Sbrian      break;
48736285Sbrian
48836285Sbrian    case DATALINK_HANGUP:
48936285Sbrian    case DATALINK_DIAL:
49052488Sbrian    case DATALINK_LOGOUT:
49136285Sbrian    case DATALINK_LOGIN:
49237141Sbrian      result = descriptor_Write(&dl->chat.desc, bundle, fdset);
49336285Sbrian      break;
49436285Sbrian
49536285Sbrian    case DATALINK_READY:
49636285Sbrian    case DATALINK_LCP:
49736285Sbrian    case DATALINK_AUTH:
49838174Sbrian    case DATALINK_CBCP:
49936285Sbrian    case DATALINK_OPEN:
50043888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
50143888Sbrian        result += descriptor_Write(&dl->chap.desc, bundle, fdset);
50243888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
50343888Sbrian        result += descriptor_Write(&dl->physical->desc, bundle, fdset);
50436285Sbrian      break;
50536285Sbrian  }
50637141Sbrian
50737141Sbrian  return result;
50836285Sbrian}
50936285Sbrian
51036285Sbrianstatic void
51137007Sbriandatalink_ComeDown(struct datalink *dl, int how)
51236285Sbrian{
51337007Sbrian  if (how != CLOSE_NORMAL) {
51444468Sbrian    dl->dial.tries = -1;
51536285Sbrian    dl->reconnect_tries = 0;
51637012Sbrian    if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
51737007Sbrian      dl->stayonline = 1;
51836285Sbrian  }
51936285Sbrian
52037012Sbrian  if (dl->state >= DATALINK_READY && dl->stayonline) {
52137007Sbrian    dl->stayonline = 0;
52247061Sbrian    physical_StopDeviceTimer(dl->physical);
52337007Sbrian    datalink_NewState(dl, DATALINK_READY);
52437007Sbrian  } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
52546686Sbrian    physical_Offline(dl->physical);
52636285Sbrian    if (dl->script.run && dl->state != DATALINK_OPENING) {
52752488Sbrian      if (dl->state == DATALINK_LOGOUT) {
52852488Sbrian        datalink_NewState(dl, DATALINK_HANGUP);
52954914Sbrian        if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
53054914Sbrian          log_Printf(LogWARN, "Invalid hangup script\n");
53152488Sbrian      } else {
53252488Sbrian        datalink_NewState(dl, DATALINK_LOGOUT);
53354914Sbrian        if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
53454914Sbrian          log_Printf(LogWARN, "Invalid logout script\n");
53552488Sbrian      }
53636285Sbrian    } else
53736285Sbrian      datalink_HangupDone(dl);
53836285Sbrian  }
53936285Sbrian}
54036285Sbrian
54136285Sbrianstatic void
54236285Sbriandatalink_LayerStart(void *v, struct fsm *fp)
54336285Sbrian{
54436285Sbrian  /* The given FSM is about to start up ! */
54536285Sbrian  struct datalink *dl = (struct datalink *)v;
54636285Sbrian
54736285Sbrian  if (fp->proto == PROTO_LCP)
54836285Sbrian    (*dl->parent->LayerStart)(dl->parent->object, fp);
54936285Sbrian}
55036285Sbrian
55136285Sbrianstatic void
55236285Sbriandatalink_LayerUp(void *v, struct fsm *fp)
55336285Sbrian{
55436285Sbrian  /* The given fsm is now up */
55536285Sbrian  struct datalink *dl = (struct datalink *)v;
55644106Sbrian  struct lcp *lcp = &dl->physical->link.lcp;
55736285Sbrian
55836285Sbrian  if (fp->proto == PROTO_LCP) {
55943693Sbrian    datalink_GotAuthname(dl, "");
56044106Sbrian    lcp->auth_ineed = lcp->want_auth;
56144106Sbrian    lcp->auth_iwait = lcp->his_auth;
56244106Sbrian    if (lcp->his_auth || lcp->want_auth) {
56345350Sbrian      if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
56436285Sbrian        bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
56536285Sbrian      log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
56644106Sbrian                Auth2Nam(lcp->his_auth, lcp->his_authtype),
56744106Sbrian                Auth2Nam(lcp->want_auth, lcp->want_authtype));
56844106Sbrian      if (lcp->his_auth == PROTO_PAP)
56943693Sbrian        auth_StartReq(&dl->pap);
57044106Sbrian      if (lcp->want_auth == PROTO_CHAP)
57143693Sbrian        auth_StartReq(&dl->chap.auth);
57236285Sbrian    } else
57336285Sbrian      datalink_AuthOk(dl);
57436285Sbrian  }
57536285Sbrian}
57636285Sbrian
57744094Sbrianstatic void
57844094Sbriandatalink_AuthReInit(struct datalink *dl)
57944094Sbrian{
58044094Sbrian  auth_StopTimer(&dl->pap);
58144094Sbrian  auth_StopTimer(&dl->chap.auth);
58244094Sbrian  chap_ReInit(&dl->chap);
58344094Sbrian}
58444094Sbrian
58536285Sbrianvoid
58643693Sbriandatalink_GotAuthname(struct datalink *dl, const char *name)
58736285Sbrian{
58843693Sbrian  strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
58943693Sbrian  dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
59036285Sbrian}
59136285Sbrian
59236285Sbrianvoid
59338174Sbriandatalink_NCPUp(struct datalink *dl)
59436285Sbrian{
59537320Sbrian  int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
59636310Sbrian
59736285Sbrian  if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
59836285Sbrian    /* we've authenticated in multilink mode ! */
59936285Sbrian    switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
60036285Sbrian      case MP_LINKSENT:
60136285Sbrian        /* We've handed the link off to another ppp (well, we will soon) ! */
60236285Sbrian        return;
60336285Sbrian      case MP_UP:
60436285Sbrian        /* First link in the bundle */
60538174Sbrian        auth_Select(dl->bundle, dl->peer.authname);
60649434Sbrian        bundle_CalculateBandwidth(dl->bundle);
60736285Sbrian        /* fall through */
60836285Sbrian      case MP_ADDED:
60936285Sbrian        /* We're in multilink mode ! */
61036310Sbrian        dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
61149434Sbrian        bundle_CalculateBandwidth(dl->bundle);
61236285Sbrian        break;
61336285Sbrian      case MP_FAILED:
61436285Sbrian        datalink_AuthNotOk(dl);
61536285Sbrian        return;
61636285Sbrian    }
61736285Sbrian  } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
61836285Sbrian    log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
61937160Sbrian    datalink_NewState(dl, DATALINK_OPEN);
62049434Sbrian    bundle_CalculateBandwidth(dl->bundle);
62137160Sbrian    (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
62236285Sbrian    return;
62336285Sbrian  } else {
62436285Sbrian    dl->bundle->ncp.mp.peer = dl->peer;
62536285Sbrian    ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
62638174Sbrian    auth_Select(dl->bundle, dl->peer.authname);
62736285Sbrian  }
62836285Sbrian
62937320Sbrian  if (ccpok) {
63037320Sbrian    fsm_Up(&dl->physical->link.ccp.fsm);
63137320Sbrian    fsm_Open(&dl->physical->link.ccp.fsm);
63237320Sbrian  }
63336285Sbrian  datalink_NewState(dl, DATALINK_OPEN);
63436285Sbrian  bundle_NewPhase(dl->bundle, PHASE_NETWORK);
63536285Sbrian  (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
63636285Sbrian}
63736285Sbrian
63836285Sbrianvoid
63938174Sbriandatalink_CBCPComplete(struct datalink *dl)
64038174Sbrian{
64138174Sbrian  datalink_NewState(dl, DATALINK_LCP);
64244094Sbrian  datalink_AuthReInit(dl);
64338174Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
64438174Sbrian}
64538174Sbrian
64638174Sbrianvoid
64738174Sbriandatalink_CBCPFailed(struct datalink *dl)
64838174Sbrian{
64938174Sbrian  cbcp_Down(&dl->cbcp);
65038174Sbrian  datalink_CBCPComplete(dl);
65138174Sbrian}
65238174Sbrian
65338174Sbrianvoid
65438174Sbriandatalink_AuthOk(struct datalink *dl)
65538174Sbrian{
65642600Sbrian  if ((dl->physical->link.lcp.his_callback.opmask &
65742600Sbrian       CALLBACK_BIT(CALLBACK_CBCP) ||
65842600Sbrian       dl->physical->link.lcp.want_callback.opmask &
65942600Sbrian       CALLBACK_BIT(CALLBACK_CBCP)) &&
66042600Sbrian      !(dl->physical->link.lcp.want_callback.opmask &
66142600Sbrian        CALLBACK_BIT(CALLBACK_AUTH))) {
66242600Sbrian    /* We must have agreed CBCP if AUTH isn't there any more */
66338174Sbrian    datalink_NewState(dl, DATALINK_CBCP);
66438174Sbrian    cbcp_Up(&dl->cbcp);
66538174Sbrian  } else if (dl->physical->link.lcp.want_callback.opmask) {
66642600Sbrian    /* It's not CBCP */
66738174Sbrian    log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
66838174Sbrian    datalink_NewState(dl, DATALINK_LCP);
66944094Sbrian    datalink_AuthReInit(dl);
67038174Sbrian    fsm_Close(&dl->physical->link.lcp.fsm);
67138174Sbrian  } else
67238174Sbrian    switch (dl->physical->link.lcp.his_callback.opmask) {
67338174Sbrian      case 0:
67438174Sbrian        datalink_NCPUp(dl);
67538174Sbrian        break;
67638174Sbrian
67738174Sbrian      case CALLBACK_BIT(CALLBACK_AUTH):
67838174Sbrian        auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
67938174Sbrian                          sizeof dl->cbcp.fsm.phone);
68038174Sbrian        if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
68138174Sbrian          log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
68238174Sbrian                     dl->peer.authname);
68338174Sbrian          *dl->cbcp.fsm.phone = '\0';
68438174Sbrian        } else {
68538174Sbrian          char *ptr = strchr(dl->cbcp.fsm.phone, ',');
68638174Sbrian          if (ptr)
68738174Sbrian            *ptr = '\0';	/* Call back on the first number */
68838174Sbrian          log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
68938174Sbrian                     dl->cbcp.fsm.phone);
69038174Sbrian          dl->cbcp.required = 1;
69138174Sbrian        }
69238174Sbrian        dl->cbcp.fsm.delay = 0;
69338174Sbrian        datalink_NewState(dl, DATALINK_LCP);
69444094Sbrian        datalink_AuthReInit(dl);
69538174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
69638174Sbrian        break;
69738174Sbrian
69838174Sbrian      case CALLBACK_BIT(CALLBACK_E164):
69938174Sbrian        strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
70038174Sbrian                sizeof dl->cbcp.fsm.phone - 1);
70138174Sbrian        dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
70238174Sbrian        log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
70338174Sbrian                   dl->cbcp.fsm.phone);
70438174Sbrian        dl->cbcp.required = 1;
70538174Sbrian        dl->cbcp.fsm.delay = 0;
70638174Sbrian        datalink_NewState(dl, DATALINK_LCP);
70744094Sbrian        datalink_AuthReInit(dl);
70838174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
70938174Sbrian        break;
71038174Sbrian
71138174Sbrian      default:
71238174Sbrian        log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
71338174Sbrian                   dl->name);
71438174Sbrian        datalink_NewState(dl, DATALINK_LCP);
71544094Sbrian        datalink_AuthReInit(dl);
71638174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
71738174Sbrian        break;
71838174Sbrian    }
71938174Sbrian}
72038174Sbrian
72138174Sbrianvoid
72236285Sbriandatalink_AuthNotOk(struct datalink *dl)
72336285Sbrian{
72436285Sbrian  datalink_NewState(dl, DATALINK_LCP);
72544094Sbrian  datalink_AuthReInit(dl);
72636285Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
72736285Sbrian}
72836285Sbrian
72936285Sbrianstatic void
73036285Sbriandatalink_LayerDown(void *v, struct fsm *fp)
73136285Sbrian{
73236285Sbrian  /* The given FSM has been told to come down */
73336285Sbrian  struct datalink *dl = (struct datalink *)v;
73436285Sbrian
73536285Sbrian  if (fp->proto == PROTO_LCP) {
73636285Sbrian    switch (dl->state) {
73736285Sbrian      case DATALINK_OPEN:
73836285Sbrian        peerid_Init(&dl->peer);
73937060Sbrian        fsm2initial(&dl->physical->link.ccp.fsm);
74036928Sbrian        datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
74136285Sbrian        (*dl->parent->LayerDown)(dl->parent->object, fp);
74238174Sbrian        /* fall through (just in case) */
74336285Sbrian
74438174Sbrian      case DATALINK_CBCP:
74538174Sbrian        if (!dl->cbcp.required)
74638174Sbrian          cbcp_Down(&dl->cbcp);
74738174Sbrian        /* fall through (just in case) */
74838174Sbrian
74936285Sbrian      case DATALINK_AUTH:
75036285Sbrian        timer_Stop(&dl->pap.authtimer);
75136285Sbrian        timer_Stop(&dl->chap.auth.authtimer);
75236285Sbrian    }
75336285Sbrian    datalink_NewState(dl, DATALINK_LCP);
75444094Sbrian    datalink_AuthReInit(dl);
75536285Sbrian  }
75636285Sbrian}
75736285Sbrian
75836285Sbrianstatic void
75936285Sbriandatalink_LayerFinish(void *v, struct fsm *fp)
76036285Sbrian{
76136285Sbrian  /* The given fsm is now down */
76236285Sbrian  struct datalink *dl = (struct datalink *)v;
76336285Sbrian
76436285Sbrian  if (fp->proto == PROTO_LCP) {
76537060Sbrian    fsm2initial(fp);
76636285Sbrian    (*dl->parent->LayerFinish)(dl->parent->object, fp);
76737007Sbrian    datalink_ComeDown(dl, CLOSE_NORMAL);
76836285Sbrian  } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
76936285Sbrian    fsm_Open(fp);		/* CCP goes to ST_STOPPED */
77036285Sbrian}
77136285Sbrian
77236285Sbrianstruct datalink *
77336285Sbriandatalink_Create(const char *name, struct bundle *bundle, int type)
77436285Sbrian{
77536285Sbrian  struct datalink *dl;
77636285Sbrian
77736285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
77836285Sbrian  if (dl == NULL)
77936285Sbrian    return dl;
78036285Sbrian
78136285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
78236285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
78336285Sbrian  dl->desc.IsSet = datalink_IsSet;
78436285Sbrian  dl->desc.Read = datalink_Read;
78536285Sbrian  dl->desc.Write = datalink_Write;
78636285Sbrian
78736285Sbrian  dl->state = DATALINK_CLOSED;
78836285Sbrian
78936285Sbrian  *dl->cfg.script.dial = '\0';
79036285Sbrian  *dl->cfg.script.login = '\0';
79152488Sbrian  *dl->cfg.script.logout = '\0';
79236285Sbrian  *dl->cfg.script.hangup = '\0';
79336285Sbrian  *dl->cfg.phone.list = '\0';
79436285Sbrian  *dl->phone.list = '\0';
79536285Sbrian  dl->phone.next = NULL;
79636285Sbrian  dl->phone.alt = NULL;
79736285Sbrian  dl->phone.chosen = "N/A";
79837007Sbrian  dl->stayonline = 0;
79936285Sbrian  dl->script.run = 1;
80036285Sbrian  dl->script.packetmode = 1;
80136285Sbrian  mp_linkInit(&dl->mp);
80236285Sbrian
80336285Sbrian  dl->bundle = bundle;
80436285Sbrian  dl->next = NULL;
80536285Sbrian
80644468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
80736285Sbrian
80844468Sbrian  dl->dial.tries = 0;
80936285Sbrian  dl->cfg.dial.max = 1;
81036285Sbrian  dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
81136285Sbrian  dl->cfg.dial.timeout = DIAL_TIMEOUT;
81244468Sbrian  dl->cfg.dial.inc = 0;
81344468Sbrian  dl->cfg.dial.maxinc = 10;
81436285Sbrian
81536285Sbrian  dl->reconnect_tries = 0;
81636285Sbrian  dl->cfg.reconnect.max = 0;
81736285Sbrian  dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
81836285Sbrian
81938174Sbrian  dl->cfg.callback.opmask = 0;
82038174Sbrian  dl->cfg.cbcp.delay = 0;
82138174Sbrian  *dl->cfg.cbcp.phone = '\0';
82238174Sbrian  dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
82338174Sbrian
82436285Sbrian  dl->name = strdup(name);
82536285Sbrian  peerid_Init(&dl->peer);
82636285Sbrian  dl->parent = &bundle->fsm;
82736285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
82836285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
82936285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
83036285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
83136285Sbrian  dl->fsmp.object = dl;
83236285Sbrian
83346686Sbrian  if ((dl->physical = physical_Create(dl, type)) == NULL) {
83436285Sbrian    free(dl->name);
83536285Sbrian    free(dl);
83636285Sbrian    return NULL;
83736285Sbrian  }
83843693Sbrian
83943693Sbrian  pap_Init(&dl->pap, dl->physical);
84043693Sbrian  chap_Init(&dl->chap, dl->physical);
84138174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
84236285Sbrian
84352488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
84454055Sbrian  chat_Init(&dl->chat, dl->physical);
84552488Sbrian
84636285Sbrian  log_Printf(LogPHASE, "%s: Created in %s state\n",
84736285Sbrian             dl->name, datalink_State(dl));
84836285Sbrian
84936285Sbrian  return dl;
85036285Sbrian}
85136285Sbrian
85236285Sbrianstruct datalink *
85336285Sbriandatalink_Clone(struct datalink *odl, const char *name)
85436285Sbrian{
85536285Sbrian  struct datalink *dl;
85636285Sbrian
85736285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
85836285Sbrian  if (dl == NULL)
85936285Sbrian    return dl;
86036285Sbrian
86136285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
86236285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
86336285Sbrian  dl->desc.IsSet = datalink_IsSet;
86436285Sbrian  dl->desc.Read = datalink_Read;
86536285Sbrian  dl->desc.Write = datalink_Write;
86636285Sbrian
86736285Sbrian  dl->state = DATALINK_CLOSED;
86836285Sbrian
86936285Sbrian  memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
87036285Sbrian  mp_linkInit(&dl->mp);
87136285Sbrian  *dl->phone.list = '\0';
87236285Sbrian  dl->phone.next = NULL;
87336285Sbrian  dl->phone.alt = NULL;
87436285Sbrian  dl->phone.chosen = "N/A";
87536285Sbrian  dl->bundle = odl->bundle;
87636285Sbrian  dl->next = NULL;
87744468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
87844468Sbrian  dl->dial.tries = 0;
87936285Sbrian  dl->reconnect_tries = 0;
88036285Sbrian  dl->name = strdup(name);
88136285Sbrian  peerid_Init(&dl->peer);
88236285Sbrian  dl->parent = odl->parent;
88336285Sbrian  memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
88436285Sbrian  dl->fsmp.object = dl;
88536285Sbrian
88646686Sbrian  if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
88736285Sbrian    free(dl->name);
88836285Sbrian    free(dl);
88936285Sbrian    return NULL;
89036285Sbrian  }
89143693Sbrian  pap_Init(&dl->pap, dl->physical);
89244305Sbrian  dl->pap.cfg = odl->pap.cfg;
89343693Sbrian
89443693Sbrian  chap_Init(&dl->chap, dl->physical);
89544305Sbrian  dl->chap.auth.cfg = odl->chap.auth.cfg;
89643693Sbrian
89736285Sbrian  memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
89836285Sbrian  memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
89936285Sbrian         sizeof dl->physical->link.lcp.cfg);
90036285Sbrian  memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
90136285Sbrian         sizeof dl->physical->link.ccp.cfg);
90236285Sbrian  memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
90336285Sbrian         sizeof dl->physical->async.cfg);
90436285Sbrian
90538174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
90636285Sbrian
90752488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
90854055Sbrian  chat_Init(&dl->chat, dl->physical);
90952488Sbrian
91036285Sbrian  log_Printf(LogPHASE, "%s: Cloned in %s state\n",
91136285Sbrian             dl->name, datalink_State(dl));
91236285Sbrian
91336285Sbrian  return dl;
91436285Sbrian}
91536285Sbrian
91636285Sbrianstruct datalink *
91736285Sbriandatalink_Destroy(struct datalink *dl)
91836285Sbrian{
91936285Sbrian  struct datalink *result;
92036285Sbrian
92136285Sbrian  if (dl->state != DATALINK_CLOSED) {
92236285Sbrian    log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
92336285Sbrian              datalink_State(dl));
92436285Sbrian    switch (dl->state) {
92536285Sbrian      case DATALINK_HANGUP:
92636285Sbrian      case DATALINK_DIAL:
92736285Sbrian      case DATALINK_LOGIN:
92854055Sbrian        chat_Finish(&dl->chat);		/* Gotta blat the timers ! */
92936285Sbrian        break;
93036285Sbrian    }
93136285Sbrian  }
93236285Sbrian
93354055Sbrian  chat_Destroy(&dl->chat);
93444468Sbrian  timer_Stop(&dl->dial.timer);
93536285Sbrian  result = dl->next;
93646686Sbrian  physical_Destroy(dl->physical);
93736285Sbrian  free(dl->name);
93836285Sbrian  free(dl);
93936285Sbrian
94036285Sbrian  return result;
94136285Sbrian}
94236285Sbrian
94336285Sbrianvoid
94436285Sbriandatalink_Up(struct datalink *dl, int runscripts, int packetmode)
94536285Sbrian{
94636285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
94736285Sbrian    /* Ignore scripts */
94836285Sbrian    runscripts = 0;
94936285Sbrian
95036285Sbrian  switch (dl->state) {
95136285Sbrian    case DATALINK_CLOSED:
95236285Sbrian      if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
95336285Sbrian          bundle_Phase(dl->bundle) == PHASE_TERMINATE)
95436285Sbrian        bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
95536285Sbrian      datalink_NewState(dl, DATALINK_OPENING);
95636285Sbrian      dl->reconnect_tries =
95736285Sbrian        dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
95844468Sbrian      dl->dial.tries = dl->cfg.dial.max;
95936285Sbrian      dl->script.run = runscripts;
96036285Sbrian      dl->script.packetmode = packetmode;
96136285Sbrian      break;
96236285Sbrian
96336285Sbrian    case DATALINK_OPENING:
96436285Sbrian      if (!dl->script.run && runscripts)
96536285Sbrian        dl->script.run = 1;
96636285Sbrian      /* fall through */
96736285Sbrian
96836285Sbrian    case DATALINK_DIAL:
96936285Sbrian    case DATALINK_LOGIN:
97036285Sbrian    case DATALINK_READY:
97136285Sbrian      if (!dl->script.packetmode && packetmode) {
97236285Sbrian        dl->script.packetmode = 1;
97360945Sbrian        if (dl->state == DATALINK_READY) {
97460945Sbrian          dl->script.run = 0;
97560945Sbrian          datalink_NewState(dl, DATALINK_CARRIER);
97660945Sbrian        }
97736285Sbrian      }
97836285Sbrian      break;
97936285Sbrian  }
98036285Sbrian}
98136285Sbrian
98236285Sbrianvoid
98337007Sbriandatalink_Close(struct datalink *dl, int how)
98436285Sbrian{
98536285Sbrian  /* Please close */
98636285Sbrian  switch (dl->state) {
98736285Sbrian    case DATALINK_OPEN:
98836285Sbrian      peerid_Init(&dl->peer);
98937060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
99036285Sbrian      /* fall through */
99136285Sbrian
99238174Sbrian    case DATALINK_CBCP:
99336285Sbrian    case DATALINK_AUTH:
99436285Sbrian    case DATALINK_LCP:
99544094Sbrian      datalink_AuthReInit(dl);
99636285Sbrian      fsm_Close(&dl->physical->link.lcp.fsm);
99737007Sbrian      if (how != CLOSE_NORMAL) {
99844468Sbrian        dl->dial.tries = -1;
99936285Sbrian        dl->reconnect_tries = 0;
100037007Sbrian        if (how == CLOSE_LCP)
100137007Sbrian          dl->stayonline = 1;
100236285Sbrian      }
100352029Sbrian      break;
100436285Sbrian
100536285Sbrian    default:
100637007Sbrian      datalink_ComeDown(dl, how);
100736285Sbrian  }
100836285Sbrian}
100936285Sbrian
101036285Sbrianvoid
101137007Sbriandatalink_Down(struct datalink *dl, int how)
101236285Sbrian{
101336285Sbrian  /* Carrier is lost */
101436285Sbrian  switch (dl->state) {
101536285Sbrian    case DATALINK_OPEN:
101636285Sbrian      peerid_Init(&dl->peer);
101737060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
101836285Sbrian      /* fall through */
101936285Sbrian
102038174Sbrian    case DATALINK_CBCP:
102136285Sbrian    case DATALINK_AUTH:
102236285Sbrian    case DATALINK_LCP:
102337060Sbrian      fsm2initial(&dl->physical->link.lcp.fsm);
102456656Sbrian      if (dl->state == DATALINK_OPENING)
102556656Sbrian        return;			/* we're doing a callback... */
102636285Sbrian      /* fall through */
102736285Sbrian
102836285Sbrian    default:
102937007Sbrian      datalink_ComeDown(dl, how);
103036285Sbrian  }
103136285Sbrian}
103236285Sbrian
103336285Sbrianvoid
103436285Sbriandatalink_StayDown(struct datalink *dl)
103536285Sbrian{
103636285Sbrian  dl->reconnect_tries = 0;
103736285Sbrian}
103836285Sbrian
103937007Sbrianvoid
104037007Sbriandatalink_DontHangup(struct datalink *dl)
104137007Sbrian{
104237012Sbrian  if (dl->state >= DATALINK_LCP)
104337012Sbrian    dl->stayonline = 1;
104437007Sbrian}
104537007Sbrian
104636285Sbrianint
104736285Sbriandatalink_Show(struct cmdargs const *arg)
104836285Sbrian{
104936285Sbrian  prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
105038174Sbrian  prompt_Printf(arg->prompt, " State:              %s\n",
105136285Sbrian                datalink_State(arg->cx));
105238174Sbrian  prompt_Printf(arg->prompt, " Peer name:          ");
105336285Sbrian  if (*arg->cx->peer.authname)
105436285Sbrian    prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
105536285Sbrian  else if (arg->cx->state == DATALINK_OPEN)
105636285Sbrian    prompt_Printf(arg->prompt, "None requested\n");
105736285Sbrian  else
105836285Sbrian    prompt_Printf(arg->prompt, "N/A\n");
105938174Sbrian  prompt_Printf(arg->prompt, " Discriminator:      %s\n",
106036285Sbrian                mp_Enddisc(arg->cx->peer.enddisc.class,
106136285Sbrian                           arg->cx->peer.enddisc.address,
106236285Sbrian                           arg->cx->peer.enddisc.len));
106336285Sbrian
106436285Sbrian  prompt_Printf(arg->prompt, "\nDefaults:\n");
106538174Sbrian  prompt_Printf(arg->prompt, " Phone List:         %s\n",
106636285Sbrian                arg->cx->cfg.phone.list);
106736285Sbrian  if (arg->cx->cfg.dial.max)
106838174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
106936285Sbrian                  arg->cx->cfg.dial.max);
107036285Sbrian  else
107138174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
107244261Sbrian  if (arg->cx->cfg.dial.next_timeout >= 0)
107336285Sbrian    prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
107436285Sbrian  else
107536285Sbrian    prompt_Printf(arg->prompt, "random/");
107644261Sbrian  if (arg->cx->cfg.dial.timeout >= 0)
107736285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
107836285Sbrian  else
107936285Sbrian    prompt_Printf(arg->prompt, "random\n");
108038174Sbrian  prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
108136285Sbrian                arg->cx->cfg.reconnect.max);
108236285Sbrian  if (arg->cx->cfg.reconnect.timeout > 0)
108336285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
108436285Sbrian  else
108536285Sbrian    prompt_Printf(arg->prompt, "random\n");
108638174Sbrian  prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
108738174Sbrian                PHYS_DIRECT ?  "accepted: " : "requested:");
108838174Sbrian  if (!arg->cx->cfg.callback.opmask)
108938174Sbrian    prompt_Printf(arg->prompt, "none\n");
109038174Sbrian  else {
109138174Sbrian    int comma = 0;
109238174Sbrian
109338174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
109438174Sbrian      prompt_Printf(arg->prompt, "none");
109538174Sbrian      comma = 1;
109638174Sbrian    }
109738174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
109838174Sbrian      prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
109938174Sbrian      comma = 1;
110038174Sbrian    }
110138174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
110238174Sbrian      prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
110338174Sbrian      if (arg->cx->physical->type != PHYS_DIRECT)
110438174Sbrian        prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
110538174Sbrian      comma = 1;
110638174Sbrian    }
110738174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
110838174Sbrian      prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
110938174Sbrian      prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
111038174Sbrian                    arg->cx->cfg.cbcp.delay);
111140483Sbrian      prompt_Printf(arg->prompt, "                     phone: ");
111240483Sbrian      if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
111340483Sbrian        if (arg->cx->physical->type & PHYS_DIRECT)
111440483Sbrian          prompt_Printf(arg->prompt, "Caller decides\n");
111540483Sbrian        else
111640483Sbrian          prompt_Printf(arg->prompt, "Dialback server decides\n");
111740483Sbrian      } else
111840483Sbrian        prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
111938174Sbrian      prompt_Printf(arg->prompt, "                     timeout: %lds\n",
112038174Sbrian                    arg->cx->cfg.cbcp.fsmretry);
112138174Sbrian    } else
112238174Sbrian      prompt_Printf(arg->prompt, "\n");
112338174Sbrian  }
112438174Sbrian
112538174Sbrian  prompt_Printf(arg->prompt, " Dial Script:        %s\n",
112636285Sbrian                arg->cx->cfg.script.dial);
112738174Sbrian  prompt_Printf(arg->prompt, " Login Script:       %s\n",
112836285Sbrian                arg->cx->cfg.script.login);
112952488Sbrian  prompt_Printf(arg->prompt, " Logout Script:      %s\n",
113052488Sbrian                arg->cx->cfg.script.logout);
113138174Sbrian  prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
113236285Sbrian                arg->cx->cfg.script.hangup);
113336285Sbrian  return 0;
113436285Sbrian}
113536285Sbrian
113636285Sbrianint
113736285Sbriandatalink_SetReconnect(struct cmdargs const *arg)
113836285Sbrian{
113936285Sbrian  if (arg->argc == arg->argn+2) {
114036285Sbrian    arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
114136285Sbrian    arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
114236285Sbrian    return 0;
114336285Sbrian  }
114436285Sbrian  return -1;
114536285Sbrian}
114636285Sbrian
114736285Sbrianint
114836285Sbriandatalink_SetRedial(struct cmdargs const *arg)
114936285Sbrian{
115044468Sbrian  const char *sep, *osep;
115144468Sbrian  int timeout, inc, maxinc, tries;
115236285Sbrian
115336285Sbrian  if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
115436285Sbrian    if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
115536285Sbrian	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
115636285Sbrian      arg->cx->cfg.dial.timeout = -1;
115736285Sbrian      randinit();
115836285Sbrian    } else {
115936285Sbrian      timeout = atoi(arg->argv[arg->argn]);
116036285Sbrian
116136285Sbrian      if (timeout >= 0)
116236285Sbrian	arg->cx->cfg.dial.timeout = timeout;
116336285Sbrian      else {
116436285Sbrian	log_Printf(LogWARN, "Invalid redial timeout\n");
116536285Sbrian	return -1;
116636285Sbrian      }
116736285Sbrian    }
116836285Sbrian
116944468Sbrian    sep = strchr(arg->argv[arg->argn], '+');
117044468Sbrian    if (sep) {
117144468Sbrian      inc = atoi(++sep);
117244468Sbrian      osep = sep;
117344468Sbrian      if (inc >= 0)
117444468Sbrian        arg->cx->cfg.dial.inc = inc;
117544468Sbrian      else {
117644468Sbrian        log_Printf(LogWARN, "Invalid timeout increment\n");
117744468Sbrian        return -1;
117844468Sbrian      }
117944468Sbrian      sep = strchr(sep, '-');
118044468Sbrian      if (sep) {
118144468Sbrian        maxinc = atoi(++sep);
118244468Sbrian        if (maxinc >= 0)
118344468Sbrian          arg->cx->cfg.dial.maxinc = maxinc;
118444468Sbrian        else {
118544468Sbrian          log_Printf(LogWARN, "Invalid maximum timeout increments\n");
118644468Sbrian          return -1;
118744468Sbrian        }
118844468Sbrian      } else {
118944468Sbrian        /* Default timeout increment */
119044468Sbrian        arg->cx->cfg.dial.maxinc = 10;
119144468Sbrian        sep = osep;
119244468Sbrian      }
119344468Sbrian    } else {
119444468Sbrian      /* Default timeout increment & max increment */
119544468Sbrian      arg->cx->cfg.dial.inc = 0;
119644468Sbrian      arg->cx->cfg.dial.maxinc = 10;
119744468Sbrian      sep = arg->argv[arg->argn];
119844468Sbrian    }
119944468Sbrian
120044468Sbrian    sep = strchr(sep, '.');
120144468Sbrian    if (sep) {
120244468Sbrian      if (strcasecmp(++sep, "random") == 0) {
120336285Sbrian	arg->cx->cfg.dial.next_timeout = -1;
120436285Sbrian	randinit();
120536285Sbrian      } else {
120644468Sbrian	timeout = atoi(sep);
120736285Sbrian	if (timeout >= 0)
120836285Sbrian	  arg->cx->cfg.dial.next_timeout = timeout;
120936285Sbrian	else {
121036285Sbrian	  log_Printf(LogWARN, "Invalid next redial timeout\n");
121136285Sbrian	  return -1;
121236285Sbrian	}
121336285Sbrian      }
121436285Sbrian    } else
121536285Sbrian      /* Default next timeout */
121636285Sbrian      arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
121736285Sbrian
121836285Sbrian    if (arg->argc == arg->argn+2) {
121936285Sbrian      tries = atoi(arg->argv[arg->argn+1]);
122036285Sbrian
122136285Sbrian      if (tries >= 0) {
122236285Sbrian	arg->cx->cfg.dial.max = tries;
122336285Sbrian      } else {
122436285Sbrian	log_Printf(LogWARN, "Invalid retry value\n");
122536285Sbrian	return 1;
122636285Sbrian      }
122736285Sbrian    }
122836285Sbrian    return 0;
122936285Sbrian  }
123044468Sbrian
123136285Sbrian  return -1;
123236285Sbrian}
123336285Sbrian
123455146Sbrianstatic const char * const states[] = {
123536285Sbrian  "closed",
123636285Sbrian  "opening",
123736285Sbrian  "hangup",
123836285Sbrian  "dial",
123949472Sbrian  "carrier",
124052488Sbrian  "logout",
124136285Sbrian  "login",
124236285Sbrian  "ready",
124336285Sbrian  "lcp",
124436285Sbrian  "auth",
124538174Sbrian  "cbcp",
124636285Sbrian  "open"
124736285Sbrian};
124836285Sbrian
124936285Sbrianconst char *
125036285Sbriandatalink_State(struct datalink *dl)
125136285Sbrian{
125236285Sbrian  if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
125336285Sbrian    return "unknown";
125436285Sbrian  return states[dl->state];
125536285Sbrian}
125636285Sbrian
125736285Sbrianstatic void
125836285Sbriandatalink_NewState(struct datalink *dl, int state)
125936285Sbrian{
126036285Sbrian  if (state != dl->state) {
126136285Sbrian    if (state >= 0 && state < sizeof states / sizeof states[0]) {
126236285Sbrian      log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
126336285Sbrian                 states[state]);
126436285Sbrian      dl->state = state;
126536285Sbrian    } else
126636285Sbrian      log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
126736285Sbrian  }
126836285Sbrian}
126936285Sbrian
127036285Sbrianstruct datalink *
127136285Sbrianiov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
127252942Sbrian             int fd, int *auxfd, int *nauxfd)
127336285Sbrian{
127436285Sbrian  struct datalink *dl, *cdl;
127544305Sbrian  struct fsm_retry copy;
127636285Sbrian  char *oname;
127736285Sbrian
127836285Sbrian  dl = (struct datalink *)iov[(*niov)++].iov_base;
127936285Sbrian  dl->name = iov[*niov].iov_base;
128036285Sbrian
128136285Sbrian  if (dl->name[DATALINK_MAXNAME-1]) {
128236285Sbrian    dl->name[DATALINK_MAXNAME-1] = '\0';
128336285Sbrian    if (strlen(dl->name) == DATALINK_MAXNAME - 1)
128436285Sbrian      log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
128536285Sbrian  }
128636285Sbrian
128736285Sbrian  /* Make sure the name is unique ! */
128836285Sbrian  oname = NULL;
128936285Sbrian  do {
129036285Sbrian    for (cdl = bundle->links; cdl; cdl = cdl->next)
129136285Sbrian      if (!strcasecmp(dl->name, cdl->name)) {
129236285Sbrian        if (oname)
129336285Sbrian          free(datalink_NextName(dl));
129436285Sbrian        else
129536285Sbrian          oname = datalink_NextName(dl);
129636285Sbrian        break;	/* Keep renaming 'till we have no conflicts */
129736285Sbrian      }
129836285Sbrian  } while (cdl);
129936285Sbrian
130036285Sbrian  if (oname) {
130136285Sbrian    log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
130236285Sbrian    free(oname);
130336285Sbrian  } else {
130436285Sbrian    dl->name = strdup(dl->name);
130536285Sbrian    free(iov[*niov].iov_base);
130636285Sbrian  }
130736285Sbrian  (*niov)++;
130836285Sbrian
130936285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
131036285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
131136285Sbrian  dl->desc.IsSet = datalink_IsSet;
131236285Sbrian  dl->desc.Read = datalink_Read;
131336285Sbrian  dl->desc.Write = datalink_Write;
131436285Sbrian
131536285Sbrian  mp_linkInit(&dl->mp);
131636285Sbrian  *dl->phone.list = '\0';
131736285Sbrian  dl->phone.next = NULL;
131836285Sbrian  dl->phone.alt = NULL;
131936285Sbrian  dl->phone.chosen = "N/A";
132036285Sbrian
132136285Sbrian  dl->bundle = bundle;
132236285Sbrian  dl->next = NULL;
132344468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
132444468Sbrian  dl->dial.tries = 0;
132536285Sbrian  dl->reconnect_tries = 0;
132636285Sbrian  dl->parent = &bundle->fsm;
132736285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
132836285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
132936285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
133036285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
133136285Sbrian  dl->fsmp.object = dl;
133236285Sbrian
133352942Sbrian  dl->physical = iov2physical(dl, iov, niov, maxiov, fd, auxfd, nauxfd);
133436285Sbrian
133536285Sbrian  if (!dl->physical) {
133636285Sbrian    free(dl->name);
133736285Sbrian    free(dl);
133836285Sbrian    dl = NULL;
133936285Sbrian  } else {
134044305Sbrian    copy = dl->pap.cfg.fsm;
134143693Sbrian    pap_Init(&dl->pap, dl->physical);
134244305Sbrian    dl->pap.cfg.fsm = copy;
134343693Sbrian
134444305Sbrian    copy = dl->chap.auth.cfg.fsm;
134543693Sbrian    chap_Init(&dl->chap, dl->physical);
134644305Sbrian    dl->chap.auth.cfg.fsm = copy;
134743693Sbrian
134838174Sbrian    cbcp_Init(&dl->cbcp, dl->physical);
134936285Sbrian
135052488Sbrian    memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
135154055Sbrian    chat_Init(&dl->chat, dl->physical);
135252488Sbrian
135336285Sbrian    log_Printf(LogPHASE, "%s: Transferred in %s state\n",
135436285Sbrian              dl->name, datalink_State(dl));
135536285Sbrian  }
135636285Sbrian
135736285Sbrian  return dl;
135836285Sbrian}
135936285Sbrian
136036285Sbrianint
136136450Sbriandatalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
136253684Sbrian             int *auxfd, int *nauxfd)
136336285Sbrian{
136436285Sbrian  /* If `dl' is NULL, we're allocating before a Fromiov() */
136536285Sbrian  int link_fd;
136636285Sbrian
136736285Sbrian  if (dl) {
136844468Sbrian    timer_Stop(&dl->dial.timer);
136938174Sbrian    /* The following is purely for the sake of paranoia */
137038174Sbrian    cbcp_Down(&dl->cbcp);
137136285Sbrian    timer_Stop(&dl->pap.authtimer);
137236285Sbrian    timer_Stop(&dl->chap.auth.authtimer);
137336285Sbrian  }
137436285Sbrian
137536285Sbrian  if (*niov >= maxiov - 1) {
137636285Sbrian    log_Printf(LogERROR, "Toiov: No room for datalink !\n");
137736285Sbrian    if (dl) {
137836285Sbrian      free(dl->name);
137936285Sbrian      free(dl);
138036285Sbrian    }
138136285Sbrian    return -1;
138236285Sbrian  }
138336285Sbrian
138453684Sbrian  iov[*niov].iov_base = (void *)dl;
138536285Sbrian  iov[(*niov)++].iov_len = sizeof *dl;
138653684Sbrian  iov[*niov].iov_base = dl ? realloc(dl->name, DATALINK_MAXNAME) : NULL;
138736285Sbrian  iov[(*niov)++].iov_len = DATALINK_MAXNAME;
138836285Sbrian
138952942Sbrian  link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, auxfd,
139053684Sbrian                         nauxfd);
139136285Sbrian
139236285Sbrian  if (link_fd == -1 && dl) {
139336285Sbrian    free(dl->name);
139436285Sbrian    free(dl);
139536285Sbrian  }
139636285Sbrian
139736285Sbrian  return link_fd;
139836285Sbrian}
139936285Sbrian
140036285Sbrianvoid
140136285Sbriandatalink_Rename(struct datalink *dl, const char *name)
140236285Sbrian{
140336285Sbrian  free(dl->name);
140436285Sbrian  dl->physical->link.name = dl->name = strdup(name);
140536285Sbrian}
140636285Sbrian
140736285Sbrianchar *
140836285Sbriandatalink_NextName(struct datalink *dl)
140936285Sbrian{
141036285Sbrian  int f, n;
141136285Sbrian  char *name, *oname;
141236285Sbrian
141336285Sbrian  n = strlen(dl->name);
141436285Sbrian  name = (char *)malloc(n+3);
141536285Sbrian  for (f = n - 1; f >= 0; f--)
141636285Sbrian    if (!isdigit(dl->name[f]))
141736285Sbrian      break;
141836285Sbrian  n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
141936285Sbrian  sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
142036285Sbrian  oname = dl->name;
142136345Sbrian  dl->name = name;
142236345Sbrian  /* our physical link name isn't updated (it probably isn't created yet) */
142336285Sbrian  return oname;
142436285Sbrian}
142536285Sbrian
142636285Sbrianint
142736285Sbriandatalink_SetMode(struct datalink *dl, int mode)
142836285Sbrian{
142936285Sbrian  if (!physical_SetMode(dl->physical, mode))
143036285Sbrian    return 0;
143136285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
143236285Sbrian    dl->script.run = 0;
143336285Sbrian  if (dl->physical->type == PHYS_DIRECT)
143436285Sbrian    dl->reconnect_tries = 0;
143553830Sbrian  if (mode & (PHYS_DDIAL|PHYS_BACKGROUND|PHYS_FOREGROUND) &&
143653830Sbrian      dl->state <= DATALINK_READY)
143736285Sbrian    datalink_Up(dl, 1, 1);
143836285Sbrian  return 1;
143936285Sbrian}
144044468Sbrian
144144468Sbrianint
144244468Sbriandatalink_GetDialTimeout(struct datalink *dl)
144344468Sbrian{
144444468Sbrian  int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
144544468Sbrian
144644468Sbrian  if (dl->dial.incs < dl->cfg.dial.maxinc)
144744468Sbrian    dl->dial.incs++;
144844468Sbrian
144944468Sbrian  return result;
145044468Sbrian}
1451