From 537421826044b2ac42babe2e21bbb09c6cc65921 Mon Sep 17 00:00:00 2001 From: calle Date: Fri, 20 Oct 2000 17:19:20 +0000 Subject: [PATCH] - added support for cli callback. - added support for incoming calls. - added support for leased lines. - added manual page. --- pppdcapiplugin/Makefile | 8 +- pppdcapiplugin/capiplugin.8 | 393 ++++++++++++++++++ pppdcapiplugin/capiplugin.c | 778 ++++++++++++++++++++++++++++++++---- pppdcapiplugin/peers/leased | 17 + 4 files changed, 1126 insertions(+), 70 deletions(-) create mode 100644 pppdcapiplugin/capiplugin.8 create mode 100644 pppdcapiplugin/peers/leased diff --git a/pppdcapiplugin/Makefile b/pppdcapiplugin/Makefile index 5d77fc6d..8dd58b8a 100644 --- a/pppdcapiplugin/Makefile +++ b/pppdcapiplugin/Makefile @@ -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 diff --git a/pppdcapiplugin/capiplugin.8 b/pppdcapiplugin/capiplugin.8 new file mode 100644 index 00000000..ff6465ab --- /dev/null +++ b/pppdcapiplugin/capiplugin.8 @@ -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 +Number of seconds to wait before callback, when acting as +dialin server with callback. Default is 2 seconds. + +.TP +.B cbnumber \fI +Comma seperated list of phone numbers for call back, when acting as +dialin server with callback. + +.TP +.B cbwait \fI +Time to wait for a call back, before giving up. Default is 60 seconds. + +.TP +.B channels \fI +Comma seperated list of b-channels or ranges to activate leased line mode. + +.TP +.B cli \fI +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 +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 +For point to multipoint \fI\fR,\fI\fR,\fI + +.TP +.B dialmax \fI +Maximum number of times the list of phone numbers is tried before give up. +Default is 4. + +.TP +.B dialtimeout \fI +Time to wait until connection established or failed before giving up. +Default is 60 seconds. + +.TP +.B inmsn \fI +Phone umber to listen on for calls, when different from option \fImsn\fR. + +.TP +.B msn \fI +Phone umber from where to call out. Also used for incoming calls +if option \fIinmsn\fR is not present. + +.TP +.B number \fI +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 +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 +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) diff --git a/pppdcapiplugin/capiplugin.c b/pppdcapiplugin/capiplugin.c index 2686a58a..7d2d1dc4 100644 --- a/pppdcapiplugin/capiplugin.c +++ b/pppdcapiplugin/capiplugin.c @@ -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 #include +#include #include +#include #include "pppd.h" #include "capiconn.h" #include #include #include -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); diff --git a/pppdcapiplugin/peers/leased b/pppdcapiplugin/peers/leased new file mode 100644 index 00000000..0aeea744 --- /dev/null +++ b/pppdcapiplugin/peers/leased @@ -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 +: