- added support for cli callback.

- added support for incoming calls.
- added support for leased lines.
- added manual page.
This commit is contained in:
calle 2000-10-20 17:19:20 +00:00
parent a4f59461cf
commit 5374218260
4 changed files with 1126 additions and 70 deletions

View File

@ -4,7 +4,7 @@ LDFLAGS = -shared -L../capi20
PLUGINDIR=${DESTDIR}/etc/ppp/plugins
PEERDIR=${DESTDIR}/etc/ppp/peers/isdn
PEERS= arcor otelo talkline avm avm-ml
PEERS= arcor otelo talkline avm avm-ml leased
INSTALL=./install-sh -c
ALL = capiplugin.so userpass.so
@ -33,6 +33,12 @@ install: $(ALL)
echo $(INSTALL) peers/$$i $(PEERDIR); \
$(INSTALL) peers/$$i $(PEERDIR); \
done
for i in /usr/share/man /usr/man; do \
if [ -d $$i/man8 ] ; then \
echo $(INSTALL) capiplugin.8 $$i/man8; \
$(INSTALL) capiplugin.8 $$i/man8; \
fi ; \
done
config:
@echo nothing to configure

393
pppdcapiplugin/capiplugin.8 Normal file
View File

@ -0,0 +1,393 @@
.\" manual page [] for capiplugin 2.3
.\" $Id: capiplugin.8,v 1.1 2000/10/20 17:19:20 calle Exp $
.\" SH section heading
.\" SS subsection heading
.\" LP paragraph
.\" IP indented paragraph
.\" TP hanging label
.TH CAPIPLUGIN 8
.SH NAME
capiplugin \- Plugin for pppd (Point to Point Protocol daemon)
.SH SYNOPSIS
.B pppd
[\fIoptions\fR]
.B plugin
.B /etc/ppp/plugins/capiplugin.so
[\fIoptions for capiplugin\fR]
.SH DESCRIPTION
.LP
The capiplugin provides a method to use PPP over ISDN with
ISDN controllers that provide a CAPI2.0 interface.
The plugin is responsible for the call setup with CAPI2.0.
You can dial out, wait for incoming calls and setup communication over
leased lines. It implements the feature to reject a incoming call
and call back. When dialing out this feature can also be used.
In this case the called party has to reject the call and call back soon.
.SH HOW IS WORKS
.LP
The capiplugin registers a new_phase_hook and its own options to the pppd
when loaded. When the pppd goes into phase SERIALCONN the capiplugin
will setup a connection and sets the global variable devnam.
The capiplugin will register a timer function that is
called every second to handle the capi messages while pppd is running.
To let pppd wakeup when capi messages arrive, the capi file desciptor
is added to the list of file descriptors on which the pppd waits for input.
After the connection is established the pppd will start PPP negotiation
on device devnam. When the pppd goes into phase DEAD, the connection will
be dropped (if not already dropped) and the timer function and the capi file
desciptor will be unregistered.
.SH MODES OF OPERATION
.TP
.B normal dial out
Simply make a connection, for example to your internet provider.
.br
Required options: \fInumber\fR.
Recommended options: \fImsn\fR.
Other possible options:
\fIcontroller\fR,
\fIdialmax\fR,
\fIdialtimeout\fR,
\fIprotocol\fR and
\fIredialdelay\fR.
.TP
.B dial out with callback
Call a given number, the called party call back after rejecting the call
and then call back.
.br
Required options: \fInumber\fR and \fIclicb\fR.
Recommended options: \fIcli\fR and \fImsn\fR or \fIinmsn\fR.
Other possible options:
\fIcbwait\fR,
\fIcontroller\fR,
\fIconnectdelay\fR,
\fIdialtimeout\fR,
\fIprotocol\fR
.TP
.B wait for dial in
Wait for calls and accept incoming calls.
.br
Recommended options: \fIcli\fR and \fImsn\fR or \fIinmsn\fR.
Possible options:
\fIconnectdelay\fR,
\fIcontroller\fR,
\fIdialtimeout\fR,
\fIprotocol\fR
.TP
.B wait for dial in and call back.
Wait for calls, reject the call and then call back.
.br
Required options: \fIcbnumber\fR.
Recommended options: \fIcli\fR and \fImsn\fR or \fIinmsn\fR.
Other possible options:
\fIcbdelay\fR,
\fIconnectdelay\fR,
\fIcontroller\fR,
\fIdialtimeout\fR,
\fIprotocol\fR
.TP
.B leased line
setup a leased line connection, with or without CAPI channel bundling
.br
Required options: \fIchannels\fR.
Other possible options:
\fIconnectdelay\fR,
\fIcontroller\fR,
\fIdialtimeout\fR and
\fIprotocol\fR
.SH OPTIONS
.TP
.B cbdelay \fI<seconds>
Number of seconds to wait before callback, when acting as
dialin server with callback. Default is 2 seconds.
.TP
.B cbnumber \fI<phone numbers>
Comma seperated list of phone numbers for call back, when acting as
dialin server with callback.
.TP
.B cbwait \fI<seconds>
Time to wait for a call back, before giving up. Default is 60 seconds.
.TP
.B channels \fI<channel specification>
Comma seperated list of b-channels or ranges to activate leased line mode.
.TP
.B cli \fI<telephon numbers>
A comma seperated list of numbers from where incoming calls will be accepted.
.TP
.B clicb
Enable callback mode. When option \fInumber\fR is present, call number
and wait for callback. When option \fInumber\fR is not present,
wait for incoming call, reject the call and call back.
This option is is optional if option \fIcbnumber\fR is present.
.TP
.B connectdelay \fI<seconds>
Number of seconds to wait after connecting is established,
before PPP negotiation starts. Defaultvalue is 0 seconds.
This option is use full, when connecting with protocol \fImodem\fR.
Some internet access servers will hang up, if they get data immediatly
after connection is established.
.TP
.B controller \fI<controller specification>
For point to multipoint \fI<controller specification\fR is only the
CAPI2.0 controller number , default is 1.
For point-to-point specify \fI<controller number>\fR,\fI<ddi>\fR,\fI<length of internal numbers\>
.TP
.B dialmax \fI<times>
Maximum number of times the list of phone numbers is tried before give up.
Default is 4.
.TP
.B dialtimeout \fI<seconds>
Time to wait until connection established or failed before giving up.
Default is 60 seconds.
.TP
.B inmsn \fI<msn>
Phone umber to listen on for calls, when different from option \fImsn\fR.
.TP
.B msn \fI<msn>
Phone umber from where to call out. Also used for incoming calls
if option \fIinmsn\fR is not present.
.TP
.B number \fI<phone numbers>
Comma seperated list of phone numbers to call.
Every number in the list is called until a connection can be established.
When the end of the list is reached, the first number is called again.
See option \fIdialmax\fR.
.TP
.B numberprefix \fI<prefix>
phone number to dial to get outline access. For example. \fInumberprefix 0\fR.
.TP
.B protocol \fIhdlc\fR | \fIx75\fR | \fIv42bis\fR | \fImodem\fR
ISDN protocol to use. With \fIhdlc\fR you need to add option \fIsync\fR
to the pppd. With \fIx75\fR,\fIv42bis\fR and \fImodem\fR option \fIsync\fR
MUST NOT be present. Defaultvalue is \fIhdlc\fR.
Not all controllers support \fIv42bis\fR and \fImodem\fR.
Use capiinfo(8) to see the features your controller support.
.TP
.B redialdelay \fI<seconds>
Number of seconds to wait between redialing. Default is 5 seconds.
.SH EXAMPLE FOR NORMAL DIAL OUT
.LP
Probably the most common use of pppd is to dial out to an ISP. This
can be done with a command such as
.IP
pppd call isp
.LP
where the /etc/ppp/peers/isp file is set up by the system
administrator to contain something like this:
.IP
sync
.br
noauth
.br
defaultroute
.br
name USERNAME
.br
plugin /etc/ppp/plugins/capiplugin.so
.r
msn MSN
.br
number PHONENUMBER
.br
protocol hdlc
.br
ipcp-accept-local
.br
ipcp-accept-remote
.br
/dev/null
.LP
where the /etc/ppp/pap-secrets and /etc/ppp/chap-secrets file is set up by
the system administrator to contain something like this:
.IP
USERNAME * PASSWORD *
.SH EXAMPLE FOR DIAL OUT WITH CALLBACK
.LP
Dial out with callback can be done with a command such as
.IP
pppd call isp-callback
.LP
where the /etc/ppp/peers/isp-callback file is set up by the system
administrator to contain something like this:
.IP
sync
.br
noauth
.br
defaultroute
.br
name USERNAME
.br
plugin /etc/ppp/plugins/capiplugin.so
.br
msn MSN
.br
number PHONENUMBER
.br
clicb
.br
cli PHONENUMBER
.br
protocol hdlc
.br
ipcp-accept-local
.br
ipcp-accept-remote
.br
/dev/null
.LP
where the /etc/ppp/pap-secrets and /etc/ppp/chap-secrets file is set up by
the system administrator to contain something like this:
.br
USERNAME * PASSWORD *
.SH EXAMPLE FOR WAIT FOR DIAL IN WITHOUT CLI AUTHENTICATION
.LP
Wait for incoming calls, accept them according to options \fImsn\fR,
\fIinmsn\fR, and \fIprotocol\fI.
.LP
Do not provide option \fIcli\fR to the capiplugin.
Start a pppd for every b channel.
Authorisation is made with PAP or CHAP and the ip numbers are assigned
according to file /etc/ppp/pap-secrets or file /etc/ppp/chap-secrets.
Let's assume the server has ip number 192.168.0.1 and the clients should
have the ip numbers starting at 192.168.0.2 and the hostname of the
server is \"dialinserver\".
Add this two lines to the file /etc/inittab:
.IP
p0:23:respawn:/usr/sbin/pppd call incoming
.br
p1:23:respawn:/usr/sbin/pppd call incoming
.LP
where the /etc/ppp/peers/incoming file is set up
to contain something like this:
.IP
sync
.br
auth
.br
persist
.br
plugin /etc/ppp/plugins/capiplugin.so
.br
inmsn MSN
.br
protocol hdlc
192.168.0.1:
.LP
with the file /etc/ppp/pap-secrets and file /etc/ppp/chap-secrets file is set up
to contain something like this:
.IP
user1 dialinserver PASSWORD1 192.168.0.2
.br
user2 dialinserver PASSWORD2 192.168.0.2
.SH EXAMPLE FOR WAIT FOR DIAL IN WITH CLI AUTHENTICATION
.LP
Wait for incoming calls, accept them according to options \fImsn\fR,
\fIinmsn\fR, \fIcli\fR and \fIprotocol\fI.
.LP
Start a pppd for every client.
Let's assume the server has ip number 192.168.0.1 and the clients should
have the ip numbers starting at 192.168.0.2.
Add this three lines to file /etc/inittab:
.IP
p0:23:respawn:/usr/sbin/pppd call incoming cli 04711 192.168.0.1:192.168.0.2
.br
p1:23:respawn:/usr/sbin/pppd call incoming cli 04712 192.168.0.1:192.168.0.3
.br
p2:23:respawn:/usr/sbin/pppd call incoming cli 04713 192.168.0.1:192.168.0.4
.LP
where the /etc/ppp/peers/incoming file is set up
to contain something like this:
.IP
sync
.br
auth
.br
persist
.br
plugin /etc/ppp/plugins/capiplugin.so
.br
inmsn MSN
.br
protocol hdlc
.SH EXAMPLE FOR WAIT FOR DIAL IN WITH CLI AUTHENTICATION AND CALLBACK
.LP
Wait for incoming calls, accept them according to options \fImsn\fR,
\fIinmsn\fR, \fIcli\fR and \fIprotocol\fI, reject incoming calls
and call back.
.LP
.B start a pppd for every client
Let assume the server has ip numbers 192.168.0.1 and the clients should
have the ip numbers starting at 192.168.0.2.
Add this three lines to the file /etc/inittab.
.IP
p0:23:respawn:/usr/sbin/pppd call incoming cli 04711 cbnumber 4711 192.168.0.1:192.168.0.2
.br
p1:23:respawn:/usr/sbin/pppd call incoming cli 04712 cbnumber 4712 192.168.0.1:192.168.0.3
.br
p2:23:respawn:/usr/sbin/pppd call incoming cli 04713 cbnumber 4713 192.168.0.1:192.168.0.4
.LP
where the /etc/ppp/peers/incoming file is set up
to contain something like this:
.IP
sync
.br
auth
.br
persist
.br
plugin /etc/ppp/plugins/capiplugin.so
.br
inmsn MSN
.br
protocol hdlc
.SH CAVEATS
.LP
Every pppd waiting for incoming calls can get an incoming call first.
So when you start two pppd listening on the same MSN, one with
CLI Authentication and the other not, the following can happen:
.IP
The Client with the CLI specified to the first pppd calls, but the pppd
without option \fIcli\fR will get the call first and accepts the call.
.LP
So if you want to mix CLI Authentication and PAP/CHAP Authentication
use one MSN for CLI authenticated calls and another for the PAP/CHAP
authenticated calls.
.SH DIAGNOSTICS
.LP
Messages are sent to the syslog daemon like pppd did usually, see
pppd manual page.
.SH SEE ALSO
pppd(8), capiinfo(8), capiinit(8), capictrl(8)
.SH AUTHORS
Carsten Paeth (calle@calle.in-berlin.de)

View File

@ -8,35 +8,78 @@
* 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 "pppd.h"
#include "capiconn.h"
#include <malloc.h>
#include <string.h>
#include <dlfcn.h>
static char *revision = "$Revision: 1.5 $";
static char *revision = "$Revision: 1.6 $";
static capiconn_context *ctx;
static capi_connection *conn = 0;
static int isconnected = 0;
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 int controller = 1;
static capi_contrinfo cinfo = { 0 , 0, 0 };
static int opt_contr = 1;
static char *opt_controller = "1";
static char *opt_numberprefix = 0;
static char *opt_number = 0;
static char *opt_callbacknumber = 0;
static char *opt_msn = 0;
static char *opt_inmsn = 0;
static char *opt_proto = 0;
static char *opt_channels = 0;
static unsigned char AdditionalInfo[1+2+2+31];
static int opt_dialtimeout = 60;
static int opt_dialmax = 4;
static int opt_redialdelay = 5;
static int opt_cbdelay = 2;
static int opt_connectdelay = 0;
static char *opt_cli = 0;
static int opt_cbflag = 0;
static int opt_cbwait = 60;
static int opt_acceptdelayflag = 0;
typedef struct stringlist {
struct stringlist *next;
char *s;
} STRINGLIST;
static STRINGLIST *numbers;
static STRINGLIST *callbacknumbers;
static STRINGLIST *clis;
static STRINGLIST *parsed_controller;
static int optcb(void)
{
return opt_cbflag = 1;
}
static int optacceptdelay(void)
{
return opt_acceptdelayflag = 1;
}
static option_t my_options[] = {
{
"controller", o_int, &opt_contr,
"capi controller"
"controller", o_string, &opt_controller,
"capi controller specification"
},
{
"number", o_string, &opt_number,
"number to call"
"number to call (may be comma separated)"
},
{
"numberprefix", o_string, &opt_numberprefix,
@ -48,13 +91,112 @@ static option_t my_options[] = {
},
{
"protocol", o_string, &opt_proto,
"protocol x75 or hdlc"
"protocol x75, hdlc or modem"
},
{
"inmsn", o_string, &opt_inmsn,
"called number for incoming calls"
},
{
"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"
},
{ NULL }
};
/* -------------------------------------------------------------------- */
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;
}
/* -------------------------------------------------------------------- */
static int timeoutrunning = 0;
static int timeoutshouldrun = 0;
@ -85,112 +227,474 @@ static void unsetup_timeout(void)
/* -------------------------------------------------------------------- */
static void dodisconnect(void)
static void handlemessages(void)
{
if (!conn)
return;
(void)capiconn_disconnect(conn, 0);
while (conn) {
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);
}
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 makeconnection(void)
/* -------------------------------------------------------------------- */
static void dodisconnect(void)
{
time_t t;
if (!conn)
return;
(void)capiconn_disconnect(conn, 0);
t = time(0)+10;
while (conn && time(0) < t)
handlemessages();
if (conn)
fatal("capiplugin: timeout while waiting for disconnect");
}
static void setupconnection(char *num)
{
char number[256];
if (opt_number == 0) {
fatal("capiplugin: no number");
return;
}
snprintf(number, sizeof(number), "%s%s",
opt_numberprefix ? opt_numberprefix : "",
opt_number);
opt_numberprefix ? opt_numberprefix : "", num);
if (opt_proto == 0 || strcasecmp(opt_proto, "hdlc") == 0) {
conn = capiconn_connect(ctx,
opt_contr, /* contr */
controller, /* contr */
2, /* cipvalue */
number,
opt_msn,
opt_channels ? 0 : number,
opt_channels ? 0 : opt_msn,
0, 1, 0,
0, 0, 0, 0, 0);
0, 0, 0,
opt_channels ? AdditionalInfo : 0,
0);
} else if (strcasecmp(opt_proto, "x75") == 0) {
conn = capiconn_connect(ctx,
opt_contr, /* contr */
controller, /* contr */
2, /* cipvalue */
number,
opt_msn,
opt_channels ? 0 : number,
opt_channels ? 0 : opt_msn,
0, 0, 0,
0, 0, 0, 0, 0);
0, 0, 0,
opt_channels ? AdditionalInfo : 0,
0);
} else if (strcasecmp(opt_proto, "v42bis") == 0) {
conn = capiconn_connect(ctx,
controller, /* contr */
2, /* cipvalue */
opt_channels ? 0 : number,
opt_channels ? 0 : opt_msn,
0, 8, 0,
0, 0, 0,
opt_channels ? AdditionalInfo : 0,
0);
} else if (strcasecmp(opt_proto, "modem") == 0) {
conn = capiconn_connect(ctx,
opt_contr, /* contr */
controller, /* contr */
1, /* cipvalue */
number,
opt_msn,
opt_channels ? 0 : number,
opt_channels ? 0 : opt_msn,
8, 1, 0,
0, 0, 0, 0, 0);
0, 0, 0,
opt_channels ? AdditionalInfo : 0,
0);
} else {
fatal("capiplugin: unknown protocol \"%s\"", opt_proto);
return;
}
while (!isconnected && conn) {
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);
if (opt_channels) {
info("capiplugin: leased line (%s)",
opt_proto ? opt_proto : "hdlc");
} else {
info("capiplugin: dial %s (%s)",
number, opt_proto ? opt_proto : "hdlc");
}
}
static void makeleasedline(void)
{
time_t t;
setupconnection("");
t = time(0)+opt_dialtimeout;
do {
handlemessages();
if (status != EXIT_OK && conn)
dodisconnect();
} while (time(0) < t && conn && !isconnected);
if (status != EXIT_OK)
die(status);
if (conn && isconnected) {
t = time(0)+opt_connectdelay;
do {
handlemessages();
} while (time(0) < t);
}
if (status != EXIT_OK)
die(status);
if (!conn)
fatal("capiplugin: couldn't make connection");
}
static void makeconnection(STRINGLIST *numbers)
{
time_t t;
STRINGLIST *p;
int retry = 0;
do {
for (p = numbers; p; p = p->next) {
if (retry || p != numbers) {
t = time(0)+opt_redialdelay;
do {
handlemessages();
if (status != EXIT_OK)
die(status);
} while (time(0) < t);
}
setupconnection(p->s);
t = time(0)+opt_dialtimeout;
do {
handlemessages();
if (status != EXIT_OK && conn)
dodisconnect();
} while (time(0) < t && conn && !isconnected);
if (conn && isconnected)
goto connected;
if (status != EXIT_OK)
die(status);
}
} while (++retry < opt_dialmax);
connected:
if (!conn)
fatal("capiplugin: couldn't make connection after %d retries",
retry);
}
static void makeconnection_with_callback(void)
{
STRINGLIST *p;
time_t t;
for (p = numbers; p; p = p->next) {
setupconnection(p->s);
info("capiplugin: wait for call reject");
/* 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 && !isconnected);
if (!conn) { /* Call has been rejected */
(void) capiconn_listen(ctx, controller, cipmask, 0);
info("capiplugin: waiting for callback...");
/* Wait for server calling back */
t = time(0)+opt_cbwait;
do {
handlemessages();
if (status != EXIT_OK) {
(void) capiconn_listen(ctx, controller, 0, 0);
die(status);
}
} while (!isconnected && time(0) < t);
if (isconnected) {
add_fd(capi20_fileno(applid));
setup_timeout();
return;
}
if (p->next == 0)
fatal("capiplugin: callback failed (no call)");
else
info("capiplugin: callback failed (no call)");
} else {
dodisconnect();
fatal("capiplugin: callback failed (no reject)");
}
}
if (!conn)
fatal("capiplugin: couldn't make connection");
}
static void makecallback(void)
{
time_t t;
t = time(0)+opt_cbdelay;
do {
handlemessages();
if (status != EXIT_OK)
die(status);
} while (time(0) < t);
makeconnection(callbacknumbers);
}
static void waitforcall(void)
{
(void) capiconn_listen(ctx, controller, cipmask, 0);
info("capiplugin: waiting for incoming call ...");
do {
handlemessages();
if (status != EXIT_OK) {
(void) capiconn_listen(ctx, controller, 0, 0);
die(status);
}
} while (!isconnected);
add_fd(capi20_fileno(applid));
setup_timeout();
}
static void init_capiconn(void)
{
static capi_contrinfo cinfo = { 0 , 0, 0 };
static int init = 0;
if (init)
return;
init = 1;
if (capiconn_addcontr(ctx, opt_contr, &cinfo) != CAPICONN_OK) {
if (capiconn_addcontr(ctx, controller, &cinfo) != CAPICONN_OK) {
(void)capiconn_freecontext(ctx);
(void)capi20_release(applid);
fatal("capiplugin: add controller %d failed", opt_contr);
fatal("capiplugin: add controller %d failed", controller);
return;
}
if (cinfo.ddi)
dbglog("capiplugin: contr=%d ddi=\"%s\" n=%d",
controller, cinfo.ddi, cinfo.ndigits);
else
dbglog("capiplugin: contr=%d", controller);
add_fd(capi20_fileno(applid));
setup_timeout();
}
/*
* 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 check_options(void)
{
if ( opt_proto
&& strcasecmp(opt_proto, "hdlc")
&& strcasecmp(opt_proto, "x75")
&& strcasecmp(opt_proto, "v42bis")
&& strcasecmp(opt_proto, "modem")) {
option_error("capiplugin: unknown protocol \"%s\"", opt_proto);
die(1);
}
if (strcasecmp(opt_proto, "modem") == 0)
cipmask = CIPMASK_VOICE;
else cipmask = CIPMASK_DATA;
if (opt_channels) {
channels2capi20(opt_channels, AdditionalInfo);
if (opt_number)
option_error("capiplugin: option number ignored");
if (opt_numberprefix)
option_error("capiplugin: option numberprefix ignored");
if (opt_callbacknumber)
option_error("capiplugin: option callbacknumber ignored");
if (opt_msn)
option_error("capiplugin: option msn ignored");
if (opt_inmsn)
option_error("capiplugin: option inmsn ignored");
} else if (opt_number) {
stringlist_free(&numbers);
numbers = stringlist_split(opt_number, " \t,");
if (opt_callbacknumber)
option_error("capiplugin: option callbacknumber ignored");
} else if (opt_cbflag) {
if (opt_callbacknumber == 0) {
option_error("capiplugin: option callbacknumber missing");
die(1);
}
stringlist_free(&callbacknumbers);
callbacknumbers = stringlist_split(opt_callbacknumber, " \t,");
} else {
if (opt_callbacknumber) {
opt_cbflag = 1;
stringlist_free(&callbacknumbers);
callbacknumbers = stringlist_split(opt_callbacknumber, " \t,");
}
}
if (opt_cli) {
stringlist_free(&clis);
clis = stringlist_split(opt_cli, " \t,");
}
if (opt_controller) {
STRINGLIST *sl;
char *tmp;
stringlist_free(&parsed_controller);
memset(&cinfo, 0, sizeof(cinfo));
parsed_controller = stringlist_split(opt_controller, " \t,");
sl = parsed_controller;
if (!sl) goto illcontr;
tmp = sl->s;
controller = strtol(sl->s, &tmp, 10);
if (tmp == sl->s || *tmp) goto illcontr;
if (sl->next) {
sl = sl->next;
cinfo.ddi = sl->s;
if (sl->next && sl->next->s) {
sl = sl->next;
cinfo.ndigits = strtol(sl->s, &tmp, 10);
if (tmp == sl->s || *tmp) goto illcontr;
}
}
} else {
memset(&cinfo, 0, sizeof(cinfo));
controller = 1;
}
return;
illcontr:
option_error("capiplugin: illegal controller specification \"%s\"",
opt_controller);
die(1);
}
static int capi_new_phase_hook(int phase)
{
int fd;
switch (phase) {
case PHASE_DEAD:
info("capiplugin: phase dead");
if ((fd = capi20_fileno(applid)) >= 0)
remove_fd(fd);
unsetup_timeout();
dodisconnect();
break;
case PHASE_INITIALIZE:
info("capiplugin: phase initialize");
break;
case PHASE_SERIALCONN:
info("capiplugin: phase serialconn");
info("capiplugin: phase serialconn%s",
opt_cbflag ? " (callback)" : "");
check_options();
init_capiconn();
makeconnection();
if (opt_number) {
if (opt_cbflag) {
makeconnection_with_callback();
} else {
makeconnection(numbers);
}
} else if (opt_channels) {
makeleasedline();
} else {
waitforcall();
}
break;
case PHASE_DORMANT:
info("capiplugin: phase dormant");
@ -212,9 +716,6 @@ static int capi_new_phase_hook(int phase)
break;
case PHASE_TERMINATE:
info("capiplugin: phase terminate");
if ((fd = capi20_fileno(applid)) >= 0)
remove_fd(fd);
unsetup_timeout();
break;
case PHASE_DISCONNECT:
info("capiplugin: phase disconnect");
@ -232,13 +733,28 @@ static char *conninfo(capi_connection *p)
{
static char buf[1024];
capi_conninfo *cp = capiconn_getinfo(p);
char *callingnumber = "";
char *callednumber = "";
snprintf(buf, sizeof(buf),
"plci=0x%x ncci=0x%x %s",
cp->plci,
cp->ncci,
cp->isincoming ? "incoming" : "outgoing"
);
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;
}
@ -247,14 +763,136 @@ static void disconnected(capi_connection *cp,
unsigned reason,
unsigned reason_b3)
{
info("capiplugin: disconnected(%s): %s: 0x%04x (0x%04x) - %s",
conninfo(cp),
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));
conn = 0;
isconnected = 0;
}
static void incoming(capi_connection *cp,
unsigned contr,
unsigned cipvalue,
char *callednumber,
char *callingnumber)
{
STRINGLIST *p;
char *s;
info("capiplugin: incoming call: %s (0x%x)", conninfo(cp), cipvalue);
if (opt_inmsn) {
if ( (s = strstr(callednumber, opt_inmsn)) == 0
|| strcmp(s, opt_inmsn) != 0) {
info("capiplugin: ignoring call, msn mismatch (%s != %s)",
opt_inmsn, callednumber);
(void) capiconn_ignore(cp);
return;
}
} else if (opt_msn) {
if ( (s = strstr(callednumber, opt_msn)) == 0
|| strcmp(s, opt_msn) != 0) {
info("capiplugin: ignoring call, msn mismatch (%s != %s)",
opt_msn, callednumber);
(void) capiconn_ignore(cp);
return;
}
}
if (opt_cli) {
for (p = clis; p; p = p->next) {
if (strcmp(p->s, callingnumber) == 0)
break;
}
if (!p) {
info("capiplugin: ignoring call, cli mismatch (%s != %s)",
opt_cli, callingnumber);
(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);
(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 (opt_proto && strcasecmp(opt_proto, "modem") == 0) {
if (opt_cbflag) goto callback;
(void) capiconn_accept(cp, 8, 1, 0, 0, 0, 0, 0);
goto accepted;
} else {
info("capiplugin: ignoring speech call from %s",
callingnumber);
(void) capiconn_ignore(cp);
}
break;
case 2: /* Unrestricted digital information */
case 3: /* Restricted digital information */
if (opt_proto == 0
|| strcasecmp(opt_proto, "hdlc") == 0) {
if (opt_cbflag) goto callback;
(void) capiconn_accept(cp, 0, 1, 0, 0, 0, 0, 0);
goto accepted;
} else if (strcasecmp(opt_proto, "x75") == 0) {
if (opt_cbflag) goto callback;
(void) capiconn_accept(cp, 0, 0, 0, 0, 0, 0, 0);
goto accepted;
} else if (strcasecmp(opt_proto, "v42bis") == 0) {
if (opt_cbflag) goto callback;
(void) capiconn_accept(cp, 0, 8, 0, 0, 0, 0, 0);
goto accepted;
} else {
info("capiplugin: ignoring digital call from %s",
callingnumber);
(void) capiconn_ignore(cp);
}
break;
case 17: /* Group 2/3 facsimile */
info("capiplugin: ignoring fax call from %s",
callingnumber);
(void) capiconn_ignore(cp);
break;
default:
info("capiplugin: ignoring type %d call from %s",
cipvalue, callingnumber);
(void) capiconn_ignore(cp);
break;
}
return;
accepted:
(void) capiconn_listen(ctx, controller, 0, 0);
return;
callback:
(void) capiconn_listen(ctx, controller, 0, 0);
info("capiplugin: rejecting call: %s (0x%x)", conninfo(cp), cipvalue);
capiconn_reject(cp);
makecallback();
return;
}
static void connected(capi_connection *cp, _cstruct NCPI)
{
capi_conninfo *p = capiconn_getinfo(cp);
@ -262,8 +900,10 @@ static void connected(capi_connection *cp, _cstruct NCPI)
char *tty;
tty = capi20ext_get_tty_devname(p->appid, p->ncci, buf, sizeof(buf));
info("capiplugin: connected(%s) %s", conninfo(cp), tty);
info("capiplugin: connected(%s): %s", conninfo(cp), tty);
strcpy(devnam, tty);
if (opt_connectdelay)
sleep(opt_connectdelay);
isconnected = 1;
}
@ -272,7 +912,7 @@ void put_message(unsigned appid, unsigned char *msg)
unsigned err;
err = capi20_put_message (appid, msg);
if (err)
fatal("capiplugin: putmessage(appid=%u) = 0x%x", appid, err);
fatal("capiplugin: putmessage(appid=%d) = 0x%x", appid, err);
}
/* -------------------------------------------------------------------- */
@ -282,7 +922,7 @@ capiconn_callbacks callbacks = {
free: free,
disconnected: disconnected,
incoming: 0,
incoming: incoming,
connected: connected,
received: 0,
datasent: 0,
@ -299,7 +939,7 @@ void plugin_init(void)
{
int err;
info("plugin_init: capiconnect (%s)", revision);
info("capiplugin: %s", revision);
add_options(my_options);

View File

@ -0,0 +1,17 @@
debug
sync
noauth
defaultroute
lcp-echo-interval 5
lcp-echo-failure 3
lcp-max-configure 50
lcp-max-terminate 2
noccp
noipx
persist
plugin /etc/ppp/plugins/capiplugin.so
channels 1
protocol hdlc
ipcp-accept-local
ipcp-accept-remote
: