2210 lines
57 KiB
C
2210 lines
57 KiB
C
/*
|
|
* capiconnect.c - pppd plugin to make ISDN connections over CAPI2.0
|
|
*
|
|
* Copyright 2000-2004 Carsten Paeth (calle@calle.in-berlin.de)
|
|
* Copyright 2000-2004 AVM GmbH Berlin (info@avm.de)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
#include <unistd.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include "pppd.h"
|
|
#include "capiconn.h"
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
#include <linux/if.h>
|
|
|
|
|
|
static char *revision = "$Revision: 1.36 $";
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
#define AVMADSLPARAMFILE "/etc/drdsl/adsl.conf"
|
|
|
|
#define DIM(array) (sizeof(array)/sizeof((array)[0]))
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
#include "patchlevel.h"
|
|
#ifdef VERSION
|
|
char pppd_version[] = VERSION;
|
|
#endif
|
|
|
|
#define PPPVersion(v1,v2,v3,v4) ((v1)*1000000+(v2)*10000+(v3)*100+(v4))
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
#if PPPVER >= PPPVersion(2,4,1,0)
|
|
#define _timeout(a,b,c) timeout(a, b, c, 0);
|
|
#else
|
|
#define _timeout(a,b,c) timeout(a, b, c);
|
|
#endif
|
|
#if PPPVER >= PPPVersion(2,4,0,0)
|
|
#define _script_setenv(a,b) script_setenv(a,b,0)
|
|
#else
|
|
#define _script_setenv(a,b) script_setenv(a,b)
|
|
#endif
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int exiting = 0;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
typedef struct stringlist {
|
|
struct stringlist *next;
|
|
char *s;
|
|
} STRINGLIST;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int curphase = -1;
|
|
static int wakeupnow = 0;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void handlemessages(void) ;
|
|
static int shmatch(char *string, char *expr);
|
|
static void stringlist_free(STRINGLIST **pp);
|
|
static int stringlist_append_string(STRINGLIST **pp, char *s);
|
|
static STRINGLIST *stringlist_split(char *tosplit, char *seps);
|
|
static void makecallback(void);
|
|
static char *phase2str(int phase);
|
|
static void wakeupdemand(void);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static capiconn_context *ctx;
|
|
static unsigned applid;
|
|
#define CM(x) (1<<(x))
|
|
#define CIPMASK_ALL 0x1FFF03FF
|
|
#define CIPMASK_VOICE (CM(1)|CM(4)|CM(5)|CM(16)|CM(26))
|
|
#define CIPMASK_DATA (CM(2)|CM(3))
|
|
static unsigned long cipmask = CIPMASK_ALL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static char *opt_controller[4];
|
|
|
|
static struct contrparams {
|
|
int controller;
|
|
capi_contrinfo cinfo;
|
|
char *opt_numberprefix;
|
|
char *opt_msn;
|
|
char *opt_inmsn;
|
|
STRINGLIST *inmsns;
|
|
} contrparams[4] = {
|
|
{ 1, { 0, 0, 0 } },
|
|
};
|
|
|
|
static struct contrparams *actcontroller = &contrparams[0];
|
|
|
|
/*
|
|
* numbers
|
|
*/
|
|
static char *opt_number = 0;
|
|
static char *opt_callbacknumber = 0;
|
|
static char *opt_cli = 0;
|
|
static STRINGLIST *numbers;
|
|
static STRINGLIST *callbacknumbers;
|
|
static STRINGLIST *clis;
|
|
/*
|
|
* protocol
|
|
*/
|
|
#define PROTO_HDLC 0
|
|
#define PROTO_X75 1
|
|
#define PROTO_V42BIS 2
|
|
#define PROTO_MODEM 3
|
|
#define PROTO_V110_ASYNC 4
|
|
#define PROTO_V120_ASYNC 5
|
|
#define PROTO_ADSLPPPOE 6
|
|
#define PROTO_ADSLPPPOA 7
|
|
#define PROTO_ADSLPPPOALLC 8
|
|
#define PROTO_ANALOGMODEM 9
|
|
static char *opt_proto = "hdlc";
|
|
static int proto = PROTO_HDLC;
|
|
static int opt_avmadsl = 0;
|
|
static int opt_vpi = -1; /* T-DSL: 1 */
|
|
static int opt_vci = -1; /* T-DSL: 32 */
|
|
/*
|
|
* leased line
|
|
*/
|
|
static char *opt_channels = 0;
|
|
static unsigned char AdditionalInfo[1+2+2+31];
|
|
static unsigned char B1Config[1+2+2+2];
|
|
/*
|
|
* redialing
|
|
*/
|
|
static int opt_dialtimeout = 60;
|
|
static int opt_dialmax = 4;
|
|
static int opt_redialdelay = 5;
|
|
/*
|
|
* callback & COSO
|
|
*/
|
|
static int opt_cbdelay = 2;
|
|
static int opt_cbflag = 0;
|
|
static int opt_cbwait = 60;
|
|
#define COSO_CALLER 0
|
|
#define COSO_LOCAL 1
|
|
#define COSO_REMOTE 2
|
|
static char *opt_coso = 0;
|
|
static int coso = COSO_CALLER;
|
|
/*
|
|
* connect delay
|
|
*/
|
|
static int opt_connectdelay = 0;
|
|
/*
|
|
* accept delay
|
|
*/
|
|
static int opt_acceptdelayflag = 0;
|
|
/*
|
|
* demand wakeup by phone call
|
|
*/
|
|
static int opt_voicecallwakeup = 0;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int optcb(void) { return opt_cbflag = 1; }
|
|
static int optacceptdelay(void) { return opt_acceptdelayflag = 1; }
|
|
static int optvoicecallwakeup(void) { return opt_voicecallwakeup = 1; }
|
|
static int optavmadsl(void) { return opt_avmadsl = 1; }
|
|
|
|
static option_t my_options[] = {
|
|
|
|
/* controller */
|
|
|
|
{
|
|
"controller", o_string, &opt_controller[0],
|
|
"capi controller specification"
|
|
},
|
|
{
|
|
"numberprefix", o_string, &contrparams[0].opt_numberprefix,
|
|
"prefix for number"
|
|
},
|
|
{
|
|
"msn", o_string, &contrparams[0].opt_msn,
|
|
"number to call from"
|
|
},
|
|
{
|
|
"inmsn", o_string, &contrparams[0].opt_inmsn,
|
|
"called number for incoming calls"
|
|
},
|
|
|
|
/* controller1 */
|
|
|
|
{
|
|
"controller1", o_string, &opt_controller[1],
|
|
"capi controller specification"
|
|
},
|
|
{
|
|
"numberprefix1", o_string, &contrparams[1].opt_numberprefix,
|
|
"prefix for number"
|
|
},
|
|
{
|
|
"msn1", o_string, &contrparams[1].opt_msn,
|
|
"number to call from"
|
|
},
|
|
{
|
|
"inmsn1", o_string, &contrparams[1].opt_inmsn,
|
|
"called number for incoming calls"
|
|
},
|
|
|
|
/* controller2 */
|
|
|
|
{
|
|
"controller2", o_string, &opt_controller[2],
|
|
"capi controller specification"
|
|
},
|
|
{
|
|
"numberprefix2", o_string, &contrparams[2].opt_numberprefix,
|
|
"prefix for number"
|
|
},
|
|
{
|
|
"msn2", o_string, &contrparams[2].opt_msn,
|
|
"number to call from"
|
|
},
|
|
{
|
|
"inmsn2", o_string, &contrparams[2].opt_inmsn,
|
|
"called number for incoming calls"
|
|
},
|
|
|
|
/* controller3 */
|
|
|
|
{
|
|
"controller3", o_string, &opt_controller[3],
|
|
"capi controller specification"
|
|
},
|
|
{
|
|
"numberprefix3", o_string, &contrparams[3].opt_numberprefix,
|
|
"prefix for number"
|
|
},
|
|
{
|
|
"msn3", o_string, &contrparams[3].opt_msn,
|
|
"number to call from"
|
|
},
|
|
{
|
|
"inmsn3", o_string, &contrparams[3].opt_inmsn,
|
|
"called number for incoming calls"
|
|
},
|
|
|
|
/* */
|
|
|
|
{
|
|
"number", o_string, &opt_number,
|
|
"number to call (may be comma separated)"
|
|
},
|
|
{
|
|
"protocol", o_string, &opt_proto,
|
|
"protocol x75, hdlc, modem, adslpppoe"
|
|
},
|
|
{
|
|
"cli", o_string, &opt_cli,
|
|
"calling number for incoming calls (may be comma separated list)"
|
|
},
|
|
{
|
|
"clicb", o_special_noarg, &optcb,
|
|
"call number and wait for callback"
|
|
},
|
|
{
|
|
"cbwait", o_int, &opt_cbwait,
|
|
"number of seconds to wait for callback"
|
|
},
|
|
{
|
|
"dialtimeout", o_int, &opt_dialtimeout,
|
|
"number of seconds to wait for connection or reject"
|
|
},
|
|
{
|
|
"dialmax", o_int, &opt_dialmax,
|
|
"number of dial retries"
|
|
},
|
|
{
|
|
"redialdelay", o_int, &opt_redialdelay,
|
|
"number of seconds to wait between dial retries"
|
|
},
|
|
{
|
|
"channels", o_string, &opt_channels,
|
|
"channel to use for leased line (may be comma separated list)"
|
|
},
|
|
|
|
{
|
|
"cbdelay", o_int, &opt_cbdelay,
|
|
"number of seconds to wait before calling back"
|
|
},
|
|
{
|
|
"cbnumber", o_string, &opt_callbacknumber,
|
|
"number to call (may be comma separated)"
|
|
},
|
|
{
|
|
"connectdelay", o_int, &opt_connectdelay,
|
|
"number of seconds to wait after connection is established"
|
|
},
|
|
{
|
|
"acceptdelay", o_special_noarg, &optacceptdelay,
|
|
"wait 1 second before accept incoming call"
|
|
},
|
|
{
|
|
"coso", o_string, &opt_coso,
|
|
"COSO: caller,local or remote"
|
|
},
|
|
{
|
|
"voicecallwakeup", o_special_noarg, &optvoicecallwakeup,
|
|
"call number and wait for callback"
|
|
},
|
|
{
|
|
"vpi", o_int, &opt_vpi,
|
|
"VPI for Fritz!Card DSL"
|
|
},
|
|
{
|
|
"vci", o_int, &opt_vci,
|
|
"VCI for Fritz!Card DSL"
|
|
},
|
|
{
|
|
"avmadsl", o_special_noarg, &optavmadsl,
|
|
"read DSL parameters from /etc/drdsl/adsl.conf"
|
|
},
|
|
{ NULL }
|
|
};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int nextcontroller(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (&contrparams[i] <= actcontroller)
|
|
continue;
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
actcontroller = &contrparams[i];
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- Handle leased lines (CAPI-Bundling) ----------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int decodechannels(char *teln, unsigned long *bmaskp, int *activep)
|
|
{
|
|
unsigned long bmask = 0;
|
|
int active = !0;
|
|
char *s;
|
|
int channel;
|
|
int i;
|
|
|
|
s = teln;
|
|
while (*s && *s == ' ') s++;
|
|
if (!*s)
|
|
fatal("capiplugin: option channels: list empty");
|
|
if (*s == 'p' || *s == 'P') {
|
|
active = 0;
|
|
s++;
|
|
}
|
|
if (*s == 'a' || *s == 'A') {
|
|
active = !0;
|
|
s++;
|
|
}
|
|
while (*s) {
|
|
int digit1 = 0;
|
|
int digit2 = 0;
|
|
if (!isdigit(*s))
|
|
goto illegal;
|
|
while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; }
|
|
channel = digit1;
|
|
if (channel <= 0 && channel > 30)
|
|
goto rangeerror;
|
|
if (*s == 0 || *s == ',' || *s == ' ') {
|
|
bmask |= (1 << digit1);
|
|
digit1 = 0;
|
|
if (*s) s++;
|
|
continue;
|
|
}
|
|
if (*s != '-')
|
|
goto illegal;
|
|
s++;
|
|
if (!isdigit(*s)) return -3;
|
|
while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; }
|
|
channel = digit2;
|
|
if (channel <= 0 && channel > 30)
|
|
goto rangeerror;
|
|
if (*s == 0 || *s == ',' || *s == ' ') {
|
|
if (digit1 > digit2)
|
|
for (i = digit2; i <= digit1 ; i++)
|
|
bmask |= (1 << i);
|
|
else
|
|
for (i = digit1; i <= digit2 ; i++)
|
|
bmask |= (1 << i);
|
|
digit1 = digit2 = 0;
|
|
if (*s) s++;
|
|
continue;
|
|
}
|
|
goto illegal;
|
|
}
|
|
if (activep) *activep = active;
|
|
if (bmaskp) *bmaskp = bmask;
|
|
return 0;
|
|
illegal:
|
|
fatal("capiplugin: option channels: illegal octet '%c'", *s);
|
|
return -1;
|
|
rangeerror:
|
|
fatal("capiplugin: option channels: channel %d out of range", channel);
|
|
return -1;
|
|
}
|
|
|
|
static int channels2capi20(char *teln, unsigned char *AdditionalInfo)
|
|
{
|
|
unsigned long bmask;
|
|
int active;
|
|
int i;
|
|
|
|
decodechannels(teln, &bmask, &active);
|
|
/* info("capiplugin: \"%s\" 0x%lx %d\n", teln, bmask, active); */
|
|
/* Length */
|
|
AdditionalInfo[0] = 2+2+31;
|
|
/* Channel: 3 => use channel allocation */
|
|
AdditionalInfo[1] = 3; AdditionalInfo[2] = 0;
|
|
/* Operation: 0 => DTE mode, 1 => DCE mode */
|
|
if (active) {
|
|
AdditionalInfo[3] = 0; AdditionalInfo[4] = 0;
|
|
} else {
|
|
AdditionalInfo[3] = 1; AdditionalInfo[4] = 0;
|
|
}
|
|
/* Channel mask array */
|
|
AdditionalInfo[5] = 0; /* no D-Channel */
|
|
for (i=1; i <= 30; i++)
|
|
AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void plugin_check_options(void)
|
|
{
|
|
static int init = 0;
|
|
int i;
|
|
|
|
if (init)
|
|
return;
|
|
init = 1;
|
|
|
|
if (opt_avmadsl) {
|
|
if (access(AVMADSLPARAMFILE, R_OK) == 0) {
|
|
dbglog("loading adsl parameters from %s ...", AVMADSLPARAMFILE);
|
|
if (options_from_file (AVMADSLPARAMFILE, 0, 0, 0) == 0)
|
|
die(1);
|
|
} else {
|
|
dbglog("using default adsl parameters");
|
|
if (!opt_controller[0]) opt_controller[0] = "2";
|
|
opt_proto = "adslpppoe";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* protocol
|
|
*/
|
|
if (strcasecmp(opt_proto, "hdlc") == 0) {
|
|
proto = PROTO_HDLC;
|
|
} else if (strcasecmp(opt_proto, "x75") == 0) {
|
|
proto = PROTO_X75;
|
|
} else if (strcasecmp(opt_proto, "v42bis") == 0) {
|
|
proto = PROTO_V42BIS;
|
|
} else if (strcasecmp(opt_proto, "modem") == 0) {
|
|
proto = PROTO_MODEM;
|
|
} else if (strcasecmp(opt_proto, "v110async") == 0) {
|
|
proto = PROTO_V110_ASYNC;
|
|
} else if (strcasecmp(opt_proto, "v120async") == 0) {
|
|
proto = PROTO_V120_ASYNC;
|
|
} else if (strcasecmp(opt_proto, "adslpppoe") == 0) {
|
|
proto = PROTO_ADSLPPPOE;
|
|
if (!opt_channels) opt_channels = "1";
|
|
} else if (strcasecmp(opt_proto, "adslpppoa") == 0) {
|
|
proto = PROTO_ADSLPPPOA;
|
|
if (!opt_channels) opt_channels = "1";
|
|
} else if (strcasecmp(opt_proto, "adslpppoallc") == 0) {
|
|
proto = PROTO_ADSLPPPOALLC;
|
|
if (!opt_channels) opt_channels = "1";
|
|
} else if (strcasecmp(opt_proto, "analogmodem") == 0) {
|
|
proto = PROTO_ANALOGMODEM;
|
|
} else {
|
|
option_error("capiplugin: unknown protocol \"%s\"", opt_proto);
|
|
die(1);
|
|
}
|
|
if (strcasecmp(opt_proto, "modem") == 0 ||
|
|
strcasecmp(opt_proto, "analogmodem") == 0)
|
|
cipmask = CIPMASK_VOICE;
|
|
else cipmask = CIPMASK_DATA;
|
|
|
|
/*
|
|
* coso
|
|
*/
|
|
if (opt_coso == 0) {
|
|
if (opt_cbflag) {
|
|
if (opt_number) coso = COSO_REMOTE;
|
|
else coso = COSO_LOCAL;
|
|
} else {
|
|
coso = COSO_CALLER;
|
|
}
|
|
} else if (strcasecmp(opt_coso, "caller") == 0) {
|
|
coso = COSO_CALLER;
|
|
} else if (strcasecmp(opt_coso, "local") == 0) {
|
|
coso = COSO_LOCAL;
|
|
} else if (strcasecmp(opt_coso, "remote") == 0) {
|
|
coso = COSO_REMOTE;
|
|
} else {
|
|
option_error("capiplugin: wrong value for option coso");
|
|
die(1);
|
|
}
|
|
if (opt_cbflag) {
|
|
if (opt_coso) {
|
|
option_error("capiplugin: option clicb ignored");
|
|
} else if (coso == COSO_REMOTE) {
|
|
dbglog("clicb selected coso remote");
|
|
} else if (coso == COSO_LOCAL) {
|
|
dbglog("clicb selected coso local");
|
|
} else {
|
|
option_error("capiplugin: option clicb ignored");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* leased line
|
|
*/
|
|
if (opt_channels) {
|
|
channels2capi20(opt_channels, AdditionalInfo);
|
|
if (opt_number)
|
|
option_error("capiplugin: option number ignored");
|
|
if (contrparams[0].opt_numberprefix)
|
|
option_error("capiplugin: option numberprefix ignored");
|
|
if (opt_callbacknumber)
|
|
option_error("capiplugin: option callbacknumber ignored");
|
|
if (contrparams[0].opt_msn)
|
|
option_error("capiplugin: option msn ignored");
|
|
if (contrparams[0].opt_inmsn)
|
|
option_error("capiplugin: option inmsn ignored");
|
|
/*
|
|
* dialout
|
|
*/
|
|
} else if (opt_number) {
|
|
stringlist_free(&numbers);
|
|
numbers = stringlist_split(opt_number, " \t,");
|
|
/*
|
|
* dialin
|
|
*/
|
|
} else {
|
|
if (coso == COSO_LOCAL) {
|
|
if (opt_callbacknumber == 0) {
|
|
option_error("capiplugin: option callbacknumber missing");
|
|
die(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (opt_callbacknumber) {
|
|
stringlist_free(&callbacknumbers);
|
|
callbacknumbers = stringlist_split(opt_callbacknumber, " \t,");
|
|
}
|
|
|
|
/*
|
|
* controller
|
|
*/
|
|
for (i = 0; i < DIM(opt_controller); i++) {
|
|
if (opt_controller[i]) {
|
|
STRINGLIST *parsed_controller = 0;
|
|
STRINGLIST *sl;
|
|
char *tmp;
|
|
memset(&contrparams[i].cinfo, 0, sizeof(capi_contrinfo));
|
|
parsed_controller = stringlist_split(opt_controller[i], " \t,");
|
|
sl = parsed_controller;
|
|
if (!sl) goto illcontr;
|
|
tmp = sl->s;
|
|
contrparams[i].controller = strtol(sl->s, &tmp, 10);
|
|
if (tmp == sl->s || *tmp) goto illcontr;
|
|
if (sl->next) {
|
|
sl = sl->next;
|
|
contrparams[i].cinfo.ddi = strdup(sl->s);
|
|
if (contrparams[i].cinfo.ddi == 0)
|
|
goto illcontr;
|
|
if (sl->next && sl->next->s) {
|
|
sl = sl->next;
|
|
contrparams[i].cinfo.ndigits = strtol(sl->s, &tmp, 10);
|
|
if (tmp == sl->s || *tmp) {
|
|
stringlist_free(&parsed_controller);
|
|
goto illcontr;
|
|
}
|
|
}
|
|
}
|
|
stringlist_free(&parsed_controller);
|
|
} else {
|
|
memset(&contrparams[i].cinfo, 0, sizeof(capi_contrinfo));
|
|
contrparams[i].controller = 1;
|
|
}
|
|
continue;
|
|
illcontr:
|
|
option_error("capiplugin: illegal controller specification \"%s\"",
|
|
opt_controller[i]);
|
|
die(1);
|
|
}
|
|
|
|
/*
|
|
* cli & inmsn
|
|
*/
|
|
if (opt_cli) {
|
|
STRINGLIST *sl;
|
|
char *old;
|
|
stringlist_free(&clis);
|
|
clis = stringlist_split(opt_cli, " \t,");
|
|
for (sl = clis; sl; sl = sl->next) {
|
|
if (*sl->s != '*') {
|
|
old = sl->s;
|
|
sl->s = (char *)malloc(strlen(old)+2);
|
|
if (sl->s) {
|
|
*sl->s = '*';
|
|
strcpy(sl->s+1, old);
|
|
free(old);
|
|
} else {
|
|
sl->s = old;
|
|
option_error("capiplugin: prepend '*' to cli failed");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].opt_inmsn) {
|
|
stringlist_free(&contrparams[i].inmsns);
|
|
contrparams[i].inmsns = stringlist_split(contrparams[i].opt_inmsn, " \t,");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* dial on demand
|
|
*/
|
|
if (demand) {
|
|
if (!opt_number && !opt_channels) {
|
|
option_error("capiplugin: number or channels missing for demand");
|
|
die(1);
|
|
}
|
|
if (opt_voicecallwakeup)
|
|
cipmask |= CIPMASK_VOICE;
|
|
} else if (opt_voicecallwakeup) {
|
|
option_error("capiplugin: option voicecallwakeup ignored");
|
|
opt_voicecallwakeup = 0;
|
|
}
|
|
|
|
if ( proto == PROTO_ADSLPPPOE
|
|
|| proto == PROTO_ADSLPPPOA
|
|
|| proto == PROTO_ADSLPPPOALLC) {
|
|
if (opt_cbflag) {
|
|
option_error("capiplugin: option cbflag not alloed with protocol adslpppoe");
|
|
die(1);
|
|
}
|
|
if (opt_coso) {
|
|
option_error("capiplugin: option coso not alloed with protocol adslpppoe");
|
|
die(1);
|
|
}
|
|
if (opt_number) {
|
|
option_error("capiplugin: option number not alloed with protocol adslpppoe");
|
|
die(1);
|
|
}
|
|
if (opt_callbacknumber) {
|
|
option_error("capiplugin: option cbnumber not alloed with protocol adslpppoe");
|
|
die(1);
|
|
}
|
|
if (contrparams[0].opt_inmsn) {
|
|
option_error("capiplugin: option inmsn ignored");
|
|
contrparams[0].opt_inmsn = 0;
|
|
}
|
|
if (opt_cli) {
|
|
option_error("capiplugin: option cli ignored");
|
|
opt_cli = 0;
|
|
}
|
|
if (proto == PROTO_ADSLPPPOA || proto == PROTO_ADSLPPPOALLC) {
|
|
if (opt_vpi == -1 || opt_vci == -1) {
|
|
option_error("capiplugin: need options vci and vpi");
|
|
die(1);
|
|
}
|
|
} else {
|
|
if ( (opt_vpi == -1 && opt_vci != -1)
|
|
|| (opt_vpi != -1 && opt_vci == -1)) {
|
|
option_error("capiplugin: need options vci and vpi");
|
|
die(1);
|
|
}
|
|
}
|
|
B1Config[0] = 0;
|
|
if (opt_vpi != -1) {
|
|
int vcctype = 1;
|
|
if (proto == PROTO_ADSLPPPOA) vcctype = 3;
|
|
else if (proto == PROTO_ADSLPPPOALLC) vcctype = 2;
|
|
B1Config[0] = 6;
|
|
B1Config[1] = vcctype & 0xff;
|
|
B1Config[2] = (vcctype >> 8) & 0xff;
|
|
B1Config[3] = opt_vpi & 0xff;
|
|
B1Config[4] = (opt_vpi >> 8) & 0xff;
|
|
B1Config[5] = opt_vci & 0xff;
|
|
B1Config[6] = (opt_vci >> 8) & 0xff;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- Match with * and ? ---------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int shmatch(char *string, char *expr)
|
|
{
|
|
char *match = expr;
|
|
char *s = string;
|
|
char *p;
|
|
int escape = 0;
|
|
|
|
while (*match && *s) {
|
|
if (escape) {
|
|
if (*s != *match)
|
|
return 0;
|
|
s++;
|
|
match++;
|
|
} else if (*match == '\\') {
|
|
match++;
|
|
escape = 1;
|
|
} else if (*match == '*') {
|
|
match++;
|
|
if (*match == 0)
|
|
return 1;
|
|
if (*match == '\\')
|
|
match++;
|
|
while ((p = strchr(s, *match)) != 0) {
|
|
if (shmatch(p+1, match+1))
|
|
return 1;
|
|
s = p + 1;
|
|
}
|
|
return 0;
|
|
} else if (*match == '?') {
|
|
s++;
|
|
match++;
|
|
} else {
|
|
if (*s != *match)
|
|
return 0;
|
|
s++;
|
|
match++;
|
|
}
|
|
}
|
|
if (*s == 0) {
|
|
if (*match == 0) return 1;
|
|
if (*match == '*' && match[1] == 0) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- STRINGLIST for parsing ------------------------------------ */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void stringlist_free(STRINGLIST **pp)
|
|
{
|
|
STRINGLIST *p, *next;
|
|
|
|
p = *pp;
|
|
while (p) {
|
|
next = p->next;
|
|
if (p->s) free(p->s);
|
|
free(p);
|
|
p = next;
|
|
}
|
|
*pp = 0;
|
|
}
|
|
|
|
static int stringlist_append_string(STRINGLIST **pp, char *s)
|
|
{
|
|
STRINGLIST *p;
|
|
for (; *pp; pp = &(*pp)->next) ;
|
|
if ((p = (STRINGLIST *)malloc(sizeof(STRINGLIST))) == 0)
|
|
return -1;
|
|
memset(p, 0, sizeof(STRINGLIST));
|
|
if ((p->s = strdup(s)) == 0) {
|
|
free(p);
|
|
return -1;
|
|
}
|
|
p->next = 0;
|
|
*pp = p;
|
|
return 0;
|
|
}
|
|
|
|
static STRINGLIST *stringlist_split(char *tosplit, char *seps)
|
|
{
|
|
STRINGLIST *p = 0;
|
|
char *str = strdup(tosplit);
|
|
char *s;
|
|
if (!str) return 0;
|
|
for (s = strtok(str, seps); s; s = strtok(0, seps)) {
|
|
if (*s == 0) continue; /* if strtok is not working correkt */
|
|
if (stringlist_append_string(&p, s) < 0) {
|
|
stringlist_free(&p);
|
|
free(str);
|
|
return 0;
|
|
}
|
|
}
|
|
free(str);
|
|
return p;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- connection management ------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
typedef struct conn {
|
|
struct conn *next;
|
|
capi_connection *conn;
|
|
int type;
|
|
#define CONNTYPE_OUTGOING 0
|
|
#define CONNTYPE_INCOMING 1
|
|
#define CONNTYPE_IGNORE 2
|
|
#define CONNTYPE_REJECT 3
|
|
#define CONNTYPE_FOR_CALLBACK 4
|
|
int inprogress;
|
|
int isconnected;
|
|
} CONN;
|
|
|
|
static CONN *connections;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static CONN *conn_remember(capi_connection *conn, int type)
|
|
{
|
|
CONN *p, **pp;
|
|
for (pp = &connections; *pp; pp = &(*pp)->next) ;
|
|
if ((p = (CONN *)malloc(sizeof(CONN))) == 0) {
|
|
int serrno = errno;
|
|
fatal("capiplugin: malloc failed - %s (%d)",
|
|
strerror(serrno), serrno);
|
|
return 0;
|
|
}
|
|
memset(p, 0, sizeof(CONN));
|
|
p->conn = conn;
|
|
p->type = type;
|
|
p->next = 0;
|
|
switch (type) {
|
|
case CONNTYPE_OUTGOING:
|
|
case CONNTYPE_INCOMING:
|
|
case CONNTYPE_FOR_CALLBACK:
|
|
p->inprogress = 1;
|
|
p->isconnected = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
*pp = p;
|
|
return p;
|
|
}
|
|
|
|
static int conn_forget(capi_connection *conn)
|
|
{
|
|
CONN **pp, *p;
|
|
for (pp = &connections; *pp && (*pp)->conn != conn; pp = &(*pp)->next) ;
|
|
if (*pp) {
|
|
p = *pp;
|
|
*pp = (*pp)->next;
|
|
free(p);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static CONN *conn_find(capi_connection *cp)
|
|
{
|
|
CONN *p;
|
|
for (p = connections; p; p = p->next) {
|
|
if (p->conn == cp)
|
|
return p;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int conn_connected(capi_connection *conn)
|
|
{
|
|
CONN *p;
|
|
for (p = connections; p; p = p->next) {
|
|
if (p->conn == conn) {
|
|
p->isconnected = 1;
|
|
p->inprogress = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
fatal("capiplugin: connected connection not found ??");
|
|
return -1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int conn_incoming_inprogress(void)
|
|
{
|
|
CONN *p;
|
|
for (p = connections; p; p = p->next) {
|
|
if (p->type == CONNTYPE_INCOMING)
|
|
return p->inprogress;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int conn_incoming_connected(void)
|
|
{
|
|
CONN *p;
|
|
for (p = connections; p; p = p->next) {
|
|
if (p->type == CONNTYPE_INCOMING)
|
|
return p->isconnected;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int conn_incoming_exists(void)
|
|
{
|
|
CONN *p;
|
|
for (p = connections; p; p = p->next) {
|
|
if (p->type == CONNTYPE_INCOMING)
|
|
return p->isconnected || p->inprogress;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int conn_inprogress(capi_connection *cp)
|
|
{
|
|
CONN *p;
|
|
for (p = connections; p; p = p->next) {
|
|
if (p->conn == cp)
|
|
return p->inprogress;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int conn_isconnected(capi_connection *cp)
|
|
{
|
|
CONN *p;
|
|
if (cp) {
|
|
for (p = connections; p; p = p->next) {
|
|
if (p->conn == cp)
|
|
return p->isconnected;
|
|
}
|
|
} else {
|
|
for (p = connections; p; p = p->next) {
|
|
if (p->isconnected)
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- Handle CAPI messages every second ------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int timeoutrunning = 0;
|
|
static int timeoutshouldrun = 0;
|
|
|
|
static void timeoutfunc(void *arg)
|
|
{
|
|
unsigned char *msg = 0;
|
|
/* info("capiplugin: checking for capi messages"); */
|
|
while (capi20_get_message (applid, &msg) == 0)
|
|
capiconn_inject(applid, msg);
|
|
if (wakeupnow && curphase == PHASE_DORMANT)
|
|
wakeupdemand();
|
|
if (timeoutshouldrun)
|
|
_timeout (timeoutfunc, 0, 1);
|
|
}
|
|
|
|
static void setup_timeout(void)
|
|
{
|
|
timeoutshouldrun = 1;
|
|
if (!timeoutrunning)
|
|
_timeout (timeoutfunc, 0, 1);
|
|
}
|
|
|
|
#if PPPVER >= PPPVersion(2,4,0,0)
|
|
static void unsetup_timeout(void)
|
|
{
|
|
timeoutshouldrun = 0;
|
|
if (timeoutrunning)
|
|
untimeout (timeoutfunc, 0);
|
|
timeoutrunning = 0;
|
|
}
|
|
#endif
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- demand & wakeup pppd -------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static u_int32_t ouripaddr;
|
|
static u_int32_t gwipaddr;
|
|
static int nwakeuppackets = 0;
|
|
|
|
static void setupincoming_for_demand(void)
|
|
{
|
|
struct ifreq ifr;
|
|
int serrno;
|
|
int sock_fd;
|
|
int i;
|
|
|
|
nwakeuppackets = 0;
|
|
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
serrno = errno;
|
|
fatal("capiplugin: socket(AF_INET,SOCK_DRAM): failed - %s (%d)",
|
|
strerror(serrno), serrno);
|
|
return;
|
|
}
|
|
|
|
memset (&ifr, '\0', sizeof (ifr));
|
|
strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
|
|
if (ioctl(sock_fd, SIOCGIFADDR, (caddr_t) &ifr) < 0) {
|
|
serrno = errno;
|
|
close(sock_fd);
|
|
fatal("capiplugin: ioctl(SIOCGIFADDR): failed - %s (%d)",
|
|
strerror(serrno), serrno);
|
|
return;
|
|
}
|
|
ouripaddr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
|
|
|
|
memset (&ifr, '\0', sizeof (ifr));
|
|
strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
|
|
if (ioctl(sock_fd, SIOCGIFDSTADDR, (caddr_t) &ifr) < 0) {
|
|
serrno = errno;
|
|
close(sock_fd);
|
|
fatal("capiplugin: ioctl(SIOCGIFDSTADDR): failed - %s (%d)",
|
|
strerror(serrno), serrno);
|
|
return;
|
|
}
|
|
gwipaddr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
|
|
|
|
close(sock_fd);
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void) capiconn_listen(ctx, contrparams[i].controller, cipmask, 0);
|
|
}
|
|
info("capiplugin: waiting for demand wakeup ...");
|
|
}
|
|
|
|
static void wakeupdemand(void)
|
|
{
|
|
char data[] = "Ignore, is for demand wakeup";
|
|
struct sockaddr_in laddr, raddr;
|
|
size_t addrlen;
|
|
int sock_fd;
|
|
int serrno;
|
|
|
|
if (curphase != PHASE_DORMANT) {
|
|
info("capiplugin: wakeup not possible in phase %s, delayed",
|
|
phase2str(curphase));
|
|
wakeupnow++;
|
|
return;
|
|
}
|
|
|
|
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
serrno = errno;
|
|
fatal("capiplugin: socket(AF_INET,SOCK_DRAM): failed - %s (%d)",
|
|
strerror(serrno), serrno);
|
|
return;
|
|
}
|
|
memset (&laddr, '\0', sizeof(laddr));
|
|
laddr.sin_family = AF_INET;
|
|
laddr.sin_addr.s_addr = ouripaddr;
|
|
laddr.sin_port = 0;
|
|
if (bind(sock_fd, (struct sockaddr *)&laddr, sizeof(laddr)) != 0) {
|
|
serrno = errno;
|
|
close(sock_fd);
|
|
fatal("capiplugin: bind(%I): failed - %s (%d)",
|
|
ouripaddr, strerror(serrno), serrno);
|
|
return;
|
|
}
|
|
addrlen = sizeof(laddr);
|
|
getsockname(sock_fd, (struct sockaddr *)&laddr, &addrlen);
|
|
memset (&raddr, '\0', sizeof(raddr));
|
|
raddr.sin_family = AF_INET;
|
|
raddr.sin_addr.s_addr = gwipaddr;
|
|
raddr.sin_port = htons(9); /* discard */
|
|
if (connect(sock_fd, (struct sockaddr *)&raddr, sizeof(raddr)) != 0) {
|
|
serrno = errno;
|
|
close(sock_fd);
|
|
fatal("capiplugin: connect(%I:%d): failed - %s (%d)",
|
|
gwipaddr, ntohs(raddr.sin_port), strerror(serrno), serrno);
|
|
return;
|
|
}
|
|
notice("capiplugin: sending wakeup packet (UDP %I:%d -> %I:%d)",
|
|
ouripaddr, ntohs(laddr.sin_port),
|
|
gwipaddr, ntohs(raddr.sin_port));
|
|
if (send(sock_fd, data, sizeof(data), 0) < 0) {
|
|
serrno = errno;
|
|
close(sock_fd);
|
|
fatal("capiplugin: send wakup packet failed - %s (%d)",
|
|
strerror(serrno), serrno);
|
|
return;
|
|
}
|
|
close(sock_fd);
|
|
nwakeuppackets++;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- CAPI setup & handling ------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void init_capiconn(void)
|
|
{
|
|
static int init = 0;
|
|
int i;
|
|
|
|
if (init)
|
|
return;
|
|
init = 1;
|
|
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
if (capiconn_addcontr(ctx, contrparams[i].controller, &contrparams[i].cinfo) != CAPICONN_OK) {
|
|
(void)capiconn_freecontext(ctx);
|
|
(void)capi20_release(applid);
|
|
fatal("capiplugin: add controller %d failed", contrparams[i].controller);
|
|
return;
|
|
}
|
|
if (contrparams[i].cinfo.ddi)
|
|
dbglog("capiplugin: contr=%d ddi=\"%s\" n=%d",
|
|
contrparams[i].controller, contrparams[i].cinfo.ddi, contrparams[i].cinfo.ndigits);
|
|
else
|
|
dbglog("capiplugin: contr=%d", contrparams[i].controller);
|
|
}
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
|
|
}
|
|
handlemessages();
|
|
add_fd(capi20_fileno(applid));
|
|
setup_timeout();
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void handlemessages(void)
|
|
{
|
|
unsigned char *msg = 0;
|
|
struct timeval tv;
|
|
tv.tv_sec = 1;
|
|
tv.tv_usec = 0;
|
|
if (capi20_waitformessage(applid, &tv) == 0) {
|
|
if (capi20_get_message(applid, &msg) == 0)
|
|
capiconn_inject(applid, msg);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void dodisconnect(capi_connection *cp)
|
|
{
|
|
CONN *conn;
|
|
time_t t;
|
|
|
|
if ((conn = conn_find(cp)) == 0)
|
|
return;
|
|
(void)capiconn_disconnect(cp, 0);
|
|
conn->isconnected = conn->inprogress = 0;
|
|
|
|
t = time(0)+10;
|
|
do {
|
|
handlemessages();
|
|
} while (conn_find(cp) && time(0) < t);
|
|
|
|
if (conn_find(cp))
|
|
fatal("capiplugin: timeout while waiting for disconnect");
|
|
}
|
|
|
|
static void disconnectall(void)
|
|
{
|
|
time_t t;
|
|
CONN *p;
|
|
int i;
|
|
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
|
|
}
|
|
for (p = connections; p; p = p->next) {
|
|
if (p->inprogress || p->isconnected) {
|
|
p->isconnected = p->inprogress = 0;
|
|
capiconn_disconnect(p->conn, 0);
|
|
}
|
|
}
|
|
t = time(0)+10;
|
|
do {
|
|
handlemessages();
|
|
} while (connections && time(0) < t);
|
|
|
|
if (connections && !exiting)
|
|
fatal("capiplugin: disconnectall failed");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static char *conninfo(capi_connection *p)
|
|
{
|
|
static char buf[1024];
|
|
capi_conninfo *cp = capiconn_getinfo(p);
|
|
char *callingnumber = "";
|
|
char *callednumber = "";
|
|
|
|
if (cp->callingnumber && cp->callingnumber[0] > 2)
|
|
callingnumber = cp->callingnumber+3;
|
|
if (cp->callednumber && cp->callednumber[0] > 1)
|
|
callednumber = cp->callednumber+2;
|
|
|
|
if (debug) {
|
|
snprintf(buf, sizeof(buf),
|
|
"\"%s\" -> \"%s\" %s (pcli=0x%x/ncci=0x%x)",
|
|
callingnumber, callednumber,
|
|
cp->isincoming ? "incoming" : "outgoing",
|
|
cp->plci, cp->ncci
|
|
);
|
|
} else {
|
|
snprintf(buf, sizeof(buf),
|
|
"\"%s\" -> \"%s\" %s",
|
|
callingnumber, callednumber,
|
|
cp->isincoming ? "incoming" : "outgoing");
|
|
}
|
|
buf[sizeof(buf)-1] = 0;
|
|
return buf;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- reject reason handling (wuerg) ---------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static unsigned dreason = 0;
|
|
|
|
static int was_no_reject(void)
|
|
{
|
|
if ((dreason & 0xff00) != 0x3400)
|
|
return 1;
|
|
switch (dreason) {
|
|
case 0x34a2: /* No circuit / channel available */
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int try_next_controller(void)
|
|
{
|
|
switch (dreason) {
|
|
case 0x3301: /* Protocol error layer 1 (broken line or B-channel removed by signalling protocol */
|
|
case 0x34a2: /* No circuit / channel available */
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- disconnect handler ---------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void disconnected(capi_connection *cp,
|
|
int localdisconnect,
|
|
unsigned reason,
|
|
unsigned reason_b3)
|
|
{
|
|
CONN *p;
|
|
|
|
if ((p = conn_find(cp)) == 0)
|
|
return;
|
|
conn_forget(cp);
|
|
switch (p->type) {
|
|
case CONNTYPE_INCOMING:
|
|
break;
|
|
case CONNTYPE_IGNORE:
|
|
case CONNTYPE_REJECT:
|
|
return;
|
|
case CONNTYPE_OUTGOING:
|
|
case CONNTYPE_FOR_CALLBACK:
|
|
dreason = reason;
|
|
break;
|
|
|
|
}
|
|
if (reason != 0x3304 || debug) /* Another Applikation got the call */
|
|
info("capiplugin: disconnect(%s): %s 0x%04x (0x%04x) - %s",
|
|
localdisconnect ? "local" : "remote",
|
|
conninfo(cp),
|
|
reason, reason_b3, capi_info2str(reason));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- incoming call handler ------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void incoming(capi_connection *cp,
|
|
unsigned contr,
|
|
unsigned cipvalue,
|
|
char *callednumber,
|
|
char *callingnumber)
|
|
{
|
|
struct contrparams *ctlp = 0;
|
|
STRINGLIST *p;
|
|
char *s;
|
|
int i;
|
|
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
if (contrparams[i].controller != (int)contr)
|
|
continue;
|
|
ctlp = &contrparams[i];
|
|
break;
|
|
}
|
|
|
|
info("capiplugin: incoming call(%u): %s (0x%x)", contr, conninfo(cp), cipvalue);
|
|
if (!ctlp) {
|
|
info("capiplugin: ignoring call, controller %u not found ??",
|
|
contr);
|
|
conn_remember(cp, CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
return;
|
|
}
|
|
|
|
if (conn_incoming_exists()) {
|
|
info("capiplugin: ignoring call, incoming connection exists");
|
|
conn_remember(cp, CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
return;
|
|
}
|
|
|
|
if (ctlp->opt_inmsn) {
|
|
for (p = ctlp->inmsns; p; p = p->next) {
|
|
if ( (s = strstr(callednumber, p->s)) != 0
|
|
&& strcmp(s, p->s) == 0)
|
|
break;
|
|
}
|
|
if (!p) {
|
|
info("capiplugin: ignoring call, msn %s not in \"%s\"",
|
|
callednumber, ctlp->opt_inmsn);
|
|
conn_remember(cp, CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
return;
|
|
}
|
|
} else if (ctlp->opt_msn) {
|
|
if ( (s = strstr(callednumber, ctlp->opt_msn)) == 0
|
|
|| strcmp(s, ctlp->opt_msn) != 0) {
|
|
info("capiplugin: ignoring call, msn mismatch (%s != %s)",
|
|
ctlp->opt_msn, callednumber);
|
|
conn_remember(cp, CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (opt_cli) {
|
|
for (p = clis; p; p = p->next) {
|
|
if (shmatch(callingnumber, p->s))
|
|
break;
|
|
}
|
|
if (!p) {
|
|
info("capiplugin: ignoring call, cli mismatch (%s != %s)",
|
|
opt_cli, callingnumber);
|
|
conn_remember(cp, CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
return;
|
|
}
|
|
} else if (opt_number) {
|
|
for (p = numbers; p; p = p->next) {
|
|
if ( (s = strstr(callingnumber, p->s)) != 0
|
|
&& strcmp(s, p->s) == 0)
|
|
break;
|
|
}
|
|
if (!p) {
|
|
info("capiplugin: ignoring call, number mismatch (%s != %s)",
|
|
opt_number, callingnumber);
|
|
conn_remember(cp, CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
return;
|
|
}
|
|
} else if (opt_acceptdelayflag) {
|
|
/*
|
|
* non cli or number match,
|
|
* give more specific listen a chance (bad)
|
|
*/
|
|
info("capiplugin: accept delayed, no cli or number match");
|
|
sleep(1);
|
|
}
|
|
|
|
switch (cipvalue) {
|
|
case 1: /* Speech */
|
|
case 4: /* 3.1 kHz audio */
|
|
case 5: /* 7 kHz audio */
|
|
case 16: /* Telephony */
|
|
case 26: /* 7 kHz telephony */
|
|
if (proto == PROTO_MODEM || proto == PROTO_ANALOGMODEM) {
|
|
if (demand) goto wakeupmatch;
|
|
if (coso == COSO_LOCAL) goto callback;
|
|
goto accept;
|
|
} else if (opt_voicecallwakeup) {
|
|
goto wakeupdemand;
|
|
} else {
|
|
info("capiplugin: ignoring speech call from %s",
|
|
callingnumber);
|
|
conn_remember(cp,CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
}
|
|
break;
|
|
case 2: /* Unrestricted digital information */
|
|
case 3: /* Restricted digital information */
|
|
if (proto == PROTO_HDLC) {
|
|
if (demand) goto wakeupmatch;
|
|
if (coso == COSO_LOCAL) goto callback;
|
|
goto accept;
|
|
} else if (proto == PROTO_X75) {
|
|
if (demand) goto wakeupmatch;
|
|
if (coso == COSO_LOCAL) goto callback;
|
|
goto accept;
|
|
} else if (proto == PROTO_V42BIS) {
|
|
if (demand) goto wakeupmatch;
|
|
if (coso == COSO_LOCAL) goto callback;
|
|
goto accept;
|
|
} else if (proto == PROTO_V110_ASYNC) {
|
|
if (demand) goto wakeupmatch;
|
|
if (coso == COSO_LOCAL) goto callback;
|
|
goto accept;
|
|
} else if (proto == PROTO_V120_ASYNC) {
|
|
if (demand) goto wakeupmatch;
|
|
if (coso == COSO_LOCAL) goto callback;
|
|
goto accept;
|
|
} else {
|
|
info("capiplugin: ignoring digital call from %s",
|
|
callingnumber);
|
|
conn_remember(cp,CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
}
|
|
break;
|
|
case 17: /* Group 2/3 facsimile */
|
|
info("capiplugin: ignoring fax call from %s",
|
|
callingnumber);
|
|
conn_remember(cp,CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
break;
|
|
default:
|
|
info("capiplugin: ignoring type %d call from %s",
|
|
cipvalue, callingnumber);
|
|
conn_remember(cp,CONNTYPE_IGNORE);
|
|
(void) capiconn_ignore(cp);
|
|
break;
|
|
}
|
|
return;
|
|
|
|
callback:
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
|
|
}
|
|
dbglog("capiplugin: rejecting call: %s (0x%x)", conninfo(cp), cipvalue);
|
|
conn_remember(cp, CONNTYPE_REJECT);
|
|
capiconn_reject(cp);
|
|
makecallback();
|
|
return;
|
|
|
|
wakeupdemand:
|
|
dbglog("capiplugin: rejecting call: %s (0x%x)", conninfo(cp), cipvalue);
|
|
conn_remember(cp, CONNTYPE_REJECT);
|
|
capiconn_reject(cp);
|
|
wakeupdemand();
|
|
return;
|
|
|
|
wakeupmatch:
|
|
if (coso == COSO_LOCAL)
|
|
goto wakeupdemand;
|
|
|
|
accept:
|
|
switch (proto) {
|
|
default:
|
|
case PROTO_HDLC:
|
|
(void) capiconn_accept(cp, 0, 1, 0, 0, 0, 0, 0);
|
|
break;
|
|
case PROTO_X75:
|
|
(void) capiconn_accept(cp, 0, 0, 0, 0, 0, 0, 0);
|
|
break;
|
|
case PROTO_V42BIS:
|
|
(void) capiconn_accept(cp, 0, 8, 0, 0, 0, 0, 0);
|
|
break;
|
|
case PROTO_MODEM:
|
|
(void) capiconn_accept(cp, 8, 1, 0, 0, 0, 0, 0);
|
|
break;
|
|
case PROTO_V110_ASYNC:
|
|
(void) capiconn_accept(cp, 2, 1, 0, 0, 0, 0, 0);
|
|
break;
|
|
case PROTO_V120_ASYNC:
|
|
(void) capiconn_accept(cp, 0, 9, 0, 0, 0, 0, 0);
|
|
break;
|
|
case PROTO_ANALOGMODEM:
|
|
(void) capiconn_accept(cp, 7, 7, 7, 0, 0, 0, 0);
|
|
break;
|
|
}
|
|
conn_remember(cp, CONNTYPE_INCOMING);
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- connection established handler ---------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void connected(capi_connection *cp, _cstruct NCPI)
|
|
{
|
|
capi_conninfo *p = capiconn_getinfo(cp);
|
|
char *callingnumber = "";
|
|
char *callednumber = "";
|
|
char buf[PATH_MAX];
|
|
char *tty;
|
|
int retry = 0;
|
|
int serrno;
|
|
|
|
info("capiplugin: connected: %s", conninfo(cp));
|
|
|
|
tty = capi20ext_get_tty_devname(p->appid, p->ncci, buf, sizeof(buf));
|
|
serrno = errno;
|
|
while (tty == 0 && serrno == ESRCH) {
|
|
if (++retry > 4)
|
|
break;
|
|
dbglog("capiplugin: capitty not ready, waiting for driver ...");
|
|
sleep(1);
|
|
tty = capi20ext_get_tty_devname(p->appid, p->ncci, buf, sizeof(buf));
|
|
serrno = errno;
|
|
}
|
|
if (tty == 0) {
|
|
if (serrno == EINVAL) {
|
|
fatal("capiplugin: failed to get tty devname - CAPI Middleware Support not enabled in kernel ?");
|
|
}
|
|
fatal("capiplugin: failed to get tty devname - %s (%d)",
|
|
strerror(serrno), serrno);
|
|
}
|
|
retry = 0;
|
|
while (access(tty, 0) != 0 && errno == ENOENT) {
|
|
if (++retry > 4) {
|
|
fatal("capiplugin: tty %s doesn't exist - CAPI Filesystem Support not enabled in kernel or not mounted ?", tty);
|
|
}
|
|
sleep(1);
|
|
}
|
|
info("capiplugin: using %s: %s", tty, conninfo(cp));
|
|
strcpy(devnam, tty);
|
|
|
|
if (p->callingnumber && p->callingnumber[0] > 2)
|
|
callingnumber = p->callingnumber+3;
|
|
if (p->callednumber && p->callednumber[0] > 1)
|
|
callednumber = p->callednumber+2;
|
|
_script_setenv("CALLEDNUMBER", callednumber);
|
|
_script_setenv("CALLINGNUMBER", callingnumber);
|
|
sprintf(buf, "%d", p->cipvalue); _script_setenv("CIPVALUE", buf);
|
|
sprintf(buf, "%d", p->b1proto); _script_setenv("B1PROTOCOL", buf);
|
|
sprintf(buf, "%d", p->b2proto); _script_setenv("B2PROTOCOL", buf);
|
|
sprintf(buf, "%d", p->b3proto); _script_setenv("B3PROTOCOL", buf);
|
|
|
|
conn_connected(cp);
|
|
if (curphase == PHASE_DORMANT)
|
|
wakeupdemand();
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- charge information ---------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
void chargeinfo(capi_connection *cp, unsigned long charge, int inunits)
|
|
{
|
|
if (inunits) {
|
|
info("capiplugin: %s: charge in units: %d", conninfo(cp), charge);
|
|
} else {
|
|
info("capiplugin: %s: charge in currency: %d", conninfo(cp), charge);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- tranfer capi message to CAPI ------------------------------ */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
int put_message(unsigned appid, unsigned char *msg)
|
|
{
|
|
unsigned err;
|
|
err = capi20_put_message (appid, msg);
|
|
if (err && !exiting)
|
|
fatal("capiplugin: putmessage(appid=%d) - %s 0x%x",
|
|
appid, capi_info2str(err), err);
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- capiconn module setup ------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
capiconn_callbacks callbacks = {
|
|
malloc: malloc,
|
|
free: free,
|
|
|
|
disconnected: disconnected,
|
|
incoming: incoming,
|
|
connected: connected,
|
|
received: 0,
|
|
datasent: 0,
|
|
chargeinfo: chargeinfo,
|
|
dtmf_received: 0,
|
|
|
|
capi_put_message: put_message,
|
|
|
|
debugmsg: (void (*)(const char *, ...))dbglog,
|
|
infomsg: (void (*)(const char *, ...))info,
|
|
errmsg: (void (*)(const char *, ...))error
|
|
};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- create a connection --------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static capi_connection *setupconnection(char *num, int awaitingreject)
|
|
{
|
|
struct capi_connection *cp;
|
|
char number[256];
|
|
|
|
snprintf(number, sizeof(number), "%s%s",
|
|
actcontroller->opt_numberprefix ? actcontroller->opt_numberprefix : "", num);
|
|
|
|
if (proto == PROTO_HDLC) {
|
|
cp = capiconn_connect(ctx,
|
|
actcontroller->controller, /* contr */
|
|
2, /* cipvalue */
|
|
opt_channels ? 0 : number,
|
|
opt_channels ? 0 : actcontroller->opt_msn,
|
|
0, 1, 0,
|
|
0, 0, 0,
|
|
opt_channels ? AdditionalInfo : 0,
|
|
0);
|
|
} else if (proto == PROTO_X75) {
|
|
cp = capiconn_connect(ctx,
|
|
actcontroller->controller, /* contr */
|
|
2, /* cipvalue */
|
|
opt_channels ? 0 : number,
|
|
opt_channels ? 0 : actcontroller->opt_msn,
|
|
0, 0, 0,
|
|
0, 0, 0,
|
|
opt_channels ? AdditionalInfo : 0,
|
|
0);
|
|
} else if (proto == PROTO_V42BIS) {
|
|
cp = capiconn_connect(ctx,
|
|
actcontroller->controller, /* contr */
|
|
2, /* cipvalue */
|
|
opt_channels ? 0 : number,
|
|
opt_channels ? 0 : actcontroller->opt_msn,
|
|
0, 8, 0,
|
|
0, 0, 0,
|
|
opt_channels ? AdditionalInfo : 0,
|
|
0);
|
|
} else if (proto == PROTO_MODEM) {
|
|
cp = capiconn_connect(ctx,
|
|
actcontroller->controller, /* contr */
|
|
4, /* cipvalue */
|
|
opt_channels ? 0 : number,
|
|
opt_channels ? 0 : actcontroller->opt_msn,
|
|
8, 1, 0,
|
|
0, 0, 0,
|
|
opt_channels ? AdditionalInfo : 0,
|
|
0);
|
|
} else if (proto == PROTO_V110_ASYNC) {
|
|
cp = capiconn_connect(ctx,
|
|
actcontroller->controller, /* contr */
|
|
2, /* cipvalue */
|
|
opt_channels ? 0 : number,
|
|
opt_channels ? 0 : actcontroller->opt_msn,
|
|
2, 1, 0,
|
|
0, 0, 0,
|
|
opt_channels ? AdditionalInfo : 0,
|
|
0);
|
|
} else if (proto == PROTO_V120_ASYNC) {
|
|
cp = capiconn_connect(ctx,
|
|
actcontroller->controller, /* contr */
|
|
2, /* cipvalue */
|
|
opt_channels ? 0 : number,
|
|
opt_channels ? 0 : actcontroller->opt_msn,
|
|
0, 9, 0,
|
|
0, 0, 0,
|
|
opt_channels ? AdditionalInfo : 0,
|
|
0);
|
|
} else if (proto == PROTO_ADSLPPPOE) {
|
|
cp = capiconn_connect(ctx,
|
|
actcontroller->controller, /* contr */
|
|
2, /* cipvalue */
|
|
0,
|
|
0,
|
|
28, 30, 30,
|
|
B1Config[0] ? B1Config : 0,
|
|
0, /* B2Config */
|
|
0, /* B3Config */
|
|
opt_channels ? AdditionalInfo : 0,
|
|
0);
|
|
} else if (proto == PROTO_ADSLPPPOA || proto == PROTO_ADSLPPPOALLC) {
|
|
cp = capiconn_connect(ctx,
|
|
actcontroller->controller, /* contr */
|
|
2, /* cipvalue */
|
|
0,
|
|
0,
|
|
28, 1, 0,
|
|
B1Config[0] ? B1Config : 0,
|
|
0, /* B2Config */
|
|
0, /* B3Config */
|
|
opt_channels ? AdditionalInfo : 0,
|
|
0);
|
|
} else if (proto == PROTO_ANALOGMODEM) {
|
|
cp = capiconn_connect(ctx,
|
|
actcontroller->controller, /* contr */
|
|
4, /* cipvalue */
|
|
opt_channels ? 0 : number,
|
|
opt_channels ? 0 : actcontroller->opt_msn,
|
|
7, 7, 7,
|
|
0, 0, 0,
|
|
opt_channels ? AdditionalInfo : 0,
|
|
0);
|
|
} else {
|
|
fatal("capiplugin: unknown protocol \"%s\"", opt_proto);
|
|
return 0;
|
|
}
|
|
if (opt_channels) {
|
|
info("capiplugin: leased line (%s)", opt_proto);
|
|
} else if (awaitingreject) {
|
|
info("capiplugin: dialing %s (awaiting reject)", number);
|
|
} else {
|
|
info("capiplugin: dialing %s (%s)", number, opt_proto);
|
|
}
|
|
if (awaitingreject)
|
|
conn_remember(cp, CONNTYPE_FOR_CALLBACK);
|
|
else
|
|
conn_remember(cp, CONNTYPE_OUTGOING);
|
|
return cp;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- connect leased line --------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void makeleasedline(void)
|
|
{
|
|
capi_connection *cp;
|
|
int retry = 0;
|
|
time_t t;
|
|
|
|
do {
|
|
if (retry) {
|
|
t = time(0)+opt_redialdelay;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
} while (time(0) < t);
|
|
}
|
|
|
|
cp = setupconnection("", 0);
|
|
|
|
t = time(0)+opt_dialtimeout;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK) {
|
|
if (conn_find(cp)) {
|
|
info("capiplugin: pppd status %d, disconnecting ...", status);
|
|
dodisconnect(cp);
|
|
} else {
|
|
die(status);
|
|
}
|
|
}
|
|
} while (time(0) < t && conn_inprogress(cp));
|
|
|
|
if (conn_isconnected(cp))
|
|
goto connected;
|
|
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
|
|
} while ((++retry < opt_dialmax) || (opt_dialmax == 0));
|
|
|
|
connected:
|
|
if (conn_isconnected(cp)) {
|
|
t = time(0)+opt_connectdelay;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
} while (time(0) < t);
|
|
}
|
|
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
|
|
if (!conn_isconnected(cp))
|
|
fatal("capiplugin: couldn't make connection");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- connect a dialup connection ------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void makeconnection(STRINGLIST *numbers)
|
|
{
|
|
capi_connection *cp = 0;
|
|
time_t t;
|
|
STRINGLIST *p;
|
|
int retry = 0;
|
|
|
|
do {
|
|
for (p = numbers; p; p = p->next) {
|
|
if (retry || p != numbers) {
|
|
again:
|
|
t = time(0)+opt_redialdelay;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
} while (time(0) < t);
|
|
}
|
|
|
|
cp = setupconnection(p->s, 0);
|
|
|
|
t = time(0)+opt_dialtimeout;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK) {
|
|
if (conn_find(cp)) {
|
|
info("capiplugin: pppd status %d, disconnecting ...", status);
|
|
dodisconnect(cp);
|
|
} else if (try_next_controller()) {
|
|
if (nextcontroller())
|
|
goto again;
|
|
die(status);
|
|
} else {
|
|
die(status);
|
|
}
|
|
}
|
|
} while (time(0) < t && conn_inprogress(cp));
|
|
|
|
if (conn_isconnected(cp))
|
|
goto connected;
|
|
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
}
|
|
} while ((++retry < opt_dialmax) || (opt_dialmax == 0));
|
|
|
|
connected:
|
|
if (conn_isconnected(cp)) {
|
|
t = time(0)+opt_connectdelay;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
} while (time(0) < t);
|
|
}
|
|
|
|
if (!conn_isconnected(cp))
|
|
fatal("capiplugin: couldn't make connection after %d retries",
|
|
retry);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- dial and wait for callback -------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void makeconnection_with_callback(void)
|
|
{
|
|
capi_connection *cp;
|
|
STRINGLIST *p;
|
|
time_t t;
|
|
int retry = 0;
|
|
int i;
|
|
|
|
do {
|
|
for (p = numbers; p; p = p->next) {
|
|
|
|
if (retry || p != numbers) {
|
|
again:
|
|
t = time(0)+opt_redialdelay;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
} while (time(0) < t);
|
|
}
|
|
|
|
cp = setupconnection(p->s, 1);
|
|
|
|
/* Wait specific time for the server rejecting the call */
|
|
t = time(0)+opt_dialtimeout;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
} while (time(0) < t && conn_inprogress(cp));
|
|
|
|
if (conn_isconnected(cp)) {
|
|
dodisconnect(cp);
|
|
fatal("capiplugin: callback failed - other party answers the call (no reject)");
|
|
} else if (try_next_controller()) {
|
|
if (nextcontroller())
|
|
goto again;
|
|
} else if (was_no_reject()) {
|
|
goto again;
|
|
} else {
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void) capiconn_listen(ctx, contrparams[i].controller, cipmask, 0);
|
|
}
|
|
info("capiplugin: waiting for callback...");
|
|
|
|
/* Wait for server calling back */
|
|
t = time(0)+opt_cbwait;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK) {
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void) capiconn_listen(ctx, contrparams[i].controller, 0, 0);
|
|
}
|
|
die(status);
|
|
}
|
|
} while (!conn_incoming_connected() && time(0) < t);
|
|
|
|
if (conn_incoming_connected()) {
|
|
add_fd(capi20_fileno(applid));
|
|
setup_timeout();
|
|
return;
|
|
}
|
|
info("capiplugin: callback failed (no call)");
|
|
}
|
|
}
|
|
} while ((++retry < opt_dialmax) || (opt_dialmax == 0));
|
|
|
|
fatal("capiplugin: callback failed (no call)");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- execute a callback ---------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void makecallback(void)
|
|
{
|
|
time_t t;
|
|
|
|
t = time(0)+opt_cbdelay;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
} while (time(0) < t);
|
|
|
|
if (callbacknumbers)
|
|
makeconnection(callbacknumbers);
|
|
else makeconnection(numbers);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- wait for an incoming call --------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static void waitforcall(void)
|
|
{
|
|
int try = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void) capiconn_listen(ctx, contrparams[i].controller, cipmask, 0);
|
|
}
|
|
info("capiplugin: waiting for incoming call ...");
|
|
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK) {
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void)capiconn_listen(ctx, contrparams[i].controller, 0, 0);
|
|
}
|
|
die(status);
|
|
}
|
|
if (conn_incoming_inprogress()) try=1;
|
|
if (try && !conn_incoming_inprogress()) {
|
|
try = 0;
|
|
if (!conn_incoming_connected()) {
|
|
for (i = 0; i < DIM(contrparams); i++) {
|
|
if (contrparams[i].controller == 0)
|
|
continue;
|
|
(void)capiconn_listen(ctx, contrparams[i].controller, cipmask, 0);
|
|
}
|
|
info("capiplugin: waiting for incoming call ...");
|
|
}
|
|
}
|
|
} while (!conn_incoming_connected());
|
|
|
|
if (conn_incoming_connected()) {
|
|
time_t t = time(0)+opt_connectdelay;
|
|
do {
|
|
handlemessages();
|
|
if (status != EXIT_OK)
|
|
die(status);
|
|
} while (time(0) < t);
|
|
}
|
|
add_fd(capi20_fileno(applid));
|
|
setup_timeout();
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- PPPD state change hook ------------------------------------ */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static char *phase2str(int phase)
|
|
{
|
|
static struct tmpbuf {
|
|
struct tmpbuf *next;
|
|
char buf[32];
|
|
} buffer[] = {
|
|
{ &buffer[1] },
|
|
{ &buffer[2] },
|
|
{ &buffer[0] },
|
|
};
|
|
static struct tmpbuf *p = &buffer[0];
|
|
|
|
switch (phase) {
|
|
case PHASE_DEAD: return "dead";
|
|
case PHASE_INITIALIZE: return "initialize";
|
|
case PHASE_SERIALCONN: return "serialconn";
|
|
case PHASE_DORMANT: return "dormant";
|
|
case PHASE_ESTABLISH: return "establish";
|
|
case PHASE_AUTHENTICATE: return "authenticate";
|
|
case PHASE_CALLBACK: return "callback";
|
|
case PHASE_NETWORK: return "network";
|
|
case PHASE_RUNNING: return "running";
|
|
case PHASE_TERMINATE: return "terminate";
|
|
case PHASE_DISCONNECT: return "disconnect";
|
|
case PHASE_HOLDOFF: return "holdoff";
|
|
}
|
|
p = p->next;
|
|
sprintf(p->buf,"unknown-%d", phase);
|
|
return p->buf;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int capi_new_phase_hook(int phase)
|
|
{
|
|
if (phase == curphase) {
|
|
info("capiplugin: phase %s, again.", phase2str(phase));
|
|
return 0;
|
|
}
|
|
if (curphase != -1) {
|
|
info("capiplugin: phase %s (was %s).",
|
|
phase2str(phase), phase2str(curphase));
|
|
} else {
|
|
info("capiplugin: phase %s.", phase2str(phase));
|
|
}
|
|
curphase = phase;
|
|
switch (phase) {
|
|
case PHASE_INITIALIZE:
|
|
case PHASE_ESTABLISH:
|
|
case PHASE_AUTHENTICATE:
|
|
case PHASE_CALLBACK:
|
|
case PHASE_NETWORK:
|
|
case PHASE_RUNNING:
|
|
case PHASE_TERMINATE:
|
|
case PHASE_DISCONNECT:
|
|
case PHASE_HOLDOFF:
|
|
break;
|
|
|
|
case PHASE_DEAD:
|
|
disconnectall();
|
|
break;
|
|
|
|
case PHASE_DORMANT:
|
|
status = EXIT_OK;
|
|
plugin_check_options();
|
|
init_capiconn();
|
|
if (contrparams[0].opt_inmsn || opt_cli) {
|
|
if (wakeupnow)
|
|
wakeupdemand();
|
|
setupincoming_for_demand();
|
|
}
|
|
break;
|
|
|
|
case PHASE_SERIALCONN:
|
|
status = EXIT_OK;
|
|
wakeupnow = 0;
|
|
if (conn_isconnected(0))
|
|
break;
|
|
plugin_check_options();
|
|
init_capiconn();
|
|
if (opt_number) {
|
|
if (coso == COSO_REMOTE) {
|
|
makeconnection_with_callback();
|
|
} else {
|
|
makeconnection(numbers);
|
|
}
|
|
} else if (opt_channels) {
|
|
makeleasedline();
|
|
} else {
|
|
waitforcall();
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* -------- init/exit function ---------------------------------------- */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
#if PPPVER >= PPPVersion(2,4,0,0)
|
|
|
|
static void plugin_exit(void)
|
|
{
|
|
int fd;
|
|
if ((fd = capi20_fileno(applid)) >= 0)
|
|
remove_fd(fd);
|
|
unsetup_timeout();
|
|
disconnectall();
|
|
info("capiplugin: exit");
|
|
}
|
|
|
|
static void phase_notify_func(void *p, int phase)
|
|
{
|
|
(void)capi_new_phase_hook(phase);
|
|
}
|
|
|
|
static void exit_notify_func(void *p, int phase)
|
|
{
|
|
exiting = 1;
|
|
plugin_exit();
|
|
}
|
|
#endif
|
|
|
|
void plugin_init(void)
|
|
{
|
|
int serrno;
|
|
int err;
|
|
|
|
info("capiplugin: %s", revision);
|
|
info("capiconn: %s", capiconn_version());
|
|
|
|
add_options(my_options);
|
|
|
|
if ((err = capi20_register (2, 8, 2048, &applid)) != 0) {
|
|
serrno = errno;
|
|
fatal("capiplugin: CAPI_REGISTER failed - %s (0x%04x) [%s (%d)]",
|
|
capi_info2str(err), err,
|
|
strerror(serrno), errno);
|
|
return;
|
|
}
|
|
if (capi20ext_set_flags(applid, 1) < 0) {
|
|
serrno = errno;
|
|
(void)capi20_release(applid);
|
|
fatal("capiplugin: failed to set highjacking mode - %s (%d)",
|
|
strerror(serrno), serrno);
|
|
return;
|
|
}
|
|
|
|
if ((ctx = capiconn_getcontext(applid, &callbacks)) == 0) {
|
|
(void)capi20_release(applid);
|
|
fatal("capiplugin: get_context failed");
|
|
return;
|
|
}
|
|
|
|
#if PPPVER >= PPPVersion(2,4,0,0)
|
|
add_notifier(&phasechange, phase_notify_func, 0);
|
|
add_notifier(&exitnotify, exit_notify_func, 0);
|
|
#else
|
|
new_phase_hook = capi_new_phase_hook;
|
|
#endif
|
|
}
|