Sisyphus repository
Last update: 1 october 2023 | SRPMs: 18631 | Visits: 37559574
en ru br
ALT Linux repos
S:2.5.0-alt1
5.0: 2.4.4-alt11
4.1: 2.4.4-alt10.M41.1
4.0: 2.4.4-alt10
+updates:2.4.4-alt10
3.0: 2.4.2-alt6
+updates:2.4.2-alt6.M30.1

Group :: Networking/Other
RPM: ppp

 Main   Changelog   Spec   Patches   Sources   Download   Gear   Bugs and FR  Repocop 

Patch: ppp-2.4.5-cbcp.patch
Download


 pppd/Makefile.linux |    2 +-
 pppd/auth.c         |    9 +-
 pppd/cbcp.c         |  504 ++++++++++++++++++++++++++++++++++++++++++++++-----
 pppd/cbcp.h         |   14 ++
 pppd/ipcp.c         |    9 +
 pppd/ipv6cp.c       |   10 +
 pppd/ipxcp.c        |   10 +
 pppd/lcp.c          |   10 +
 pppd/main.c         |   94 ++++++++++
 pppd/options.c      |   36 ++++
 pppd/pathnames.h    |    7 +
 pppd/pppd.8         |    8 +
 pppd/pppd.h         |   12 ++
 pppd/sys-linux.c    |    2 +-
 pppd/tty.c          |  188 ++++++++++++--------
 15 files changed, 790 insertions(+), 125 deletions(-)
diff --git a/pppd/Makefile.linux b/pppd/Makefile.linux
index 060db6a..59de73b 100644
--- a/pppd/Makefile.linux
+++ b/pppd/Makefile.linux
@@ -17,7 +17,7 @@ PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap-new.c md5.c ccp.c \
 
 HEADERS = ccp.h session.h chap-new.h ecp.h fsm.h ipcp.h \
 	ipxcp.h lcp.h magic.h md5.h patchlevel.h pathnames.h pppd.h \
-	upap.h eap.h
+	upap.h eap.h cbcp.h
 
 MANPAGES = pppd.8
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o md5.o ccp.o \
diff --git a/pppd/auth.c b/pppd/auth.c
index fb71944..7a7a70b 100644
--- a/pppd/auth.c
+++ b/pppd/auth.c
@@ -245,7 +245,7 @@ extern char *crypt __P((const char *, const char *));
 
 /* Prototypes for procedures local to this file. */
 
-static void network_phase __P((int));
+void network_phase __P((int));
 static void check_idle __P((void *));
 static void connect_time_expired __P((void *));
 static int  null_login __P((int));
@@ -591,8 +591,10 @@ void start_link(unit)
 	notice("Starting negotiation on %s", ppp_devnam);
     add_fd(fd_ppp);
 
+#ifndef CBCP_SUPPORT
     status = EXIT_NEGOTIATION_FAILED;
     new_phase(PHASE_ESTABLISH);
+#endif
 
     lcp_lowerup(0);
     return;
@@ -804,7 +806,7 @@ link_established(unit)
 /*
  * Proceed to the network phase.
  */
-static void
+void
 network_phase(unit)
     int unit;
 {
@@ -830,7 +832,8 @@ network_phase(unit)
     /*
      * If we negotiated callback, do it now.
      */
-    if (go->neg_cbcp) {
+    if (((go->neg_cbcp) || (lcp_hisoptions[unit].neg_cbcp))
+	&& (phase != PHASE_CALLBACK)) {
 	new_phase(PHASE_CALLBACK);
 	(*cbcp_protent.open)(unit);
 	return;
diff --git a/pppd/cbcp.c b/pppd/cbcp.c
index 7f2f787..3c1f366 100644
--- a/pppd/cbcp.c
+++ b/pppd/cbcp.c
@@ -44,19 +44,24 @@
 #include "cbcp.h"
 #include "fsm.h"
 #include "lcp.h"
+#include "ipcp.h"
+#include "pathnames.h"
+#include <sys/stat.h>
 
 static const char rcsid[] = RCSID;
 
 /*
  * Options.
  */
-static int setcbcp __P((char **));
+/*
+int setcbcp __P((char **));
 
 static option_t cbcp_option_list[] = {
     { "callback", o_special, (void *)setcbcp,
       "Ask for callback", OPT_PRIO | OPT_A2STRVAL, &cbcp[0].us_number },
     { NULL }
 };
+*/
 
 /*
  * Protocol entry points.
@@ -84,7 +89,7 @@ struct protent cbcp_protent = {
     0,
     "CBCP",
     NULL,
-    cbcp_option_list,
+    NULL,
     NULL,
     NULL,
     NULL
@@ -94,26 +99,22 @@ cbcp_state cbcp[NUM_PPP];
 
 /* internal prototypes */
 
+static void cbcp_sendreq __P((void *arg));
 static void cbcp_recvreq __P((cbcp_state *us, u_char *pckt, int len));
-static void cbcp_resp __P((cbcp_state *us));
-static void cbcp_up __P((cbcp_state *us));
+static void cbcp_sendresp __P((cbcp_state *us));
+static void cbcp_recvresp __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_sendack __P((void *));
 static void cbcp_recvack __P((cbcp_state *us, u_char *pckt, int len));
 static void cbcp_send __P((cbcp_state *us, int code, u_char *buf, int len));
+static void cbcp_make_options __P((int unit));
+static int  cbcp_check_user __P((char *user, char *mask));
+static void cbcp_start_callback __P((cbcp_state *us));
+static void cbcp_up __P((cbcp_state *us));
 
-/* option processing */
-static int
-setcbcp(argv)
-    char **argv;
-{
-    lcp_wantoptions[0].neg_cbcp = 1;
-    cbcp_protent.enabled_flag = 1;
-    cbcp[0].us_number = strdup(*argv);
-    if (cbcp[0].us_number == 0)
-	novm("callback number");
-    cbcp[0].us_type |= (1 << CB_CONF_USER);
-    cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
-    return (1);
-}
+cbcp_state *stop_iface = NULL;
+
+void (*cbcp_init_hook) __P((cbcp_state *)) = NULL;
+ 
 
 /* init state */
 static void
@@ -125,7 +126,6 @@ cbcp_init(iface)
     us = &cbcp[iface];
     memset(us, 0, sizeof(cbcp_state));
     us->us_unit = iface;
-    us->us_type |= (1 << CB_CONF_NO);
 }
 
 /* lower layer is up */
@@ -137,9 +137,6 @@ cbcp_lowerup(iface)
 
     dbglog("cbcp_lowerup");
     dbglog("want: %d", us->us_type);
-
-    if (us->us_type == CB_CONF_USER)
-        dbglog("phone no: %s", us->us_number);
 }
 
 static void
@@ -147,6 +144,8 @@ cbcp_open(unit)
     int unit;
 {
     dbglog("cbcp_open");
+    if (lcp_hisoptions[unit].neg_cbcp)
+    	cbcp_make_options(unit);
 }
 
 /* process an incomming packet */
@@ -161,6 +160,8 @@ cbcp_input(unit, inpacket, pktlen)
     u_short len;
 
     cbcp_state *us = &cbcp[unit];
+    lcp_options *go  = &lcp_gotoptions[unit];
+    lcp_options *his = &lcp_hisoptions[unit];
 
     inp = inpacket;
 
@@ -184,16 +185,34 @@ cbcp_input(unit, inpacket, pktlen)
  
     switch(code) {
     case CBCP_REQ:
+	if ( !go->neg_cbcp )
+	{
+	    dbglog("CBCP received CBCP_REQ, but CBCP running in server mode!");
+	    return;
+	}
         us->us_id = id;
 	cbcp_recvreq(us, inp, len);
 	break;
 
     case CBCP_RESP:
-	if (debug)
-	    dbglog("CBCP_RESP received");
+	if ( !his->neg_cbcp )
+	{
+	    dbglog("CBCP received CBCP_RESP, but CBCP running in client mode!");
+	    return;
+	}
+	if (id != us->us_id)
+	    dbglog("id doesn't match: expected %d recv %d",
+		   us->us_id, id);
+
+	cbcp_recvresp(us, inp, len);
 	break;
 
     case CBCP_ACK:
+	if ( !go->neg_cbcp )
+	{
+   	    dbglog("CBCP received CBCP_ACK, but CBCP running in server mode!");
+	    return;
+	}
 	if (debug && id != us->us_id)
 	    dbglog("id doesn't match: expected %d recv %d",
 		   us->us_id, id);
@@ -275,7 +294,7 @@ cbcp_printpkt(p, plen, printer, arg)
 		printer(arg, " delay = %d", delay);
 	    }
 
-	    if (olen > 3) {
+	    if (olen > 4) {
 	        int addrt;
 		char str[256];
 
@@ -307,7 +326,7 @@ cbcp_recvreq(us, pckt, pcktlen)
     u_char *pckt;
     int pcktlen;
 {
-    u_char type, opt_len, delay, addr_type;
+    u_char type, opt_len, addr_type;
     char address[256];
     int len = pcktlen;
 
@@ -321,8 +340,9 @@ cbcp_recvreq(us, pckt, pcktlen)
 	if (opt_len < 2 || opt_len > len)
 	    break;
 
+	us->us_delay = 0;
 	if (opt_len > 2)
-	    GETCHAR(delay, pckt);
+	    GETCHAR(us->us_delay, pckt);
 
 	us->us_allowed |= (1 << type);
 
@@ -357,28 +377,29 @@ cbcp_recvreq(us, pckt, pcktlen)
 	return;
     }
 
-    cbcp_resp(us);
+    cbcp_sendresp(us);
 }
 
 static void
-cbcp_resp(us)
+cbcp_sendresp(us)
     cbcp_state *us;
 {
-    u_char cb_type;
+    u_char cb_allowed;
     u_char buf[256];
     u_char *bufp = buf;
     int len = 0;
     int slen;
 
-    cb_type = us->us_allowed & us->us_type;
-    dbglog("cbcp_resp cb_type=%d", cb_type);
+    cb_allowed = us->us_allowed;
+    dbglog("cbcp_resp cb_type=%d", cb_allowed);
 
 #if 0
     if (!cb_type)
         lcp_down(us->us_unit);
 #endif
 
-    if (cb_type & ( 1 << CB_CONF_USER ) ) {
+    if (cb_allowed & ( 1 << CB_CONF_USER ) ) {
+	us->us_type = 1 << CB_CONF_USER;
 	dbglog("cbcp_resp CONF_USER");
 	slen = strlen(us->us_number);
 	if (slen > 250) {
@@ -387,31 +408,33 @@ cbcp_resp(us)
 	}
 	PUTCHAR(CB_CONF_USER, bufp);
 	len = 3 + 1 + slen + 1;
-	PUTCHAR(len , bufp);
-	PUTCHAR(5, bufp); /* delay */
+	PUTCHAR(len, bufp);
+	PUTCHAR(us->us_delay, bufp); /* delay */
 	PUTCHAR(1, bufp);
 	BCOPY(us->us_number, bufp, slen + 1);
 	cbcp_send(us, CBCP_RESP, buf, len);
 	return;
     }
 
-    if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
+    if (cb_allowed & ( 1 << CB_CONF_ADMIN ) ) {
+	us->us_type = 1 << CB_CONF_ADMIN;
 	dbglog("cbcp_resp CONF_ADMIN");
         PUTCHAR(CB_CONF_ADMIN, bufp);
 	len = 3;
 	PUTCHAR(len, bufp);
-	PUTCHAR(5, bufp); /* delay */
+	PUTCHAR(us->us_delay, bufp); /* delay */
 	cbcp_send(us, CBCP_RESP, buf, len);
 	return;
     }
 
-    if (cb_type & ( 1 << CB_CONF_NO ) ) {
+    if (cb_allowed & ( 1 << CB_CONF_NO ) ) {
+	us->us_type = 1 << CB_CONF_NO;
         dbglog("cbcp_resp CONF_NO");
 	PUTCHAR(CB_CONF_NO, bufp);
 	len = 2;
 	PUTCHAR(len , bufp);
 	cbcp_send(us, CBCP_RESP, buf, len);
-	start_networks(us->us_unit);
+//	start_networks(us->us_unit);
 	return;
     }
 }
@@ -448,17 +471,19 @@ cbcp_recvack(us, pckt, len)
     u_char *pckt;
     int len;
 {
-    u_char type, delay, addr_type;
+    u_char type, addr_type;
     int opt_len;
     char address[256];
 
+    stop_iface = us;
+
     if (len >= 2) {
         GETCHAR(type, pckt);
 	GETCHAR(opt_len, pckt);
 	if (opt_len >= 2 && opt_len <= len) {
      
 	    if (opt_len > 2)
-		GETCHAR(delay, pckt);
+		GETCHAR(us->us_delay, pckt);
 
 	    if (opt_len > 4) {
 		GETCHAR(addr_type, pckt);
@@ -467,22 +492,407 @@ cbcp_recvack(us, pckt, len)
 		if (address[0])
 		    dbglog("peer will call: %s", address);
 	    }
-	    if (type == CB_CONF_NO)
-		return;
-
-	    cbcp_up(us);
+	    if (type != CB_CONF_NO)
+	    {
+		callback_in_progress = us->us_unit + 1;
+		callback_in_progress |=  CBCP_CLIENT;
+		cbcp_up(us);
+	    }
+	    else
+		network_phase(us->us_unit);
 
 	} else if (debug)
 	    dbglog("cbcp_recvack: malformed packet");
     }
 }
 
-/* ok peer will do callback */
-static void
+/* Make options
+   if auth req, options from callback-users file, else use CBCP_CONF_USER */
+    static void
+cbcp_make_options (unit)
+    int	unit;
+{
+    cbcp_state *us = &cbcp[unit];
+    FILE *userfile;
+    struct stat sbuf;
+    int best_fit, got_fit, newline;
+    char  uname[ 256 ], option[ 256 ];    
+
+    us->us_id = 1;
+    us->us_count = 0;
+    us->us_delay = 5; /* Default delay 5 seconds */
+    if ( *peer_authname ) { /* Username available */
+	userfile = fopen( _PATH_CBCP_USERS, "r" );
+	if ( userfile == NULL ){
+	    dbglog( "Can't open callback user file: %s %m",
+		    _PATH_CBCP_USERS );
+	    dbglog( "Allow user definied callback." );
+	    us->us_allowed = ( 1 << CB_CONF_USER );
+	}else
+	{
+	    if ( fstat(fileno(userfile), &sbuf) < 0) {
+		dbglog("Cannot stat userfile file %s: %m",
+			_PATH_CBCP_USERS );
+	    } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+		dbglog("Warning - user file %s has world and/or group access",
+			_PATH_CBCP_USERS );
+	    }
+
+	    us->us_allowed = ( 1 << CB_CONF_NO ); /* Assume, no callback allowed */
+
+	    if (getword(userfile, uname, &newline, _PATH_CBCP_USERS)){	/* file not empty */
+		newline = 1;
+		best_fit = 0;
+		*option = 0;
+		for (;;) {
+		    /*
+		     * Skip until we find a word at the start of a line.
+		     */
+		    while (!newline && getword(userfile, uname, 
+				&newline, _PATH_CBCP_USERS))
+			;
+		    if (!newline)
+			break;		/* got to end of file */
+
+		    /*
+		     * Got a user - check if it's a match or a wildcard.
+		     */
+		    got_fit = cbcp_check_user( peer_authname, uname );
+		    if ( got_fit <= best_fit ){
+			newline = 0;
+			continue;
+		    }
+
+		    /* Read the options */
+		    best_fit = got_fit;
+		    if (getword(userfile, option, &newline, _PATH_CBCP_USERS))
+			break;
+
+		    if ( newline )
+			*option = 0;
+
+		    if ( best_fit == 100 )
+			break;
+		}
+	    }
+
+	    switch ( *option ){
+		case '-' : us->us_allowed = ( 1 << CB_CONF_NO ); break;
+		case '*' :
+		case  0  : us->us_allowed = ( 1 << CB_CONF_USER ); break;
+		default  : us->us_allowed = ( 1 << CB_CONF_ADMIN );
+			   us->us_number = strdup( option ); break;
+	    }
+	    fclose( userfile );
+	}    
+    }
+    else
+	us->us_allowed = ( 1 << CB_CONF_USER );
+
+    /* We always accept no callback users */	
+    us->us_allowed |= ( 1 << CB_CONF_NO );
+    
+    if (cbcp_init_hook)
+	cbcp_init_hook( us );
+
+    cbcp_sendreq( us );
+}    
+
+
+/* make cbcp request packet & send it */
+    static void
+cbcp_sendreq (arg)
+    void *arg;
+{
+    cbcp_state *us=(cbcp_state *)arg;
+    u_char cb_allow = us->us_allowed;
+    u_char buf[256];
+    u_char *bufp = buf;
+    int len = 0;
+
+    us->us_count++;
+    if (us->us_count<=CBCP_MAXRETRY)
+	TIMEOUT( cbcp_sendreq, arg, CBCP_DEFTIMEOUT );
+    else
+    {
+	lcp_close(0, "Sorry, CBCP not responding.");
+	return;
+    }
+    dbglog("cbcp_sendreq cb_allowed=%d", cb_allow);
+
+
+    if (cb_allow & ( 1 << CB_CONF_USER ) ) {
+	dbglog("cbcp_sendreq CONF_USER");
+	PUTCHAR(CB_CONF_USER, bufp);
+	len+=3;
+	PUTCHAR(3 , bufp);
+	PUTCHAR(us->us_delay, bufp);
+    }
+
+    if (cb_allow & ( 1 << CB_CONF_ADMIN ) ) {
+	dbglog("cbcp_sendreq CONF_ADMIN");
+	PUTCHAR(CB_CONF_ADMIN, bufp);
+	len += 3;
+	PUTCHAR(3 , bufp);
+	PUTCHAR(us->us_delay, bufp);
+    }
+
+    if (cb_allow & ( 1 << CB_CONF_NO ) ) {
+	dbglog("cbcp_sendreq CONF_NO");
+	PUTCHAR(CB_CONF_NO, bufp);
+	len += 3;
+	PUTCHAR(3 , bufp);
+	PUTCHAR(us->us_delay, bufp);
+    }
+
+    if (len)
+	cbcp_send(us, CBCP_REQ, buf, len);
+    else
+    {
+	dbglog("cbcp: no available options to client!");    
+    }	
+}  
+
+/* Received CBCP response, make ACK */
+    static void
+cbcp_recvresp (us, pckt, len)
+    cbcp_state *us;
+    char *pckt;
+    int len;
+{
+    u_char type, addr_type;
+    int opt_len;
+    char address[256];
+
+    if (len) {
+	GETCHAR(type, pckt);
+	GETCHAR(opt_len, pckt);
+
+	if (!(( 1 << type )& us->us_allowed )) {
+	    dbglog("CBCP received options not allowed on server!");
+	    return;	    
+	}
+
+	if ((type!= CB_CONF_NO ) &&
+		(type!= CB_CONF_USER ) &&
+		(type!= CB_CONF_ADMIN )) {
+	    dbglog("CBCP received BAD Response: too more or unknown options %d",type);
+	    return;	    
+	}
+
+	UNTIMEOUT( cbcp_sendreq, us );
+	us->us_count = 0;
+
+
+	if (opt_len > 2)
+	    GETCHAR(us->us_delay, pckt)
+		if ( us->us_delay < 5 ) 
+		    us->us_delay = 5;
+
+	if (opt_len > 4) {
+	    GETCHAR(addr_type, pckt); /*  Address Type mezo elvesztve  !!! */
+	    memcpy(address, pckt, opt_len - 4);
+	    address[opt_len - 4] = 0;
+	    if (address[0])
+		dbglog("peer will callback the client on: %s", address);
+	    us->us_number=strdup( address );
+	}
+
+	us->us_type = ( 1 << type );    
+	cbcp_sendack( us );
+    }
+    else
+    {
+	dbglog("CBCP received BAD Response: size to small");
+    }
+}
+
+/* Send the CBCP_ACK packet */
+    static void
+cbcp_sendack (arg)
+    void *arg;
+{
+    cbcp_state *us= (cbcp_state *)arg;
+    u_char cb_type;
+    u_char buf[256];
+    u_char *bufp = buf;
+    int len = 0;
+
+    stop_iface = (cbcp_state *)arg;
+    cb_type = us->us_type;
+
+    dbglog("cbcp_sendack cb_type=%d", cb_type);
+
+    us->us_count++;
+    if (us->us_count<=CBCP_MAXRETRY)
+	TIMEOUT( cbcp_sendack, arg, CBCP_DEFTIMEOUT );
+    else
+    {
+	lcp_close(0, "Sorry, CBCP not responding.");
+	return;
+    }
+
+#if 0
+    if (!cb_type)
+	lcp_down(us->us_unit);
+#endif
+
+    if (cb_type == (1 << CB_CONF_USER )) {
+	dbglog("cbcp_sendack CONF_USER");
+	PUTCHAR(CB_CONF_USER, bufp);
+	len = 3 + 1 + strlen(us->us_number) + 1;
+	PUTCHAR(len , bufp);
+	PUTCHAR(us->us_delay, bufp); /* delay */
+	PUTCHAR(1, bufp); /* Elvesztett byte... */
+	BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
+	cbcp_send(us, CBCP_ACK, buf, len);
+	/*	lcp_close( 2, "Illegal, but required to server..." ); */
+	callback_in_progress = us->us_unit + 1;
+	return;
+    }
+
+    if (cb_type == (1 << CB_CONF_ADMIN )) {
+	dbglog("cbcp_sendack CONF_ADMIN");
+	PUTCHAR(CB_CONF_ADMIN, bufp);
+	len = 3;
+	PUTCHAR(len , bufp);
+	PUTCHAR(us->us_delay, bufp); /* delay */
+	PUTCHAR(0, bufp);
+	cbcp_send(us, CBCP_ACK, buf, len);
+	/*	lcp_close( 2, "Illegal, but required to server..." ); */
+	callback_in_progress = us->us_unit + 1;
+	return;
+    }
+
+    if (cb_type == (1 << CB_CONF_NO )) {
+	dbglog("cbcp_sendack CONF_NO");
+	PUTCHAR(CB_CONF_NO, bufp);
+	len = 2;
+	PUTCHAR(len , bufp);
+	PUTCHAR(0, bufp);
+	cbcp_send(us, CBCP_ACK, buf, len);
+	if (us->us_count<=1)
+	    network_phase(us->us_unit);
+	return;
+    }
+
+    dbglog("CBCP - Bad options in Ack routine.");
+}
+ 
+/* CBCP coming succesful up */
+void cbcp_stop()
+{
+    if ( stop_iface && lcp_allowoptions[stop_iface->us_unit].neg_cbcp )
+    {
+	UNTIMEOUT( cbcp_sendack, stop_iface );
+	cbcp_start_callback( stop_iface );
+    }
+}
+
+/* The server side coming up & client 'ack-ed' */
+void cbcp_start_callback (us)
+    cbcp_state *us;
+{
+    lcp_allowoptions[us->us_unit].neg_cbcp=0;
+
+    dbglog("cbcp_start_callback running");
+}
+
+/* The client side coming up: server allowed the callback */
+    static void
 cbcp_up(us)
     cbcp_state *us;
 {
     persist = 0;
     status = EXIT_CALLBACK;
     lcp_close(0, "Call me back, please");
+    lcp_wantoptions[us->us_unit].neg_cbcp = 0;
+    dbglog("cbcp_up called");
+    lcp_close(us->us_unit, "Call me back, please");
 }
+
+/* The main module gets the script with parameters to run */
+char *cbcp_get_script()
+{
+    cbcp_state *us = &cbcp[(callback_in_progress & CBCP_NCLIENT)-1];
+    char script[ 256 ];
+
+    if ( callback_in_progress & CBCP_CLIENT )
+	sprintf( script, "%s %d", _PATH_CBCP_CLIENT, us->us_delay );
+    else
+	sprintf( script, "%s %d %s", _PATH_CBCP_SERVER, 
+		us->us_delay, us->us_number );
+
+    return strdup( script );        
+}
+
+/* give me the hit rate. wild cars '*?' valids */
+int cbcp_check_user ( user, mask )
+    char *user;
+    char *mask;
+{
+    char *curr_user = user;
+    char *curr_mask = mask;
+    char *find, backp = 0;
+    int  count, len = 0;
+    
+    if ( !strcasecmp( user, mask ))
+	return 100;
+	
+    if ( !strcmp( mask, "*" ))
+	return 1;
+	
+    if ( !*user )
+	return 0;
+
+    count = 0;
+
+    while(( find = strpbrk( curr_mask, "*?" )) != 0 ) {
+	if ( find != curr_mask ){
+	    len = find - curr_mask;
+	    if ( strncmp( curr_user, curr_mask, len ))
+		break;	    
+	}
+
+	curr_mask += len + 1;
+	curr_user += len;
+	count += len;
+	if ( *curr_user == 0 )
+	    break;
+	
+	if ( *find == '?' ) {
+	    curr_user++;
+	    if ( *curr_user == 0 )
+		break;
+	} else {
+	    if ( *curr_mask == 0 )
+		break;
+		
+	    if ( ( find = strpbrk( curr_mask, "*?" )) != 0 ){
+		backp = *find;
+		*find = 0;
+	    }
+	    curr_user = strstr( curr_user, curr_mask );
+	    if ( find )
+		*find = backp;
+	    if ( !curr_user )
+		break;
+	    
+	    find = strpbrk( curr_mask, "*?" );
+	    if ( find )
+		len = find - curr_mask;
+	    else
+		len = strlen( curr_mask );
+		
+	    curr_mask += len;
+	    curr_user += len;
+	    count += len;
+	}	
+    }
+    
+    if ( *curr_user && *curr_mask && !strcmp( curr_user, curr_mask ))
+	count += strlen( curr_user );
+
+    return ( count * 100 / strlen( user ) );
+}
+
diff --git a/pppd/cbcp.h b/pppd/cbcp.h
index c2ab3f6..b47bff7 100644
--- a/pppd/cbcp.h
+++ b/pppd/cbcp.h
@@ -6,6 +6,8 @@ typedef struct cbcp_state {
     u_char us_id;		/* Current id */
     u_char us_allowed;
     int    us_type;
+    u_char us_delay;
+    u_char us_count;
     char   *us_number;    /* Telefone Number */
 } cbcp_state;
 
@@ -19,8 +21,20 @@ extern struct protent cbcp_protent;
 #define CBCP_RESP   2
 #define CBCP_ACK    3
 
+
+#define CBCP_DEFTIMEOUT	5
+#define CBCP_MAXRETRY	50
+#define CBCP_CLIENT	0x8000
+#define CBCP_NCLIENT	0x7fff
+
+
 #define CB_CONF_NO     1
 #define CB_CONF_USER   2
 #define CB_CONF_ADMIN  3
 #define CB_CONF_LIST   4
+
+
+char *cbcp_get_script __P(());
+void  cbcp_stop __P(());
+
 #endif
diff --git a/pppd/ipcp.c b/pppd/ipcp.c
index 12bcc61..5793c56 100644
--- a/pppd/ipcp.c
+++ b/pppd/ipcp.c
@@ -61,6 +61,11 @@
 #include "ipcp.h"
 #include "pathnames.h"
 
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+
 static const char rcsid[] = RCSID;
 
 /* global vars */
@@ -1418,6 +1423,10 @@ ipcp_reqci(f, inp, len, reject_if_disagree)
     u_char maxslotindex, cflag;
     int d;
 
+#ifdef CBCP_SUPPORT
+    cbcp_stop();
+#endif
+
     /*
      * Reset all his options.
      */
diff --git a/pppd/ipv6cp.c b/pppd/ipv6cp.c
index 4a09c9a..1627197 100644
--- a/pppd/ipv6cp.c
+++ b/pppd/ipv6cp.c
@@ -167,6 +167,11 @@
 #include "magic.h"
 #include "pathnames.h"
 
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+
 static const char rcsid[] = RCSID;
 
 /* global vars */
@@ -903,6 +908,11 @@ ipv6cp_reqci(f, inp, len, reject_if_disagree)
     u_char *ucp = inp;		/* Pointer to current output char */
     int l = *len;		/* Length left */
 
+#ifdef CBCP_SUPPORT
+    cbcp_stop();
+#endif
+
+
     /*
      * Reset all his options.
      */
diff --git a/pppd/ipxcp.c b/pppd/ipxcp.c
index 7b2343e..9e140d2 100644
--- a/pppd/ipxcp.c
+++ b/pppd/ipxcp.c
@@ -62,6 +62,11 @@
 #include "pathnames.h"
 #include "magic.h"
 
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+
 static const char rcsid[] = RCSID;
 
 /* global vars */
@@ -1003,6 +1008,11 @@ ipxcp_reqci(f, inp, len, reject_if_disagree)
     u_char *ucp = inp;		/* Pointer to current output char */
     int l = *len;		/* Length left */
 
+#ifdef CBCP_SUPPORT
+    cbcp_stop();
+#endif
+
+
     /*
      * Reset all his options.
      */
diff --git a/pppd/lcp.c b/pppd/lcp.c
index 5c77490..68855e2 100644
--- a/pppd/lcp.c
+++ b/pppd/lcp.c
@@ -1782,6 +1782,16 @@ lcp_reqci(f, inp, lenp, reject_if_disagree)
 	    ho->neg_accompression = 1;
 	    break;
 
+	case CI_CALLBACK:
+	    dbglog("lcp_reqci: rcvd CALLBACK");
+	    if (!ao->neg_cbcp ||
+	        cilen != CILEN_CHAR ) {
+		orc = CONFREJ;
+		break;
+	    }
+	    ho->neg_cbcp = 1;
+	    break;
+
 	case CI_MRRU:
 	    if (!ao->neg_mrru || !multilink ||
 		cilen != CILEN_SHORT) {
diff --git a/pppd/main.c b/pppd/main.c
index 014d614..9e23217 100644
--- a/pppd/main.c
+++ b/pppd/main.c
@@ -186,6 +186,11 @@ static sigjmp_buf sigjmp;
 char **script_env;		/* Env. variable values for scripts */
 int s_env_nalloc;		/* # words avail at script_env */
 
+#ifdef CBCP_SUPPORT
+int callback_in_progress;	/* Callback running */
+#endif
+
+
 u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */
 u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
 
@@ -314,6 +319,9 @@ main(argc, argv)
     char *p;
     struct passwd *pw;
     struct protent *protp;
+#ifdef CBCP_SUPPORT
+    char *connector;
+#endif
     char numbuf[16];
 
     link_stats_valid = 0;
@@ -537,6 +545,13 @@ main(argc, argv)
 
 	lcp_open(0);		/* Start protocol */
 	start_link(0);
+#ifdef CBCP_SUPPORT
+	for(callback_in_progress=1;callback_in_progress;){
+	    callback_in_progress=0;
+
+	status = EXIT_NEGOTIATION_FAILED;
+	new_phase(PHASE_ESTABLISH);
+#endif
 	while (phase != PHASE_DEAD) {
 	    handle_events();
 	    get_input();
@@ -556,6 +571,83 @@ main(argc, argv)
 	}
 	/* restore FSMs to original state */
 	lcp_close(0, "");
+#ifdef CBCP_SUPPORT
+	if (callback_in_progress){
+	    connector = NULL;
+	    cbcp_stop();
+	    if (fd_ppp != -1) 
+		remove_fd(fd_ppp);
+	    clean_check();
+	    the_channel->disestablish_ppp(devfd);
+	    fd_ppp = -1;
+
+	    if (!hungup){
+		lcp_lowerdown(0);
+	    } else {
+		tty_close_fds();
+		setup_serial(connector);		    
+
+	    }
+
+	    devfd = callback();		    
+
+	    /* set up the serial device as a ppp interface */
+#ifdef USE_TDB
+	    tdb_lockall(pppdb);
+#endif
+	    fd_ppp = the_channel->establish_ppp(devfd);
+	    if (fd_ppp < 0) {
+#ifdef USE_TDB
+		tdb_unlockall(pppdb);
+#endif
+		status = EXIT_FATAL_ERROR;
+		asked_to_quit = 1;
+		break;
+	    }
+
+	    if (!demand && ifunit >= 0)
+		set_ifunit(1);
+#ifdef USE_TDB
+	    tdb_unlockall(pppdb);
+#endif
+
+	    /*
+	     * Start opening the connection and wait for
+	     * incoming events (reply, timeout, etc.).
+	     */
+	    notice("Connect: %s <--> %s", ifname, ppp_devnam);
+	    gettimeofday(&start_time, NULL);
+	    link_stats_valid = 0;
+	    script_unsetenv("CONNECT_TIME");
+	    script_unsetenv("BYTES_SENT");
+	    script_unsetenv("BYTES_RCVD");
+	    lcp_lowerup(0);
+
+	    /*
+	     * If we are initiating this connection, wait for a short
+	     * time for something from the peer.  This can avoid bouncing
+	     * our packets off his tty before he has it set up.
+	     */
+	    add_fd(fd_ppp);
+	    if (listen_time != 0) {
+		struct timeval t;
+		t.tv_sec = listen_time / 1000;
+		t.tv_usec = listen_time % 1000;
+		wait_input(&t);
+	    }
+
+	    /*if (connector != NULL || ptycommand != NULL) {
+	      struct timeval t;
+	      t.tv_sec = 1;
+	      t.tv_usec = 0;
+	      wait_input(&t);
+	      }*/
+
+	    lcp_open(0);		/* Start protocol */
+	}
+	}	
+#endif
+    
 
 	if (!persist || asked_to_quit || (maxfail > 0 && unsuccess >= maxfail))
 	    break;
@@ -2147,3 +2239,5 @@ cleanup_db()
 	    delete_db_key(p);
 }
 #endif /* USE_TDB */
+
+
diff --git a/pppd/options.c b/pppd/options.c
index 482eab9..eec148c 100644
--- a/pppd/options.c
+++ b/pppd/options.c
@@ -75,6 +75,12 @@
 #include "pppd.h"
 #include "pathnames.h"
 
+#ifdef CBCP_SUPPORT
+#include "fsm.h"
+#include "lcp.h"
+#include "cbcp.h"
+#endif
+
 #if defined(ultrix) || defined(NeXT)
 char *strdup __P((char *));
 #endif
@@ -180,6 +186,11 @@ struct option_list {
     struct option_list *next;
 };
 
+#ifdef CBCP_SUPPORT
+static int setcbcp __P((char **));
+#endif
+
+
 static struct option_list *extra_options = NULL;
 
 /*
@@ -321,6 +332,11 @@ option_t general_options[] = {
       "Check for traffic limit every N seconds", OPT_PRIO | OPT_LLIMIT | 1 },
 #endif
 
+#ifdef CBCP_SUPPORT
+    { "callback", o_special, setcbcp,
+      "Callback request to server - OR - calling back the client" },
+#endif
+
     { NULL }
 };
 
@@ -1623,3 +1639,23 @@ loadplugin(argv)
     return 0;
 }
 #endif /* PLUGIN */
+
+#ifdef CBCP_SUPPORT
+int
+setcbcp(argv)
+    char **argv;
+{
+    cbcp[0].us_number = strdup(*argv);
+    if (cbcp_protent.enabled_flag)
+	novm("Only one callback parameter supported!");
+    if (cbcp[0].us_number == 0)	
+	novm("callback number");
+    if (!strcmp(cbcp[0].us_number,"server")){
+	lcp_allowoptions[0].neg_cbcp = 1;
+    } else {
+	lcp_wantoptions[0].neg_cbcp = 1;
+    }
+    cbcp_protent.enabled_flag = 1;
+    return 1;
+}
+#endif
diff --git a/pppd/pathnames.h b/pppd/pathnames.h
index a33f046..5e108c5 100644
--- a/pppd/pathnames.h
+++ b/pppd/pathnames.h
@@ -45,6 +45,13 @@
 #define _PATH_IPXDOWN	 _ROOT_PATH "/etc/ppp/ipx-down"
 #endif /* IPX_CHANGE */
 
+#ifdef CBCP_SUPPORT
+#define _PATH_CBCP_SERVER _ROOT_PATH "/etc/ppp/callback-server"
+#define _PATH_CBCP_CLIENT _ROOT_PATH "/etc/ppp/callback-client"
+#define _PATH_CBCP_USERS  _ROOT_PATH "/etc/ppp/callback-users"
+#define _PATH_CBCP	  _ROOT_PATH "/etc/ppp/callback"
+#endif /* CBCP_SUPPORT */
+
 #ifdef __STDC__
 #define _PATH_PPPDB	_ROOT_PATH _PATH_VARRUN "pppd2.tdb"
 #else /* __STDC__ */
diff --git a/pppd/pppd.8 b/pppd/pppd.8
index 8ea8200..3cc1e01 100644
--- a/pppd/pppd.8
+++ b/pppd/pppd.8
@@ -88,6 +88,14 @@ is not being run by root.  The \fIname\fR string may not begin with /
 or include .. as a pathname component.  The format of the options file
 is described below.
 .TP
+.B callback \fIserver/number
+When compiled with the CBCP extensions (-DCBCP_SUPPORT) the ppp daemon
+can act as a client to servers which provide CBCP-protocol callback
+negotiation or act as a \fIserver. It reads its options from 
+/etc/ppp/callback-users and invokes /etc/ppp/callback-server
+when dialing out. Otherwise it will invoke /etc/ppp/callback-client
+to wait for a call.
+.TP
 .B connect \fIscript
 Usually there is something which needs to be done to prepare the link
 before the PPP protocol can be started; for instance, with a dial-up
diff --git a/pppd/pppd.h b/pppd/pppd.h
index cf9840a..f47e1b9 100644
--- a/pppd/pppd.h
+++ b/pppd/pppd.h
@@ -71,6 +71,10 @@
 #include "eui64.h"
 #endif
 
+/* for cbcp_init_hook */
+#include "cbcp.h"
+
+
 /*
  * Limits.
  */
@@ -341,6 +345,10 @@ extern struct	bpf_program pass_filter;   /* Filter for pkts to pass */
 extern struct	bpf_program active_filter; /* Filter for link-active pkts */
 #endif
 
+#ifdef CBCP_SUPPORT
+extern int	callback_in_progress;	/*Callback running*/
+#endif
+
 #ifdef MSLANMAN
 extern bool	ms_lanman;	/* Use LanMan password instead of NT */
 				/* Has meaning only with MS-CHAP challenges */
@@ -500,6 +508,8 @@ void unlock_db __P((void));
 
 /* Procedures exported from tty.c. */
 void tty_init __P((void));
+void setup_serial __P((char *));
+int callback __P ((void));
 
 /* Procedures exported from utils.c. */
 void log_packet __P((u_char *, int, char *, int));
@@ -525,6 +535,7 @@ ssize_t complete_read __P((int, void *, size_t));
 				/* read a complete buffer */
 
 /* Procedures exported from auth.c */
+void network_phase __P((int));	  /* the dataexchanger CP-s goung up */
 void link_required __P((int));	  /* we are starting to use the link */
 void start_link __P((int));	  /* bring the link up now */
 void link_terminated __P((int));  /* we are finished with the link */
@@ -712,6 +723,7 @@ extern int (*allowed_address_hook) __P((u_int32_t addr));
 extern void (*ip_up_hook) __P((void));
 extern void (*ip_down_hook) __P((void));
 extern void (*ip_choose_hook) __P((u_int32_t *));
+extern void (*cbcp_init_hook) __P((cbcp_state *));
 
 extern int (*chap_check_hook) __P((void));
 extern int (*chap_passwd_hook) __P((char *user, char *passwd));
diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c
index b675c97..bda4ca6 100644
--- a/pppd/sys-linux.c
+++ b/pppd/sys-linux.c
@@ -936,7 +936,7 @@ void set_up_tty(int tty_fd, int local)
 	inittermios = tios;
 
     tios.c_cflag     &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
-    tios.c_cflag     |= CS8 | CREAD | HUPCL;
+    tios.c_cflag     |= CS8 | CREAD | HUPCL | CLOCAL;
 
     tios.c_iflag      = IGNBRK | IGNPAR;
     tios.c_oflag      = 0;
diff --git a/pppd/tty.c b/pppd/tty.c
index d571b11..295b52b 100644
--- a/pppd/tty.c
+++ b/pppd/tty.c
@@ -97,6 +97,10 @@
 #include "pppd.h"
 #include "fsm.h"
 #include "lcp.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif /* CBCP_SUPPORT */
+ 
 
 void tty_process_extra_options __P((void));
 void tty_check_options __P((void));
@@ -514,12 +518,10 @@ tty_check_options()
 int connect_tty()
 {
 	char *connector;
-	int fdflags;
-#ifndef __linux__
-	struct stat statbuf;
-#endif
 	char numbuf[16];
 
+	connector = doing_callback? callback_script: connect_script;
+
 	/*
 	 * Get a pty master/slave pair if the pty, notty, socket,
 	 * or record options were specified.
@@ -547,75 +549,7 @@ int connect_tty()
 		locked = 1;
 	}
 
-	/*
-	 * Open the serial device and set it up to be the ppp interface.
-	 * First we open it in non-blocking mode so we can set the
-	 * various termios flags appropriately.  If we aren't dialling
-	 * out and we want to use the modem lines, we reopen it later
-	 * in order to wait for the carrier detect signal from the modem.
-	 */
-	got_sigterm = 0;
-	connector = doing_callback? callback_script: connect_script;
-	if (devnam[0] != 0) {
-		for (;;) {
-			/* If the user specified the device name, become the
-			   user before opening it. */
-			int err, prio;
-
-			prio = privopen? OPRIO_ROOT: tty_options[0].priority;
-			if (prio < OPRIO_ROOT && seteuid(uid) == -1) {
-				error("Unable to drop privileges before opening %s: %m\n",
-				      devnam);
-				status = EXIT_OPEN_FAILED;
-				goto errret;
-			}
-			real_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
-			err = errno;
-			if (prio < OPRIO_ROOT && seteuid(0) == -1)
-				fatal("Unable to regain privileges");
-			if (real_ttyfd >= 0)
-				break;
-			errno = err;
-			if (err != EINTR) {
-				error("Failed to open %s: %m", devnam);
-				status = EXIT_OPEN_FAILED;
-			}
-			if (!persist || err != EINTR)
-				goto errret;
-		}
-		ttyfd = real_ttyfd;
-		if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
-		    || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
-			warn("Couldn't reset non-blocking mode on device: %m");
-
-#ifndef __linux__
-		/*
-		 * Linux 2.4 and above blocks normal writes to the tty
-		 * when it is in PPP line discipline, so this isn't needed.
-		 */
-		/*
-		 * Do the equivalent of `mesg n' to stop broadcast messages.
-		 */
-		if (fstat(ttyfd, &statbuf) < 0
-		    || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
-			warn("Couldn't restrict write permissions to %s: %m", devnam);
-		} else
-			tty_mode = statbuf.st_mode;
-#endif /* __linux__ */
-
-		/*
-		 * Set line speed, flow control, etc.
-		 * If we have a non-null connection or initializer script,
-		 * on most systems we set CLOCAL for now so that we can talk
-		 * to the modem before carrier comes up.  But this has the
-		 * side effect that we might miss it if CD drops before we
-		 * get to clear CLOCAL below.  On systems where we can talk
-		 * successfully to the modem with CLOCAL clear and CD down,
-		 * we could clear CLOCAL at this point.
-		 */
-		set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
-				   || initializer != NULL));
-	}
+    setup_serial(connector);
 
 	/*
 	 * If the pty, socket, notty and/or record option was specified,
@@ -1260,3 +1194,111 @@ record_write(f, code, buf, nb, tp)
     }
     return 1;
 }
+
+void setup_serial(char *connector)
+{
+	int fdflags;
+	struct stat statbuf;
+	
+	/*
+	 * Open the serial device and set it up to be the ppp interface.
+	 * First we open it in non-blocking mode so we can set the
+	 * various termios flags appropriately.  If we aren't dialling
+	 * out and we want to use the modem lines, we reopen it later
+	 * in order to wait for the carrier detect signal from the modem.
+	 */
+	hungup = 0;
+	kill_link = 0;
+	connector = doing_callback? callback_script: connect_script;
+	if (devnam[0] != 0) {
+		for (;;) {
+			/* If the user specified the device name, become the
+			   user before opening it. */
+			int err, prio;
+			
+                        prio = privopen? OPRIO_ROOT: tty_options[0].priority;
+			if (prio < OPRIO_ROOT)
+				seteuid(uid);
+			ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
+			err = errno;
+			if (prio < OPRIO_ROOT)
+				seteuid(0);
+			if (ttyfd >= 0)
+				break;
+			errno = err;
+			if (err != EINTR) {
+				error("Failed to open %s: %m", devnam);
+				status = EXIT_OPEN_FAILED;
+			}
+			if (!persist || err != EINTR)
+				return -1;
+		}
+		real_ttyfd = ttyfd;
+		if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
+		    || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+			warn("Couldn't reset non-blocking mode on device: %m");
+
+		/*
+		 * Do the equivalent of `mesg n' to stop broadcast messages.
+		 */
+		if (fstat(ttyfd, &statbuf) < 0
+		    || fchmod(ttyfd, statbuf.st_mode & ~S_IWOTH) < 0) {
+			warn("Couldn't restrict write permissions to %s: %m", devnam);
+		} else
+			tty_mode = statbuf.st_mode;
+
+		/*
+		 * Set line speed, flow control, etc.
+		 * If we have a non-null connection or initializer script,
+		 * on most systems we set CLOCAL for now so that we can talk
+		 * to the modem before carrier comes up.  But this has the
+		 * side effect that we might miss it if CD drops before we
+		 * get to clear CLOCAL below.  On systems where we can talk
+		 * successfully to the modem with CLOCAL clear and CD down,
+		 * we could clear CLOCAL at this point.
+		 */
+		set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
+				   || initializer != NULL));
+	}
+}	
+
+#ifdef CBCP_SUPPORT
+int callback()
+{
+        char *s;
+    	char numbuf[16];
+        
+  		cbcp_protent.enabled_flag = 0; /* Already not need */
+		s = cbcp_get_script();
+		syslog(LOG_INFO, "Callback with <%s>",s );
+		
+		set_up_tty( ttyfd, 1 );
+		
+		if (real_ttyfd != -1) {
+		    if (!default_device && modem) {
+			setdtr(real_ttyfd, 0);	/* in case modem is off hook */
+			sleep(1);
+			setdtr(real_ttyfd, 1);
+		    }
+		}
+
+        /* syslog(LOG_INFO, "ttyfd is %d and hungup is %d",ttyfd,hungup ); */
+		if (device_script(s, ttyfd, ttyfd, 0) < 0) {
+		    error("Callback script failed");
+		    status = EXIT_INIT_FAILED;
+		    setdtr(ttyfd, 0 );
+            return -1;
+		}
+		    
+		info("Serial connection established." );
+		
+		if (real_ttyfd != -1)
+    		    set_up_tty( real_ttyfd, 0 );
+
+		slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
+		script_setenv("SPEED", numbuf, 1);
+        
+        return ttyfd;
+
+}
+#endif /* CBCP Support */
 
design & coding: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
current maintainer: Michael Shigorin