summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuillaume Cottenceau <gc@mandriva.com>2001-06-11 11:44:34 +0000
committerGuillaume Cottenceau <gc@mandriva.com>2001-06-11 11:44:34 +0000
commitab5559aaabd1167a18ac882e64d97c5adc0e7d03 (patch)
treed22adafe4701e0abbccc7456fc58ae60ce75d5fb
parentf35f2383eed07ff16aa76f30975817117eea6cbb (diff)
downloaddrakx-backup-do-not-use-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.tar
drakx-backup-do-not-use-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.tar.gz
drakx-backup-do-not-use-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.tar.bz2
drakx-backup-do-not-use-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.tar.xz
drakx-backup-do-not-use-ab5559aaabd1167a18ac882e64d97c5adc0e7d03.zip
Initial revision
-rw-r--r--mdk-stage1/ppp/Changes-2.3441
-rw-r--r--mdk-stage1/ppp/FAQ634
-rw-r--r--mdk-stage1/ppp/PLUGINS131
-rw-r--r--mdk-stage1/ppp/README168
-rw-r--r--mdk-stage1/ppp/README.MSCHAP80284
-rw-r--r--mdk-stage1/ppp/README.cbcp97
-rw-r--r--mdk-stage1/ppp/README.linux297
-rw-r--r--mdk-stage1/ppp/README.sol2220
-rw-r--r--mdk-stage1/ppp/README.sunos462
-rw-r--r--mdk-stage1/ppp/SETUP111
-rw-r--r--mdk-stage1/ppp/chat/Makefile.linux27
-rw-r--r--mdk-stage1/ppp/chat/Makefile.linux.makeopt27
-rw-r--r--mdk-stage1/ppp/chat/Makefile.sol219
-rw-r--r--mdk-stage1/ppp/chat/Makefile.sunos419
-rw-r--r--mdk-stage1/ppp/chat/chat.8515
-rw-r--r--mdk-stage1/ppp/chat/chat.c1756
-rw-r--r--mdk-stage1/ppp/common/zlib.c5376
-rw-r--r--mdk-stage1/ppp/common/zlib.h1010
-rwxr-xr-xmdk-stage1/ppp/configure141
-rw-r--r--mdk-stage1/ppp/contrib/pppgetpass/Makefile.linux16
-rw-r--r--mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.818
-rw-r--r--mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.gtk.c92
-rw-r--r--mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.sh7
-rw-r--r--mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.vt.c218
-rw-r--r--mdk-stage1/ppp/etc.ppp/chap-secrets2
-rw-r--r--mdk-stage1/ppp/etc.ppp/options5
-rw-r--r--mdk-stage1/ppp/etc.ppp/options.options1
-rw-r--r--mdk-stage1/ppp/etc.ppp/pap-secrets2
-rw-r--r--mdk-stage1/ppp/include/linux/if_ppp.h155
-rw-r--r--mdk-stage1/ppp/include/linux/if_pppvar.h138
-rw-r--r--mdk-stage1/ppp/include/linux/ppp-comp.h203
-rw-r--r--mdk-stage1/ppp/include/linux/ppp_defs.h185
-rw-r--r--mdk-stage1/ppp/include/net/if_ppp.h133
-rw-r--r--mdk-stage1/ppp/include/net/ppp-comp.h165
-rw-r--r--mdk-stage1/ppp/include/net/ppp_defs.h184
-rw-r--r--mdk-stage1/ppp/include/net/pppio.h99
-rw-r--r--mdk-stage1/ppp/include/net/slcompress.h148
-rw-r--r--mdk-stage1/ppp/include/net/vjcompress.h144
-rw-r--r--mdk-stage1/ppp/include/pcap-int.h117
-rw-r--r--mdk-stage1/ppp/linux/Makefile.top55
-rw-r--r--mdk-stage1/ppp/modules/bsd-comp.c1116
-rw-r--r--mdk-stage1/ppp/modules/deflate.c760
-rw-r--r--mdk-stage1/ppp/modules/if_ppp.c865
-rw-r--r--mdk-stage1/ppp/modules/ppp.c2486
-rw-r--r--mdk-stage1/ppp/modules/ppp_ahdlc.c878
-rw-r--r--mdk-stage1/ppp/modules/ppp_comp.c1126
-rw-r--r--mdk-stage1/ppp/modules/ppp_mod.h190
-rw-r--r--mdk-stage1/ppp/modules/vjcompress.c587
-rw-r--r--mdk-stage1/ppp/pppd/Makefile.linux129
-rw-r--r--mdk-stage1/ppp/pppd/Makefile.linux.make131
-rw-r--r--mdk-stage1/ppp/pppd/Makefile.linux.makeopt129
-rw-r--r--mdk-stage1/ppp/pppd/Makefile.sol248
-rw-r--r--mdk-stage1/ppp/pppd/Makefile.sunos426
-rw-r--r--mdk-stage1/ppp/pppd/auth.c1952
-rw-r--r--mdk-stage1/ppp/pppd/cbcp.c456
-rw-r--r--mdk-stage1/ppp/pppd/cbcp.h26
-rw-r--r--mdk-stage1/ppp/pppd/ccp.c1257
-rw-r--r--mdk-stage1/ppp/pppd/ccp.h48
-rw-r--r--mdk-stage1/ppp/pppd/chap.c860
-rw-r--r--mdk-stage1/ppp/pppd/chap.h124
-rw-r--r--mdk-stage1/ppp/pppd/chap_ms.c338
-rw-r--r--mdk-stage1/ppp/pppd/chap_ms.h33
-rw-r--r--mdk-stage1/ppp/pppd/demand.c351
-rw-r--r--mdk-stage1/ppp/pppd/eui64.c40
-rw-r--r--mdk-stage1/ppp/pppd/eui64.h97
-rw-r--r--mdk-stage1/ppp/pppd/fsm.c762
-rw-r--r--mdk-stage1/ppp/pppd/fsm.h144
-rw-r--r--mdk-stage1/ppp/pppd/ipcp.c2054
-rw-r--r--mdk-stage1/ppp/pppd/ipcp.h73
-rw-r--r--mdk-stage1/ppp/pppd/ipv6cp.c1512
-rw-r--r--mdk-stage1/ppp/pppd/ipv6cp.h126
-rw-r--r--mdk-stage1/ppp/pppd/ipxcp.c1570
-rw-r--r--mdk-stage1/ppp/pppd/ipxcp.h71
-rw-r--r--mdk-stage1/ppp/pppd/lcp.c2224
-rw-r--r--mdk-stage1/ppp/pppd/lcp.h95
-rw-r--r--mdk-stage1/ppp/pppd/magic.c87
-rw-r--r--mdk-stage1/ppp/pppd/magic.h23
-rw-r--r--mdk-stage1/ppp/pppd/main.c1831
-rw-r--r--mdk-stage1/ppp/pppd/md4.c298
-rw-r--r--mdk-stage1/ppp/pppd/md4.h64
-rw-r--r--mdk-stage1/ppp/pppd/md5.c306
-rw-r--r--mdk-stage1/ppp/pppd/md5.h58
-rw-r--r--mdk-stage1/ppp/pppd/multilink.c396
-rw-r--r--mdk-stage1/ppp/pppd/options.c1513
-rw-r--r--mdk-stage1/ppp/pppd/patchlevel.h4
-rw-r--r--mdk-stage1/ppp/pppd/pathnames.h57
-rw-r--r--mdk-stage1/ppp/pppd/plugins/Makefile.linux19
-rw-r--r--mdk-stage1/ppp/pppd/plugins/Makefile.sol227
-rw-r--r--mdk-stage1/ppp/pppd/plugins/minconn.c46
-rw-r--r--mdk-stage1/ppp/pppd/plugins/passprompt.c108
-rw-r--r--mdk-stage1/ppp/pppd/ppp.pam6
-rw-r--r--mdk-stage1/ppp/pppd/pppd.81591
-rw-r--r--mdk-stage1/ppp/pppd/pppd.h787
-rw-r--r--mdk-stage1/ppp/pppd/pppd.h.wtmp789
-rw-r--r--mdk-stage1/ppp/pppd/sys-linux.c2672
-rw-r--r--mdk-stage1/ppp/pppd/sys-linux.c.wtmp2750
-rw-r--r--mdk-stage1/ppp/pppd/sys-solaris.c2737
-rw-r--r--mdk-stage1/ppp/pppd/sys-sunos4.c1559
-rw-r--r--mdk-stage1/ppp/pppd/tdb.c1282
-rw-r--r--mdk-stage1/ppp/pppd/tdb.h77
-rw-r--r--mdk-stage1/ppp/pppd/tty.c1164
-rw-r--r--mdk-stage1/ppp/pppd/upap.c640
-rw-r--r--mdk-stage1/ppp/pppd/upap.h87
-rw-r--r--mdk-stage1/ppp/pppd/utils.c948
-rw-r--r--mdk-stage1/ppp/pppdump/Makefile.linux17
-rw-r--r--mdk-stage1/ppp/pppdump/Makefile.linux.makeopt17
-rw-r--r--mdk-stage1/ppp/pppdump/Makefile.linux.pppdump-Makefile17
-rw-r--r--mdk-stage1/ppp/pppdump/Makefile.sol221
-rw-r--r--mdk-stage1/ppp/pppdump/Makefile.sunos421
-rw-r--r--mdk-stage1/ppp/pppdump/bsd-comp.c750
-rw-r--r--mdk-stage1/ppp/pppdump/deflate.c344
-rw-r--r--mdk-stage1/ppp/pppdump/ppp-comp.h150
-rw-r--r--mdk-stage1/ppp/pppdump/pppdump.862
-rw-r--r--mdk-stage1/ppp/pppdump/pppdump.c502
-rw-r--r--mdk-stage1/ppp/pppdump/zlib.c4614
-rw-r--r--mdk-stage1/ppp/pppdump/zlib.h631
-rw-r--r--mdk-stage1/ppp/pppstats/Makefile.linux32
-rw-r--r--mdk-stage1/ppp/pppstats/Makefile.sol220
-rw-r--r--mdk-stage1/ppp/pppstats/Makefile.sunos430
-rw-r--r--mdk-stage1/ppp/pppstats/pppstats.8217
-rw-r--r--mdk-stage1/ppp/pppstats/pppstats.c557
-rw-r--r--mdk-stage1/ppp/sample/auth-down17
-rw-r--r--mdk-stage1/ppp/sample/auth-up17
-rw-r--r--mdk-stage1/ppp/sample/ip-down22
-rw-r--r--mdk-stage1/ppp/sample/ip-up23
-rw-r--r--mdk-stage1/ppp/sample/options153
-rw-r--r--mdk-stage1/ppp/sample/options.ttyXX14
-rw-r--r--mdk-stage1/ppp/sample/pap-secrets28
-rw-r--r--mdk-stage1/ppp/scripts/README143
-rwxr-xr-xmdk-stage1/ppp/scripts/callback77
-rw-r--r--mdk-stage1/ppp/scripts/chat-callback98
-rw-r--r--mdk-stage1/ppp/scripts/chatchat/README134
-rw-r--r--mdk-stage1/ppp/scripts/chatchat/chatchat.c409
-rw-r--r--mdk-stage1/ppp/scripts/ip-down.local.add20
-rw-r--r--mdk-stage1/ppp/scripts/ip-up.local.add24
-rw-r--r--mdk-stage1/ppp/scripts/options-rsh-loc1
-rw-r--r--mdk-stage1/ppp/scripts/options-rsh-rem1
-rw-r--r--mdk-stage1/ppp/scripts/options-ssh-loc1
-rw-r--r--mdk-stage1/ppp/scripts/options-ssh-rem1
-rwxr-xr-xmdk-stage1/ppp/scripts/ppp-off34
-rwxr-xr-xmdk-stage1/ppp/scripts/ppp-on36
-rwxr-xr-xmdk-stage1/ppp/scripts/ppp-on-dialer17
-rwxr-xr-xmdk-stage1/ppp/scripts/ppp-on-rsh72
-rwxr-xr-xmdk-stage1/ppp/scripts/ppp-on-ssh76
-rwxr-xr-xmdk-stage1/ppp/scripts/redialer96
-rw-r--r--mdk-stage1/ppp/scripts/secure-card111
-rw-r--r--mdk-stage1/ppp/solaris/Makedefs16
-rw-r--r--mdk-stage1/ppp/solaris/Makedefs.sol259
-rw-r--r--mdk-stage1/ppp/solaris/Makefile.sol266
-rw-r--r--mdk-stage1/ppp/solaris/Makefile.sol2-6485
-rw-r--r--mdk-stage1/ppp/solaris/Makefile.top50
-rw-r--r--mdk-stage1/ppp/solaris/ppp.c2486
-rw-r--r--mdk-stage1/ppp/solaris/ppp.conf1
-rw-r--r--mdk-stage1/ppp/solaris/ppp_ahdlc.c878
-rw-r--r--mdk-stage1/ppp/solaris/ppp_ahdlc_mod.c49
-rw-r--r--mdk-stage1/ppp/solaris/ppp_comp.c1126
-rw-r--r--mdk-stage1/ppp/solaris/ppp_comp_mod.c81
-rw-r--r--mdk-stage1/ppp/solaris/ppp_mod.c174
-rw-r--r--mdk-stage1/ppp/solaris/ppp_mod.h190
-rw-r--r--mdk-stage1/ppp/sunos4/Makedefs13
-rw-r--r--mdk-stage1/ppp/sunos4/Makefile57
-rw-r--r--mdk-stage1/ppp/sunos4/Makefile.top46
-rw-r--r--mdk-stage1/ppp/sunos4/if_ppp_vdcmd.c57
-rwxr-xr-xmdk-stage1/ppp/sunos4/ppp.INSTALL104
-rw-r--r--mdk-stage1/ppp/sunos4/ppp_ahdlc_vdcmd.c57
-rw-r--r--mdk-stage1/ppp/sunos4/ppp_comp_vdcmd.c57
-rw-r--r--mdk-stage1/ppp/sunos4/ppp_vdcmd.c81
-rw-r--r--mdk-stage1/ppp/svr4/Makedefs16
-rw-r--r--mdk-stage1/ppp/svr4/Makedefs.sol259
-rw-r--r--mdk-stage1/ppp/svr4/Makefile.sol266
-rw-r--r--mdk-stage1/ppp/svr4/Makefile.sol2-6485
-rw-r--r--mdk-stage1/ppp/svr4/Makefile.svr460
-rw-r--r--mdk-stage1/ppp/svr4/Makefile.top50
-rw-r--r--mdk-stage1/ppp/svr4/ppp.Master1
-rw-r--r--mdk-stage1/ppp/svr4/ppp.Node1
-rw-r--r--mdk-stage1/ppp/svr4/ppp.System1
-rw-r--r--mdk-stage1/ppp/svr4/ppp.conf1
-rw-r--r--mdk-stage1/ppp/svr4/ppp_ahdl.Master1
-rw-r--r--mdk-stage1/ppp/svr4/ppp_ahdl.System1
-rw-r--r--mdk-stage1/ppp/svr4/ppp_ahdlc_mod.c49
-rw-r--r--mdk-stage1/ppp/svr4/ppp_comp.Master1
-rw-r--r--mdk-stage1/ppp/svr4/ppp_comp.System1
-rw-r--r--mdk-stage1/ppp/svr4/ppp_comp_mod.c81
-rw-r--r--mdk-stage1/ppp/svr4/ppp_mod.c174
184 files changed, 74040 insertions, 0 deletions
diff --git a/mdk-stage1/ppp/Changes-2.3 b/mdk-stage1/ppp/Changes-2.3
new file mode 100644
index 000000000..f5c954b4b
--- /dev/null
+++ b/mdk-stage1/ppp/Changes-2.3
@@ -0,0 +1,441 @@
+What was new in ppp-2.3.11.
+***************************
+
+* Support for Solaris 8 has been added, including support for
+ replumbing and IPV6.
+
+* The Solaris `snoop' utility should now work on ppp interfaces.
+
+* New hooks have been added - pap_logout_hook, ip_up_hook, and
+ ip_down_hook.
+
+* A new `passprompt' plugin is included, thanks to Alan Curry, which
+ makes it possible for pppd to call an external program to get the
+ PAP password to send to the peer.
+
+* The error messages for the situation where authentication is
+ required because the system has a default route have been improved.
+
+* There is a new connect_delay option which specifies how long pppd
+ should pause after the connect script finishes. Previously this
+ delay was fixed at 1 second. (This delay terminates as soon as pppd
+ sees a valid PPP frame from the peer.)
+
+* The `hide-password' option is now the default, and there is a new
+ `show-password' option to enable the printing of password strings in
+ the debug output.
+
+* A fairly complete list of the names of PPP protocols has been added
+ so that when pppd rejects a frame because its protocol is not
+ supported, it can print the name of the unsupported protocol.
+
+* Synchronous serial lines are supported under Linux 2.3.x.
+
+* The bug where pppd would not recognize a modem hangup under Linux
+ 2.3.x kernels has been fixed.
+
+
+What was new in ppp-2.3.10.
+***************************
+
+* Pppd now supports `plugins', which are pieces of code (packaged as
+ shared libraries) which can be loaded into pppd at runtime and which
+ can affect its behaviour. The intention is that plugins provide a
+ way for people to customize the behaviour of pppd for their own
+ needs without needing to change the base pppd source. I have added
+ some hooks into pppd (places where pppd will call a function
+ pointer, if non-zero, to replace some of pppd's code) and I will be
+ receptive to suggestions about places to add more hooks. Plugins
+ are supported under Linux and Solaris at present.
+
+* We have a new maintainer for the Solaris port, Adi Masputra of Sun
+ Microsystems, and he has updated the Solaris port so that it should
+ work on 64-bit machines under Solaris 7 and later.
+
+* Pppd now has an `allow-ip' option, which takes an argument which is
+ an IP address (or subnet) which peers are permitted to use without
+ authenticating themselves. The argument takes the same form as each
+ element of the allowed IP address list in the secrets files. The
+ allow-ip option is privileged and may be specified multiple times.
+ Using the allow-ip option should be cleaner than putting a line like
+ `"" * "" address' in /etc/ppp/pap-secrets.
+
+* Chat can now substitute environment variables into the script. This
+ is enabled by the -E flag. (Thanks to Andreas Arens for the patch.)
+
+* If the PAP username and password from the peer contains unprintable
+ characters, they will be translated to a printable form before
+ looking in the pap-secrets file. Characters >= 0x80 are translated
+ to a M- form, and characters from 0 to 0x1f (and 0x7f as well) are
+ translated to a ^X form. If this change causes you grief, let me
+ know what would be a better translation. It appears that some peers
+ send nulls or other control characters in their usernames and
+ passwords.
+
+* Pppd has new `ktune' and `noktune' options, which enable/disable
+ it to change kernel settings as appropriate. This is only
+ implemented under Linux, and requires the /proc filesystem to be
+ mounted. Under Linux, with the ktune option, pppd will enable IP
+ forwarding in the kernel if the proxyarp option is used, and will
+ enable the dynamic IP address kernel option in demand mode if the
+ local IP address changes.
+
+* Pppd no longer requires a remote address to be specified for demand
+ dialling. If none is specified, it will use a default value of
+ 10.112.112.112+unit_number. (It will not propose this default to
+ the peer.)
+
+* The default holdoff is now 0 if no connect script is given.
+
+* The IPV6 code from Tommi Komulainen, which I unfortunately only
+ partially merged in to ppp-2.3.9, has been fixed and updated.
+
+* The linux compilation glitches should be fixed now.
+
+
+What was new in ppp-2.3.9.
+**************************
+
+* Support for the new generic PPP layer under development for the
+ Linux kernel.
+
+* You can now place extra options to apply to specific users at the
+ end of the line with their password in the pap-secrets or
+ chap-secrets file, separated from the IP address(es) with a "--"
+ separator. These options are parsed after the peer is authenticated
+ but before network protocol (IPCP, IPXCP) or CCP negotiation
+ commences.
+
+* Pppd will apply the holdoff period if the link was terminated by the
+ peer. It doesn't apply it if the link was terminated because the
+ local pppd thought it was idle.
+
+* Synchronous support for Solaris has been added, thanks to John
+ Morrison, and for FreeBSD, thanks to Paul Fulghum.
+
+* IPV6 support has been merged in, from Tommi Komulainen. At the
+ moment it only supports Linux and it is not tested by me.
+
+* The `nodefaultip' option can be used in demand mode to say that pppd
+ should not suggest its local IP address to the peer.
+
+* The `init' option has been added; this causes pppd to run a script
+ to initialize the serial device (e.g. by sending an init string to
+ the modem). Unlike the connect option, this can be used in a
+ dial-in situation. (Thanks to Tobias Ringstrom.)
+
+* There is a new `logfile' option to send log messages to a file as
+ well as syslog.
+
+* There is a new, privileged `linkname' option which sets a logical
+ name for the link. Pppd will create a /var/run/ppp-<linkname>.pid
+ file containing its process ID.
+
+* There is a new `maxfail' option which specifies how many consecutive
+ failed connection attempts are permitted before pppd will exit. The
+ default value is 10, and 0 means infinity. :-)
+
+* Sundry bugs fixed.
+
+
+What was new in ppp-2.3.8.
+**************************
+
+* The exit status of pppd will now indicate whether the link was
+ successfully established, or if not, what error was encountered.
+
+* Pppd has two new options: fdlog <n> will send log messages to file
+ descriptor <n> instead of standard output, and nofdlog will stop log
+ messages from being sent to any file descriptor (they will still be
+ sent to syslog). Pppd now will not send log messages to a file
+ descriptor if the serial port is open on that file descriptor.
+
+* Pppd sets an environment variable called PPPLOGNAME for scripts that
+ it runs, indicating the login name of the user who invoked pppd.
+
+* Pppd sets environment variables CONNECT_TIME, BYTES_SENT and
+ BYTES_RCVD for the ip-down and auth-down scripts indicating the
+ statistics for the connection just terminated. (CONNECT_TIME is in
+ seconds.)
+
+* If the user has the serial device open on standard input and
+ specifies a symbolic link to the serial device on the command line,
+ pppd will detect this and behave correctly (i.e. not detach from its
+ controlling terminal). Furthermore, if the serial port is open for
+ reading and writing on standard input, pppd will assume that it is
+ locked by its invoker and not lock it itself.
+
+* Chat now has a feature where if a string to be sent begins with an
+ at sign (@), the rest of the string is taken as the name of a file
+ (regular file or named pipe), and the actual string to send is taken
+ from that file.
+
+* Support for FreeBSD-2.2.8 and 3.0 has been added, thanks to Paul
+ Fulghum.
+
+* The Tru64 (aka Digital Unix aka OSF/1) port has been updated.
+
+* The system panics on Solaris SMP systems related to PPP connections
+ being established and terminated should no longer occur.
+
+* Fixed quite a few bugs.
+
+
+What was new in ppp-2.3.7.
+**************************
+
+* Pppd can now automatically allocate itself a pseudo-tty to use as
+ the serial device. This has made three new options possible:
+
+ - `pty script' will run `script' with its standard input and output
+ connected to the master side of the pty. For example:
+ pppd pty 'ssh -t server.my.net pppd'
+ is a basic command for setting up a PPP link (tunnel) over ssh.
+ (In practice you may need to specify other options such as IP
+ addresses, etc.)
+
+ - `notty' tells pppd to communicate over its standard input and
+ output, which do not have to be a terminal device.
+
+ - `record filename' tells pppd to record all of the characters sent
+ and received over the serial device to a file called `filename'.
+ The data is recorded in a tagged format with timestamps, which can
+ be printed in a readable form with the pppdump program, which is
+ included in this distribution.
+
+* Pppd now logs the connect time and number of bytes sent and received
+ (at the level of the serial device) when the connection is
+ terminated.
+
+* If you use the updetach or nodetach option, pppd will print its
+ messages to standard output as well as logging them with syslog
+ (provided of course pppd isn't using its standard input or output as
+ its serial device).
+
+* There is a new `privgroup groupname' option (a privileged option).
+ If the user running pppd is in group `groupname', s/he can use
+ privileged options without restriction.
+
+* There is a new `receive-all' option, which causes pppd to accept all
+ control characters, even the ones that the peer should be escaping
+ (i.e. the receive asyncmap is 0). This is useful with some buggy
+ peers.
+
+* The default asyncmap is now 0.
+
+* There is a new `sync' option, currently only implemented under
+ Linux, which allows pppd to run on synchronous HDLC devices.
+
+* If a value for the device name or for the connect, disconnect,
+ welcome or pty option is given in a privileged option file
+ (i.e. /etc/ppp/options or a file loaded with the `call' option), it
+ cannot be overridden by a non-privileged user.
+
+* Many bugs have been fixed, notably:
+ - signals are not blocked unnecessarily, as they were in 2.3.6.
+ - the usepeerdns option should work now.
+ - the SPEED environment variable for scripts is set correctly.
+ - the /etc/ppp/auth-down script is not run until auth-up completes.
+ - the device is opened as root if it is the device on standard
+ input.
+ - pppd doesn't die with the ioctl(PPPIOCSASYNCMAP) error under linux
+ if a hangup occurs at the wrong time.
+
+* Some error messages have been changed to be clearer (I hope :-)
+
+
+What was new in ppp-2.3.6.
+**************************
+
+* Pppd now opens the tty device as the user (rather than as root) if
+ the device name was given by the user, i.e. on the command line or
+ in the ~/.ppprc file. If the device name was given in
+ /etc/ppp/options or in a file loaded with the `call' option, the
+ device is opened as root.
+
+* The default behaviour of pppd is now to let a peer which has not
+ authenticated itself (e.g. your ISP) use any IP address to which the
+ system does not already have a route. (This is currently only
+ supported under Linux, Solaris and Digital Unix; on the other
+ systems, the peer must now authenticate itself unless the noauth
+ option is used.)
+
+* Added new option `usepeerdns', thanks to Nick Walker
+ <nickwalker@email.com>. If the peer supplies DNS addresses, these
+ will be written to /etc/ppp/resolv.conf. The ip-up script can then
+ be used to add these addresses to /etc/resolv.conf if desired (see
+ the ip-up.local.add and ip-down.local.add files in the scripts
+ directory).
+
+* The Solaris ppp driver should now work correctly on SMP systems.
+
+* Minor corrections so that the code can compile under Solaris 7,
+ and under Linux with glibc-2.1.
+
+* The Linux kernel driver has been restructured for improved
+ performance.
+
+* Pppd now won't start the ip-down script until the ip-up script has
+ finished.
+
+
+What was new in ppp-2.3.5.
+**************************
+
+* Minor corrections to the Digital UNIX and NetBSD ports.
+
+* A workaround to avoid tickling a bug in the `se' serial port driver
+on Sun PCI Ultra machines running Solaris.
+
+* Fixed a bug in the negotiation of the Microsoft WINS server address
+option.
+
+* Fixed a bug in the Linux port where it would fail for kernel
+versions above 2.1.99.
+
+
+What was new in ppp-2.3.4.
+**************************
+
+* The NeXT port has been updated, thanks to Steve Perkins.
+
+* ppp-2.3.4 compiles and works under Solaris 2.6, using either gcc or
+cc.
+
+* With the Solaris, SVR4 and SunOS ports, you can control the choice
+of C compiler, C compiler options, and installation directories by
+editing the svr4/Makedefs or sunos4/Makedefs file.
+
+* Until now, we have been using the number 24 to identify Deflate
+compression in the CCP negotiations, which was the number in the draft
+RFC describing Deflate. The number actually assigned to Deflate is
+26. The code has been changed to use 26, but to allow the use of 24
+for now for backwards compatibility. (This can be disabled with the
+`nodeflatedraft' option to pppd.)
+
+* Fixed some bugs in the linux driver and deflate compressor which
+were causing compression problems, including corrupting long
+incompressible packets sometimes.
+
+* Fixes to the PAM and shadow password support in pppd, from Al
+Longyear and others.
+
+* Pppd now sets some environment variables for scripts it invokes
+(ip-up/down, auth-ip/down), giving information about the connection.
+The variables it sets are PEERNAME, IPLOCAL, IPREMOTE, UID, DEVICE,
+SPEED, and IFNAME.
+
+* Pppd now has an `updetach' option, which will cause it to detach
+from its controlling terminal once the link has come up (i.e. once it
+is available for IP traffic).
+
+
+What was new in ppp-2.3.3.
+**************************
+
+* Fixed compilation problems under SunOS.
+
+* Fixed a bug introduced into chat in 2.3.2, and compilation problems
+introduced into the MS-CHAP implementation in 2.3.2.
+
+* The linux kernel driver has been updated for recent 2.1-series
+kernel changes, and it now will ask kerneld to load compression
+modules when required, if the kernel is configured to support kerneld.
+
+* Pppd should now compile correctly under linux on systems with glibc.
+
+
+What was new in ppp-2.3.2.
+**************************
+
+* In 2.3.1, I made a change which was intended to make pppd able to
+detect loss of CD during or immediately after the connection script
+runs. Unfortunately, this had the side-effect that the connection
+script wouldn't work at all on some systems. This change has been
+reversed.
+
+* Fix compilation problems in the Linux kernel driver.
+
+
+What was new in ppp-2.3.1.
+**************************
+
+* Enhancements to chat, thanks to Francis Demierre. Chat can now
+accept comments in the chat script file, and has new SAY, HANGUP,
+CLR_ABORT and CLR_REPORT keywords.
+
+* Fixed a bug which causes 2.3.0 to crash Solaris systems.
+
+* Bug-fixes and restructuring of the Linux kernel driver.
+
+* The holdoff behaviour of pppd has been changed slightly: now, if
+the link comes up for IP (or other network protocol) traffic, we
+consider that the link has been successfully established, and don't
+enforce the holdoff period after the link goes down.
+
+* Pppd should now correctly wait for CD (carrier detect) from the
+modem, even when the serial port initially had CLOCAL set, and it
+should also detect loss of CD during or immediately after the
+connection script runs.
+
+* Under linux, pppd will work with older 2.2.0* version kernel
+drivers, although demand-dialling is not supported with them.
+
+* Minor bugfixes for pppd.
+
+
+What was new in ppp-2.3.
+************************
+
+* Demand-dialling. Pppd now has a mode where it will establish the
+network interface immediately when it starts, but not actually bring
+the link up until it sees some data to be sent. Look for the demand
+option description in the pppd man page. Demand-dialling is not
+supported under Ultrix or NeXTStep.
+
+* Idle timeout. Pppd will optionally terminate the link if no data
+packets are sent or received within a certain time interval.
+
+* Pppd now runs the /etc/ppp/auth-up script, if it exists, when the
+peer successfully authenticates itself, and /etc/ppp/auth-down when
+the connection is subsequently terminated. This can be useful for
+accounting purposes.
+
+* A new packet compression scheme, Deflate, has been implemented.
+This uses the same compression method as `gzip'. This method is free
+of patent or copyright restrictions, and it achieves better
+compression than BSD-Compress. It does consume more CPU cycles for
+compression than BSD-Compress, but this shouldn't be a problem for
+links running at 100kbit/s or less.
+
+* There is no code in this distribution which is covered by Brad
+Clements' restrictive copyright notice. The STREAMS modules for SunOS
+and OSF/1 have been rewritten, based on the Solaris 2 modules, which
+were written from scratch without any Clements code.
+
+* Pppstats has been reworked to clean up the output format somewhat.
+It also has a new -d option which displays data rate in kbyte/s for
+those columns which would normally display bytes.
+
+* Pppd options beginning with - or + have been renamed, e.g. -ip
+became noip, +chap became require-chap, etc. The old options are
+still accepted for compatibility but may be removed in future.
+
+* Pppd now has some options (such as the new `noauth' option) which
+can only be specified if it is being run by root, or in an
+"privileged" options file: /etc/ppp/options or an options file in the
+/etc/ppp/peers directory. There is a new "call" option to read
+options from a file in /etc/ppp/peers, making it possible for non-root
+users to make unauthenticated connections, but only to certain trusted
+peers. My intention is to make the `auth' option the default in a
+future release.
+
+* Several minor new features have been added to pppd, including the
+maxconnect and welcome options. Pppd will now terminate the
+connection when there are no network control protocols running. The
+allowed IP address(es) field in the secrets files can now specify
+subnets (with a notation like 123.45.67.89/24) and addresses which are
+not acceptable (put a ! on the front).
+
+* Numerous bugs have been fixed (no doubt some have been introduced :-)
+Thanks to those who reported bugs in ppp-2.2.
diff --git a/mdk-stage1/ppp/FAQ b/mdk-stage1/ppp/FAQ
new file mode 100644
index 000000000..96bc5c708
--- /dev/null
+++ b/mdk-stage1/ppp/FAQ
@@ -0,0 +1,634 @@
+This is a list of Frequently Asked Questions about using ppp-2.x and
+their answers.
+
+
+------------------------------------------------------------------------
+
+Q: Can you give me an example of how I might set up my machine to dial
+out to an ISP?
+
+A: Here's an example for dialling out to an ISP via a modem on
+/dev/tty02. The modem uses hardware (CTS/RTS) flow control, and the
+serial port is run at 38400 baud. The ISP assigns our IP address.
+
+To configure pppd for this connection, create a file under
+/etc/ppp/peers called (say) my-isp containing the following:
+
+tty02 crtscts 38400
+connect 'chat -v -f /etc/ppp/chat/my-isp'
+defaultroute
+
+The ppp connection is then initiated using the following command:
+
+pppd call my-isp
+
+Of course, if the directory containing pppd is not in your path, you
+will need to give the full pathname for pppd, for example,
+/usr/sbin/pppd.
+
+When you run this, pppd will use the chat program to dial the ISP and
+invoke its ppp service. Chat will read the file specified with -f,
+namely /etc/ppp/chat/my-isp, to find a list of strings to expect to
+receive, and strings to send. This file would contain something like
+this:
+
+ABORT "NO CARRIER"
+ABORT "NO DIALTONE"
+ABORT "ERROR"
+ABORT "NO ANSWER"
+ABORT "BUSY"
+ABORT "Username/Password Incorrect"
+"" "at"
+OK "at&d2&c1"
+OK "atdt2479381"
+"name:" "^Uusername"
+"word:" "\qpassword"
+"annex" "\q^Uppp"
+"Switching to PPP-ppp-Switching to PPP"
+
+You will need to change the details here. The first string on each
+line is a string to expect to receive; the second is the string to
+send. You can add or delete lines according to the dialog required to
+access your ISP's system. This example is for a modem with a standard
+AT command set, dialling out to an Annex terminal server. The \q
+toggles "quiet" mode; when quiet mode is on, the strings to be sent
+are replaced by ?????? in the log. You may need to go through the
+dialog manually using kermit or tip first to determine what should go
+in the script.
+
+To terminate the link, run the following script, called (say)
+kill-ppp:
+
+#!/bin/sh
+unit=ppp${1-0}
+piddir=/var/run
+if [ -f $piddir/$unit.pid ]; then
+ kill -1 `cat $piddir/$unit.pid`
+fi
+
+On some systems (SunOS, Solaris, Ultrix), you will need to change
+/var/run to /etc/ppp.
+
+
+------------------------------------------------------------------------
+
+Q: Can you give me an example of how I could set up my office machine
+so I can dial in to it from home?
+
+A: Let's assume that the office machine is called "office" and is on a
+local ethernet subnet. Call the home machine "home" and give it an IP
+address on the same subnet as "office". We'll require both machines
+to authenticate themselves to each other.
+
+Set up the files on "office" as follows:
+
+/etc/ppp/options contains:
+
+auth # require the peer to authenticate itself
+lock
+# other options can go here if desired
+
+/etc/ppp/chap-secrets contains:
+
+home office "beware the frub-jub" home
+office home "bird, my son!%&*" -
+
+Set up a modem on a serial port so that users can dial in to the
+modem and get a login prompt.
+
+On "home", set up the files as follows:
+
+/etc/ppp/options contains the same as on "office".
+
+/etc/ppp/chap-secrets contains:
+
+home office "beware the frub-jub" -
+office home "bird, my son!%&*" office
+
+Create a file called /etc/ppp/peers/office containing the following:
+
+tty02 crtscts 38400
+connect 'chat -v -f /etc/ppp/chat/office'
+defaultroute
+
+(You may need to change some of the details here.)
+
+Create the /etc/ppp/chat/office file containing the following:
+
+ABORT "NO CARRIER"
+ABORT "NO DIALTONE"
+ABORT "ERROR"
+ABORT "NO ANSWER"
+ABORT "BUSY"
+ABORT "ogin incorrect"
+"" "at"
+OK "at&d2&c1"
+OK "atdt2479381"
+"name:" "^Uusername"
+"word:" "\qpassword"
+"$" "\q^U/usr/sbin/pppd proxyarp"
+"~"
+
+You will need to change the details. Note that the "$" in the
+second-last line is expecting the shell prompt after a successful
+login - you may need to change it to "%" or something else.
+
+You then initiate the connection (from home) with the command:
+
+pppd call office
+
+------------------------------------------------------------------------
+
+Q: When I try to establish a connection, the modem successfully dials
+the remote system, but then hangs up a few seconds later. How do I
+find out what's going wrong?
+
+A: There are a number of possible problems here. The first thing to
+do is to ensure that pppd's messages are visible. Pppd uses the
+syslog facility to log messages which help to identify specific
+problems. Messages from pppd have facility "daemon" and levels
+ranging from "debug" to "error".
+
+Usually it is useful to see messages of level "notice" or higher on
+the console. To see these, find the line in /etc/syslog.conf which
+has /dev/console on the right-hand side, and add "daemon.notice" in
+the list on the left. The line will end up looking something like
+this:
+
+*.err;kern.debug;auth.notice;mail.crit;daemon.notice /dev/console
+
+Note that the whitespace is tabs, *not* spaces.
+
+If you are having problems, it may be useful to see messages of level
+"info" as well, in which case you would change "daemon.notice" to
+"daemon.info".
+
+In addition, it is useful to collect pppd's debugging output in a
+file - the debug option to pppd causes it to log the contents of all
+control packets sent and received in human-readable form. To do this,
+add a line like this to /etc/syslog.conf:
+
+daemon,local2.debug /etc/ppp/log
+
+and create an empty /etc/ppp/log file.
+
+When you change syslog.conf, you will need to send a HUP signal to
+syslogd to causes it to re-read syslog.conf. You can do this with a
+command like this (as root):
+
+ kill -HUP `cat /etc/syslogd.pid`
+
+(On some systems, you need to use /var/run/syslog.pid instead of
+/etc/syslogd.pid.)
+
+After setting up syslog like this, you can use the -v flag to chat and
+the `debug' option to pppd to get more information. Try initiating
+the connection again; when it fails, inspect /etc/ppp/log to see what
+happened and where the connection failed.
+
+
+------------------------------------------------------------------------
+
+Q: When I try to establish a connection, I get an error message saying
+"Serial link is not 8-bit clean". Why?
+
+A: The most common cause is that your connection script hasn't
+successfully dialled out to the remote system and invoked ppp service
+there. Instead, pppd is talking to something (a shell or login
+process on the remote machine, or maybe just the modem) which is only
+outputting 7-bit characters.
+
+This can also arise with a modem which uses an AT command set if the
+dial command is issued before pppd is invoked, rather than within a
+connect script started by pppd. If the serial port is set to 7
+bits/character plus parity when the last AT command is issued, the
+modem serial port will be set to the same setting.
+
+Note that pppd *always* sets the local serial port to 8 bits per
+character, with no parity and 1 stop bit. So you shouldn't need to
+issue an stty command before invoking pppd.
+
+
+------------------------------------------------------------------------
+
+Q: When I try to establish a connection, I get an error message saying
+"Serial line is looped back". Why?
+
+A: Probably your connection script hasn't successfully dialled out to
+the remote system and invoked ppp service there. Instead, pppd is
+talking to something which is just echoing back the characters it
+receives. The -v option to chat can help you find out what's going
+on. It can be useful to include "~" as the last expect string to
+chat, so chat won't return until it's seen the start of the first PPP
+frame from the remote system.
+
+Another possibility is that your phone connection has dropped for some
+obscure reason and the modem is echoing the characters it receives
+from your system.
+
+
+------------------------------------------------------------------------
+
+Q: I installed pppd successfully, but when I try to run it, I get a
+message saying something like "peer authentication required but no
+authentication files accessible".
+
+A: When pppd is used on a machine which already has a connection to
+the Internet (or to be more precise, one which has a default route in
+its routing table), it will require all peers to authenticate
+themselves. The reason for this is that if you don't require
+authentication, you have a security hole, because the peer can
+basically choose any IP address it wants, even the IP address of some
+trusted host (for example, a host mentioned in some .rhosts file).
+
+On machines which don't have a default route, pppd does not require
+the peer to authenticate itself. The reason is that such machines
+would mostly be using pppd to dial out to an ISP which will refuse to
+authenticate itself. In that case the peer can use any IP address as
+long as the system does not already have a route to that address.
+For example, if you have a local ethernet network, the peer can't use
+an address on that network. (In fact it could if it authenticated
+itself and it was permitted to use that address by the pap-secrets or
+chap-secrets file.)
+
+There are 3 ways around the problem:
+
+1. If possible, arrange for the peer to authenticate itself, and
+create the necessary secrets files (/etc/ppp/pap-secrets and/or
+/etc/ppp/chap-secrets).
+
+2. If the peer refuses to authenticate itself, and will always be
+using the same IP address, or one of a small set of IP addresses, you
+can create an entry in the /etc/ppp/pap-secrets file like this:
+
+ "" * "" his-ip.his-domain his-other-ip.other-domain
+
+(that is, using the empty string for the client name and password
+fields). Of couse, you replace the 4th and following fields in the
+example above with the IP address(es) that the peer may use. You can
+use either hostnames or numeric IP addresses.
+
+3. You can add the `noauth' option to the /etc/ppp/options file.
+Pppd will then not ask the peer to authenticate itself. If you do
+this, I *strongly* recommend that you remove the set-uid bit from the
+permissions on the pppd executable, with a command like this:
+
+ chmod u-s /usr/sbin/pppd
+
+Then, an intruder could only use pppd maliciously if they had already
+become root, in which case they couldn't do any more damage using pppd
+than they could anyway.
+
+
+------------------------------------------------------------------------
+
+Q: What do I need to put in the secrets files?
+
+A: Three things:
+ - secrets (i.e. passwords) to use for authenticating this host to
+ other hosts (i.e., for proving our identity to others);
+ - secrets which other hosts can use for authenticating themselves
+ to us (i.e., so that they can prove their identity to us); and
+ - information about which IP addresses other hosts may use, once
+ they have authenticated themselves.
+
+There are two authentication files: /etc/ppp/pap-secrets, which
+contains secrets for use with PAP (the Password Authentication
+Protocol), and /etc/ppp/chap-secrets, which contains secrets for use
+with CHAP (the Challenge Handshake Authentication Protocol). Both
+files have the same simple format, which is as follows:
+
+- The file contains a series of entries, each of which contains a
+secret for authenticating one machine to another.
+
+- Each entry is contained on a single logical line. A logical line
+may be continued across several lines by placing a backslash (\) at
+the end of each line except the last.
+
+- Each entry has 3 or more fields, separated by whitespace (spaces
+and/or tabs). These fields are, in order:
+ * The name of the machine that is authenticating itself
+ (the "client").
+ * The name of the machine that is authenticating the client
+ (the "server").
+ * The secret to be used for authenticating that client to that
+ server. If this field begins with the at-sign `@', the rest
+ of the field is taken as the name of a file containing the
+ actual secret.
+ * The 4th and any following fields list the IP address(es)
+ that the client may use.
+
+- The file may contain comments, which begin with a `#' and continue
+to the end of the line.
+
+- Double quotes `"' should be used around a field if it contains
+characters with special significance, such as space, tab, `#', etc.
+
+- The backslash `\' may be used before characters with special
+significance (space, tab, `#', `\', etc.) to remove that significance.
+
+Some important points to note:
+
+* A machine can be *both* a "client" and a "server" for the purposes
+of authentication - this happens when both peers require the other to
+authenticate itself. So A would authenticate itself to B, and B would
+also authenticate itself to A (possibly using a different
+authentication protocol).
+
+* If both the "client" and the "server" are running ppp-2.x, they need
+to have a similar entry in the appropriate secrets file; the first two
+fields are *not* swapped on the client, compared to the server. So
+the client might have an entry like this:
+
+ ay bee "our little secret" -
+
+and the corresponding entry on the server could look like this:
+
+ ay bee "our little secret" 123.45.67.89
+
+
+------------------------------------------------------------------------
+
+Q: Explain about PAP and CHAP?
+
+PAP stands for the Password Authentication Protocol. With this
+protocol, the "client" (the machine that needs to authenticate itself)
+sends its name and a password, in clear text, to the "server". The
+server returns a message indicating whether the name and password are
+valid.
+
+CHAP stands for the Challenge Handshake Authentication Protocol. It
+is designed to address some of the deficiencies and vulnerabilities of
+PAP. Like PAP, it is based on the client and server having a shared
+secret, but the secret is never passed in clear text over the link.
+Instead, the server sends a "challenge" - an arbitrary string of
+bytes, and the client must prove it knows the shared secret by
+generating a hash value from the challenge combined with the shared
+secret, and sending the hash value back to the server. The server
+also generates the hash value and compares it with the value received
+from the client.
+
+At a practical level, CHAP can be slightly easier to configure than
+PAP because the server sends its name with the challenge. Thus, when
+finding the appropriate secret in the secrets file, the client knows
+the server's name. In contrast, with PAP, the client has to find its
+password (i.e. the shared secret) before it has received anything from
+the server. Thus, it may be necessary to use the `remotename' option
+to pppd when using PAP authentication so that it can select the
+appropriate secret from /etc/ppp/pap-secrets.
+
+Microsoft also has a variant of CHAP which uses a different hashing
+arrangement from normal CHAP. There is a client-side implementation
+of Microsoft's CHAP in ppp-2.3; see README.MSCHAP80.
+
+
+------------------------------------------------------------------------
+
+Q: When the modem hangs up, without the remote system having
+terminated the connection properly, pppd does not notice the hangup,
+but just keeps running. How do I get pppd to notice the hangup and
+exit?
+
+A: Pppd detects modem hangup by looking for an end-of-file indication
+from the serial driver, which should be generated when the CD (carrier
+detect) signal on the serial port is deasserted. For this to work:
+
+- The modem has to be set to assert CD when the connection is made and
+deassert it when the phone line hangs up. Usually the AT&C1 modem
+command sets this mode.
+
+- The cable from the modem to the serial port must connect the CD
+signal (on pin 8).
+
+- Some serial drivers have a "software carrier detect" mode, which
+must be *disabled*. The method of doing this varies between systems.
+Under SunOS, use the ttysoftcar command. Under NetBSD, edit /etc/ttys
+to remove the "softcar" flag from the line for the serial port, and
+run ttyflags.
+
+
+------------------------------------------------------------------------
+
+Q: Why should I use PPP compression (BSD-Compress or Deflate) when my
+modem already does V.42 compression? Won't it slow the CPU down a
+lot?
+
+A: Using PPP compression is preferable, especially when using modems
+over phone lines, for the following reasons:
+
+- The V.42 compression in the modem isn't very strong - it's an LZW
+technique (same as BSD-Compress) with a 10, 11 or 12 bit code size.
+With BSD-Compress you can use a code size of up to 15 bits and get
+much better compression, or you can use Deflate and get even better
+compression ratios.
+
+- I have found that enabling V.42 compression in my 14.4k modem
+increases the round-trip time for a character to be sent, echoed and
+returned by around 40ms, from 160ms to 200ms (with error correction
+enabled). This is enough to make it feel less responsive on rlogin or
+telnet sessions. Using PPP compression adds less than 5ms (small
+enough that I couldn't measure it reliably). I admit my modem is a
+cheapie and other modems may well perform better.
+
+- While compression and decompression do require some CPU time, they
+reduce the amount of time spent in the serial driver to transmit a
+given amount of data. Many machines require an interrupt for each
+character sent or received, and the interrupt handler can take a
+significant amount of CPU time. So the increase in CPU load isn't as
+great as you might think. My measurements indicate that a system with
+a 33MHz 486 CPU should be able to do Deflate compression for serial
+link speeds of up to 100kb/s or more. It depends somewhat on the type
+of data, of course; for example, when compressing a string of nulls
+with Deflate, it's hard to get a high output data rate from the
+compressor, simply because it compresses strings of nulls so well that
+it has to eat a very large amount of input data to get each byte of
+output.
+
+
+------------------------------------------------------------------------
+
+Q: I get messages saying "Unsupported protocol (...) received". What do
+these mean?
+
+A: If you only get one or two when pppd starts negotiating with the
+peer, they mean that the peer wanted to negotiate some PPP protocol
+that pppd doesn't understand. This doesn't represent a problem, it
+simply means that there is some functionality that the peer supports
+that pppd doesn't, so that functionality can't be used.
+
+If you get them sporadically while the link is operating, or if the
+protocol numbers (in parentheses) don't correspond to any valid PPP
+protocol that the peer might be using, then the problem is probably
+that characters are getting corrupted on the receive side, or that
+extra characters are being inserted into the receive stream somehow.
+If this is happening, most packets that get corrupted should get
+discarded by the FCS (Frame Check Sequence, a 16-bit CRC) check, but a
+small number may get through.
+
+One possibility may be that you are receiving broadcast messages on
+the remote system which are being sent over your serial link. Another
+possibility is that your modem is set for XON/XOFF (software) flow
+control and is inserting ^Q and ^S characters into the receive data
+stream.
+
+
+------------------------------------------------------------------------
+
+Q: I get messages saying "Protocol-Reject for unsupported protocol ...".
+What do these mean?
+
+A: This is the other side of the previous question. If characters are
+getting corrupted on the way to the peer, or if your system is
+inserting extra bogus characters into the transmit data stream, the
+peer may send protocol-reject messages to you, resulting in the above
+message (since your pppd doesn't recognize the protocol number
+either.)
+
+
+------------------------------------------------------------------------
+
+Q: I get a message saying something like "ioctl(TIOCSETD): Operation
+not permitted". How do I fix this?
+
+A: This is because pppd is not running as root. If you have not
+installed pppd setuid-root, you will have to be root to run it. If
+you have installed pppd setuid-root and you still get this message, it
+is probably because your shell is using some other copy of pppd than
+the installed one - for example, if you are in the pppd directory
+where you've just built pppd and your $PATH has . before /usr/sbin (or
+wherever pppd gets installed).
+
+
+------------------------------------------------------------------------
+
+Q: Has your package been ported to HP/UX or IRIX or AIX?
+
+A: No. I don't have access to systems running HP/UX or AIX. No-one
+has volunteered to port it to HP/UX. I had someone who did a port for
+AIX 4.x, but who is no longer able to maintain it. And apparently AIX
+3.x is quite different, so it would need a separate port.
+
+IRIX includes a good PPP implementation in the standard distribution,
+as far as I know.
+
+
+------------------------------------------------------------------------
+
+Q: Under SunOS 4, when I try to modload the ppp modules, I get the
+message "can't open /dev/vd: No such device".
+
+A: First check in /dev that there is an entry like this:
+
+crw-r--r-- 1 root 57, 0 Oct 2 1991 vd
+
+If not, make one (mknod /dev/vd c 57 0). If the problem still exists,
+probably your kernel has been configured without the vd driver
+included. The vd driver is needed for loadable module support.
+
+First, identify the config file that was used. When you boot your
+machine, or if you run /etc/dmesg, you'll see a line that looks
+something like this:
+
+SunOS Release 4.1.3_U1 (CAP_XBOX) #7: Thu Mar 21 15:31:56 EST 1996
+ ^^^^^^^^
+ this is the config file name
+
+The config file will be in the /sys/`arch -k`/conf directory (arch -k
+should return sun4m for a SparcStation 10, sun3x for a Sun 3/80,
+etc.). Look in there for a line saying "options VDDRV". If that line
+isn't present (or is commented out), add it (or uncomment it).
+
+You then need to rebuild the kernel as described in the SunOS
+manuals. Basically you need to run config and make like this:
+
+ /usr/etc/config CAP_XBOX
+ cd ../CAP_XBOX
+ make
+
+(replacing the string CAP_XBOX by the name of the config file for your
+kernel, of course).
+
+Then copy the new kernel to /:
+
+ mv /vmunix /vmunix.working
+ cp vmunix /
+
+and reboot. Modload should then work.
+
+
+------------------------------------------------------------------------
+
+Q: I'm running Linux (or NetBSD or FreeBSD), and my system comes with
+PPP already. Should I consider installing this package? Why?
+
+A: The PPP that is already installed in your system is (or is derived
+from) some version of this PPP package. You can find out what version
+of this package is already installed with the command "pppd --help".
+If this is older than the latest version, you may wish to install the
+latest version so that you can take advantage of the new features or
+bug fixes.
+
+
+------------------------------------------------------------------------
+
+Q: I'm running pppd in demand mode, and I find that pppd often dials
+out unnecessarily when I try to make a connection within my local
+machine or with a machine on my local LAN. What can I do about this?
+
+A: Very often the cause of this is that a program is trying to contact
+a nameserver to resolve a hostname, and the nameserver (specified in
+/etc/resolv.conf, usually) is on the far side of the ppp link. You
+can try executing a command such as `ping myhost' (where myhost is the
+name of the local machine, or some other machine on a local LAN), to
+see whether that starts the ppp link. If it does, check the setup of
+your /etc/hosts file to make sure you have the local machine and any
+hosts on your local LAN listed, and /etc/resolv.conf and/or
+/etc/nsswitch.conf files to make sure you resolve hostnames from
+/etc/hosts if possible before trying to contact a nameserver.
+
+
+------------------------------------------------------------------------
+
+Q: Since I installed ppp-2.3.6, dialin users to my server have been
+getting this message when they run pppd:
+
+peer authentication required but no suitable secret(s) found for
+authenticating any peer to us (ispserver)
+
+A: In 2.3.6, the default is to let an unauthenticated peer only use IP
+addresses to which the machine doesn't already have a route. So on a
+machine with a default route, everyone has to authenticate. If you
+really don't want that, you can put `noauth' in the /etc/ppp/options
+file. Note that there is then no check on who is using which IP
+address. IMHO, this is undesirably insecure, but I guess it may be
+tolerable as long as you don't use any .rhosts files or anything like
+that. I recommend that you require dialin users to authenticate, even
+if just with PAP using their login password (using the `login' option
+to pppd). If you do use `noauth', you should at least have a pppusers
+group and set the permissions on pppd to allow only user and group to
+execute it.
+
+------------------------------------------------------------------------
+
+Q: When running pppd as a dial-in server, I often get the message
+"LCP: timeout sending Config-Requests" from pppd. It seems to be
+random, but dial-out always works fine. What is wrong?
+
+A: Most modern modems auto-detects the speed of the serial line
+between the modem and the computer. This auto-detection occurs when
+the computer sends characters to the modem, when the modem is in
+command mode. It does not occur when the modem is in data mode.
+Thus, if you send commands to the modem at 2400 bps, and then change
+the serial port speed to 115200 bps, the modem will not detect this
+change until something is transmitted from the computer to the modem.
+When running pppd in dial-in mode (i.e. without a connect script),
+pppd sets the speed of the serial port, but does not transmit
+anything. If the modem was already running at the specified speed,
+everything is fine, but if not, you will just receive garbage from the
+modem. To cure this, use an init script such as the following:
+
+ pppd ttyS0 115200 modem crtscts init "chat '' AT OK"
+
+To reset the modem and enable auto-answer, use:
+
+ pppd ttyS0 115200 modem crtscts init "chat '' ATZ OK ATS0=1 OK"
diff --git a/mdk-stage1/ppp/PLUGINS b/mdk-stage1/ppp/PLUGINS
new file mode 100644
index 000000000..0eeabe249
--- /dev/null
+++ b/mdk-stage1/ppp/PLUGINS
@@ -0,0 +1,131 @@
+Starting with version 2.3.10, pppd includes support for `plugins' -
+pieces of code which can be loaded into pppd at runtime and which can
+affect its behaviour in various ways. The idea of plugins is to
+provide a way for people to customize the behaviour of pppd without
+having to either apply local patches to each version or get their
+patches accepted into the standard distribution. My aim is that
+plugins will be able to be used with successive versions of pppd
+without needing to recompile the plugins.
+
+A plugin is a standard shared library object, typically with a name
+ending in .so. They are loaded using the standard dlopen() library
+call, so plugins are only supported on systems which support shared
+libraries and the dlopen call. At present pppd is compiled with
+plugin support only under Linux and Solaris.
+
+Plugins are loaded into pppd using the `plugin' option, which takes
+one argument, the name of a shared object file. The plugin option is
+a privileged option. I suggest that you give the full path name of
+the shared object file; if you don't, it may be possible for
+unscrupulous users to substitute another shared object file for the
+one you mean to load, e.g. by setting the LD_LIBRARY_PATH variable.
+
+Plugins are usually written in C and compiled and linked to a shared
+object file in the appropriate manner for your platform. Using gcc
+under Linux, a plugin called `xyz' could be compiled and linked with
+the following commands:
+
+ gcc -c -O xyz.c
+ gcc -shared -o xyz.so xyz.o
+
+There are some example plugins in the pppd/plugins directory in the
+ppp distribution. Currently there is one example, minconn.c, which
+implements a `minconnect' option, which specifies a minimum connect
+time before the idle timeout applies.
+
+Plugins can access global variables within pppd, so it is useful for
+them to #include "pppd.h" from the pppd source directory.
+
+Every plugin must contain a global procedure called `plugin_init'.
+This procedure will get called (with no arguments) immediately after
+the plugin is loaded.
+
+Plugins can affect the behaviour of pppd in at least three ways:
+
+1. They can add extra options which pppd will then recognize. This is
+ done by calling the add_options() procedure with a pointer to an
+ array of option_t structures. The last entry in the array must
+ have its name field set to NULL.
+
+2. Pppd contains `hook' variables which are procedure pointers. If a
+ given hook is not NULL, pppd will call the procedure it points to
+ at the appropriate point in its processing. The plugin can set any
+ of these hooks to point to its own procedures. See below for a
+ description of the hooks which are currently implemented.
+
+3. Plugin code can call any global procedures and access any global
+ variables in pppd.
+
+Here is a list of the currently implemented hooks in pppd.
+
+
+int (*idle_time_hook)(struct ppp_idle *idlep);
+
+The idle_time_hook is called when the link first comes up (i.e. when
+the first network protocol comes up) and at intervals thereafter. On
+the first call, the idlep parameter is NULL, and the return value is
+the number of seconds before pppd should check the link activity, or 0
+if there is to be no idle timeout.
+
+On subsequent calls, idlep points to a structure giving the number of
+seconds since the last packets were sent and received. If the return
+value is > 0, pppd will wait that many seconds before checking again.
+If it is <= 0, that indicates that the link should be terminated due
+to lack of activity.
+
+
+int (*holdoff_hook)(void);
+
+The holdoff_hook is called when an attempt to bring up the link fails,
+or the link is terminated, and the persist or demand option was used.
+It returns the number of seconds that pppd should wait before trying
+to reestablish the link (0 means immediately).
+
+
+int (*pap_check_hook)(void);
+int (*pap_passwd_hook)(char *user, char *passwd);
+int (*pap_auth_hook)(char *user, int userlen,
+ char *passwd, int passlen,
+ char **msgp, int *msglenp,
+ struct wordlist **paddrs,
+ struct wordlist **popts);
+
+These hooks are designed to allow a plugin to replace the normal PAP
+password processing in pppd with something different (e.g. contacting
+an external server).
+
+The pap_check_hook is called to check whether there is any possibility
+that the peer could authenticate itself to us. If it returns 1, pppd
+will ask the peer to authenticate itself. If it returns 0, pppd will
+not ask the peer to authenticate itself (but if authentication is
+required, pppd may exit, or terminate the link before network protocol
+negotiation). If it returns -1, pppd will look in the pap-secrets
+file as it would normally.
+
+The pap_passwd_hook is called to determine what username and password
+pppd should use in authenticating itself to the peer with PAP. The
+user string will already be initialized, by the `user' option, the
+`name' option, or from the hostname, but can be changed if necessary.
+MAXNAMELEN bytes of space are available at *user, and MAXSECRETLEN
+bytes of space at *passwd. If this hook returns 0, pppd will use the
+values at *user and *passwd; if it returns -1, pppd will look in the
+pap-secrets file, or use the value from the +ua or password option, as
+it would normally.
+
+The pap_auth_hook is called to determine whether the username and
+password supplied by the peer are valid. user and passwd point to
+null-terminated strings containing the username and password supplied
+by the peer, with non-printable characters converted to a printable
+form. The pap_auth_hook function should set msg to a string to be
+returned to the peer and return 1 if the username/password was valid
+and 0 if not. If the hook returns -1, pppd will look in the
+pap-secrets file as usual.
+
+If the username/password was valid, the hook can set *paddrs to point
+to a wordlist containing the IP address(es) which the peer is
+permitted to use, formatted as in the pap-secrets file. It can also
+set *popts to a wordlist containing any extra options for this user
+which pppd should apply at this point.
+
+
+## $Id$ ##
diff --git a/mdk-stage1/ppp/README b/mdk-stage1/ppp/README
new file mode 100644
index 000000000..aa1e5f9c2
--- /dev/null
+++ b/mdk-stage1/ppp/README
@@ -0,0 +1,168 @@
+This is the README file for ppp-2.4, a package which implements the
+Point-to-Point Protocol (PPP) to provide Internet connections over
+serial lines.
+
+
+Introduction.
+*************
+
+The Point-to-Point Protocol (PPP) provides a standard way to establish
+a network connection over a serial link. At present, this package
+supports IP and the protocols layered above IP, such as TCP and UDP.
+The Linux and Solaris ports of this package have optional support for
+IPV6; the Linux port of this package also has support for IPX.
+
+This software consists of two parts:
+
+- Kernel code, which establishes a network interface and passes
+packets between the serial port, the kernel networking code and the
+PPP daemon (pppd). This code is implemented using STREAMS modules on
+SunOS 4.x and Solaris, and as a line discipline under Linux and FreeBSD.
+
+- The PPP daemon (pppd), which negotiates with the peer to establish
+the link and sets up the ppp network interface. Pppd includes support
+for authentication, so you can control which other systems may make a
+PPP connection and what IP addresses they may use.
+
+The primary platforms supported by this package are Linux and Solaris.
+Code for SunOS 4.x is included here but is largely untested. I have
+code for NeXTStep, FreeBSD, SVR4, Tru64 (Digital Unix), AIX and Ultrix
+but no active maintainers for these platforms. Code for all of these
+except AIX is included in the ppp-2.3.11 release.
+
+
+Installation.
+*************
+
+The file SETUP contains general information about setting up your
+system for using PPP. There is also a README file for each supported
+system, which contains more specific details for installing PPP on
+that system. The supported systems, and the corresponding README
+files, are:
+
+ Linux README.linux
+ Solaris 2 README.sol2
+ SunOS 4.x README.sunos4
+
+In each case you start by running the ./configure script. This works
+out which operating system you are using and creates symbolic links to
+the appropriate makefiles. You then run `make' to compile the
+user-level code, and (as root) `make install' to install the
+user-level programs pppd, chat and pppstats.
+
+N.B. Since 2.3.0, leaving the permitted IP addresses column of the
+pap-secrets or chap-secrets file empty means that no addresses are
+permitted. You need to put a "*" in that column to allow the peer to
+use any IP address. (This only applies where the peer is
+authenticating itself to you, of course.)
+
+
+What's new in ppp-2.4.1.
+************************
+
+* Pppd can now print out the set of options that are in effect. The
+ new `dump' option causes pppd to print out the option values after
+ option parsing is complete. The `dryrun' option causes pppd to
+ print the options and then exit.
+
+* The option parsing code has been fixed so that options in the
+ per-tty options file are parsed correctly, and don't override values
+ from the command line in most cases.
+
+* The plugin option now looks in /usr/lib/pppd/<pppd-version> (for
+ example, /usr/lib/pppd/2.4.1b1) for shared objects for plugins if
+ there is no slash in the plugin name.
+
+* When loading a plugin, pppd will now check the version of pppd for
+ which the plugin was compiled, and refuse to load it if it is
+ different to pppd's version string. To enable this, the plugin
+ source needs to #include "pppd.h" and have a line saying:
+ char pppd_version[] = VERSION;
+
+* There is a bug in zlib, discovered by James Carlson, which can cause
+ kernel memory corruption if Deflate is used with the lowest setting,
+ 8. As a workaround pppd will now insist on using at least 9.
+
+* Pppd should compile on Solaris and SunOS again.
+
+* Pppd should now set the MTU correctly on demand-dialled interfaces.
+
+
+What was new in ppp-2.4.0.
+**************************
+
+* Multilink: this package now allows you to combine multiple serial
+ links into one logical link or `bundle', for increased bandwidth and
+ reduced latency. This is currently only supported under the
+ Linux-2.3.99pre5 or later kernels.
+
+* All the pppd processes running on a system now write information
+ into a common database. I used the `tdb' code from samba for this.
+
+* New hooks have been added.
+
+For a list of the changes made during the 2.3 series releases of this
+package, see the Changes-2.3 file.
+
+
+Compression methods.
+********************
+
+This package supports two packet compression methods: Deflate and
+BSD-Compress. Other compression methods which are in common use
+include Predictor, LZS, and MPPC. These methods are not supported for
+two reasons - they are patent-encumbered, and they cause some packets
+to expand slightly, which pppd doesn't currently allow for.
+BSD-Compress is also patent-encumbered (its inclusion in this package
+can be considered a historical anomaly :-) but it doesn't ever expand
+packets. Neither does Deflate, which uses the same algorithm as gzip.
+
+
+Patents.
+********
+
+The BSD-Compress algorithm used for packet compression is the same as
+that used in the Unix "compress" command. It is apparently covered by
+U.S. patents 4,814,746 (owned by IBM) and 4,558,302 (owned by Unisys),
+and corresponding patents in various other countries (but not
+Australia). If this is of concern, you can build the package without
+including BSD-Compress. To do this, edit net/ppp-comp.h to change the
+definition of DO_BSD_COMPRESS to 0. The bsd-comp.c files are then no
+longer needed, so the references to bsd-comp.o may optionally be
+removed from the Makefiles.
+
+
+Contacts.
+*********
+
+The comp.protocols.ppp newsgroup is a useful place to get help if you
+have trouble getting your ppp connections to work. Please do not send
+me questions of the form "please help me get connected to my ISP" -
+I'm sorry, but I simply do not have the time to answer all the
+questions like this that I get.
+
+If you find bugs in this package, please report them to the maintainer
+for the port for the operating system you are using:
+
+Linux Paul Mackerras <paulus@linuxcare.com>
+Solaris 2 James Carlson <james.d.carlson@east.sun.com>
+SunOS 4.x Adi Masputra <adi.masputra@sun.com>
+
+
+Copyrights:
+***********
+
+All of the code can be freely used and redistributed. The individual
+source files each have their own copyright and permission notice; some
+have a BSD-style notice and some are under the GPL.
+
+
+Distribution:
+*************
+
+The primary site for releases of this software is:
+
+ ftp://linuxcare.com.au/pub/ppp/
+
+
+($Id$)
diff --git a/mdk-stage1/ppp/README.MSCHAP80 b/mdk-stage1/ppp/README.MSCHAP80
new file mode 100644
index 000000000..d3ed291b7
--- /dev/null
+++ b/mdk-stage1/ppp/README.MSCHAP80
@@ -0,0 +1,284 @@
+PPP Client Support for Microsoft's CHAP-80
+==========================================
+
+Eric Rosenquist rosenqui@strataware.com
+(updated by Paul Mackerras)
+(updated by Al Longyear)
+(updated by Farrell Woods)
+
+INTRODUCTION
+
+Microsoft has introduced an extension to the Challenge/Handshake
+Authentication Protocol (CHAP) which avoids storing cleartext
+passwords on a server. (Unfortunately, this is not as secure as it
+sounds, because the encrypted password stored on a server can be used
+by a bogus client to gain access to the server just as easily as if
+the password were stored in cleartext.) The details of the Microsoft
+extensions can be found in the document:
+
+ <ftp://ftp.microsoft.com/developr/rfc/chapexts.txt>
+
+In short, MS-CHAP is identified as <auth chap 80> since the hex value
+of 80 is used to designate Microsoft's scheme. Standard PPP CHAP uses
+a value of 5. If you enable PPP debugging with the "debug" option and
+see something like the following in your logs, the remote server is
+requesting MS-CHAP:
+
+ rcvd [LCP ConfReq id=0x2 <asyncmap 0x0> <auth chap 80> <magic 0x46a3>]
+ ^^^^^^^^^^^^
+
+The standard pppd implementation will indicate its lack of support for
+MS-CHAP by NAKing it:
+
+ sent [LCP ConfNak id=0x2 <auth chap 05>]
+
+Windows NT Server systems are often configured to "Accept only
+Microsoft Authentication" (this is intended to enhance security). Up
+until now, that meant that you couldn't use this version of PPPD to
+connect to such a system. I've managed to get a client-only
+implementation of MS-CHAP working; it will authenticate itself to
+another system using MS-CHAP, but if you're using PPPD as a dial-in
+server, you won't be able to use MS-CHAP to authenticate the clients.
+This would not be a lot of extra work given that the framework is in
+place, but I didn't need it myself so I didn't implement it.
+
+
+BUILDING THE PPPD
+
+MS-CHAP uses a combination of MD4 hashing and DES encryption for
+authentication. You may need to get Eric Young's libdes library in
+order to use my MS-CHAP extensions. A lot of UNIX systems already
+have DES encryption available via the crypt(3), encrypt(3) and
+setkey(3) interfaces. Some may (such as that on Digital UNIX)
+provide only the encryption mechanism and will not perform
+decryption. This is okay. We only need to encrypt to perform
+MS-CHAP authentication.
+
+If you have encrypt/setkey available, then hopefully you need only
+define these two things in your Makefile: -DUSE_CRYPT and -DCHAPMS.
+Skip the paragraphs below about obtaining and building libdes. Do
+the "make clean" and "make" as described below. Linux users
+should not need to modify their Makefiles. Instead,
+just do "make CHAPMS=1 USE_CRYPT=1".
+
+If you don't have encrypt and setkey, you will need Eric Young's
+libdes library. You can find it in:
+
+ftp://ftp.funet.fi/pub/crypt/mirrors/ftp.psy.uq.oz.au/DES/libdes-3.06.tar.gz
+
+Australian residents can get libdes from Eric Young's site:
+
+ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/libdes-3.06.tar.gz
+
+It is also available on many other sites (ask Archie).
+
+I used libdes-3.06, but hopefully anything newer than that will work
+also. Get the library, build and test it on your system, and install
+it somewhere (typically /usr/local/lib and /usr/local/include).
+
+
+
+You should now be ready to (re)compile the PPPD. Go to the pppd
+subdirectory and make sure the Makefile contains "-DCHAPMS" in the
+CFLAGS or COMPILE_FLAGS macro, and that the LIBS macro (or LDADD for
+BSD systems) contains "-ldes". Depending on your system and where the
+DES library was installed, you may also need to alter the include and
+library paths used by your compiler.
+
+Do a "make clean" and then a "make" to rebuild pppd. Assuming all
+goes well, install the new pppd and move on to the CONFIGURATION
+section.
+
+
+CONFIGURATION
+
+If you've never used PPPD with CHAP before, read the man page (type
+"man pppd") and read the description in there. Basically, you need to
+edit the "chap-secrets" file typically named /etc/ppp/chap-secrets.
+This should contain the following two lines for each system with which
+you use CHAP (with no leading blanks):
+
+ RemoteHost Account Secret
+ Account RemoteHost Secret
+
+Note that you need both lines and that item 1 and 2 are swapped in the
+second line. I'm not sure why you need it twice, but it works and I didn't
+have time to look into it further. The "RemoteHost" is a somewhat
+arbitrary name for the remote Windows NT system you're dialing. It doesn't
+have to match the NT system's name, but it *does* have to match what you
+use with the "remotename" parameter. The "Account" is the Windows NT
+account name you have been told to use when dialing, and the "Secret" is
+the password for that account. For example, if your service provider calls
+their machine "DialupNT" and tells you your account and password are
+"customer47" and "foobar", add the following to your chap-secrets file:
+
+ DialupNT customer47 foobar
+ customer47 DialupNT foobar
+
+The only other thing you need to do for MS-CHAP (compared to normal CHAP)
+is to always use the "remotename" option, either on the command line or in
+your "options" file (see the pppd man page for details). In the case of
+the above example, you would need to use the following command line:
+
+ pppd name customer47 remotename DialupNT <other options>
+
+or add:
+
+ name customer47
+ remotename DialupNT
+
+to your PPPD "options" file.
+
+The "remotename" option is required for MS-CHAP since Microsoft PPP servers
+don't send their system name in the CHAP challenge packet.
+
+
+E=691 (AUTHENTICATION_FAILURE) ERRORS WHEN YOU HAVE THE VALID SECRET (PASSWORD)
+
+If your RAS server is not the domain controller and is not a 'stand-alone'
+server then it must make a query to the domain controller for your domain.
+
+You need to specify the domain name with the user name when you attempt to
+use this type of a configuration. The domain name is specified with the
+local name in the chap-secrets file and with the option for the 'name'
+parameter.
+
+For example, the previous example would become:
+
+ DialupNT domain\\customer47 foobar
+ domain\\customer47 DialupNT foobar
+
+and
+
+ pppd name 'domain\\customer47' remotename DialupNT <other options>
+
+or add:
+
+ name domain\\customer47
+ remotename DialupNT
+
+when the Windows NT domain name is simply called 'domain'.
+
+
+TROUBLESHOOTING
+
+Assuming that everything else has been configured correctly for PPP and
+CHAP, the MS-CHAP-specific problems you're likely to encounter are mostly
+related to your Windows NT account and its settings. A Microsoft server
+returns error codes in its CHAP response. The following are extracted from
+Microsoft's "chapexts.txt" file referenced above:
+
+ 646 ERROR_RESTRICTED_LOGON_HOURS
+ 647 ERROR_ACCT_DISABLED
+ 648 ERROR_PASSWD_EXPIRED
+ 649 ERROR_NO_DIALIN_PERMISSION
+ 691 ERROR_AUTHENTICATION_FAILURE
+ 709 ERROR_CHANGING_PASSWORD
+
+You'll see these in your pppd log as a line similar to:
+
+ Remote message: E=649 R=0
+
+The "E=" is the error number from the table above, and the "R=" flag
+indicates whether the error is transient and the client should retry. If
+you consistently get error 691, then either you're using the wrong account
+name/password, or the DES library or MD4 hashing (in md4.c) aren't working
+properly. Verify your account name and password (use a Windows NT or
+Windows 95 system to dial-in if you have one available). If that checks
+out, test the DES library with the "destest" program included with the DES
+library. If DES checks out, the md4.c routines are probably failing
+(system byte ordering may be a problem) or my code is screwing up. I've
+only got access to a Linux system, so you're on your own for anything else.
+
+Another thing that might cause problems is that some RAS servers won't
+respond at all to LCP config requests without seeing the word "CLIENT"
+from the other end. If you see pppd sending out LCP config requests
+without getting any reply, try putting something in your chat script
+to send the word CLIENT after the modem has connected.
+
+If everything compiles cleanly, but fails at authentication time, then
+it might be a case of the MD4 or DES code screwing up. The following
+small program can be used to test the MS-CHAP code to see if it
+produces a known response:
+
+-----------------
+#include <stdio.h>
+
+#include "pppd.h"
+#include "chap.h"
+#include "chap_ms.h"
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ u_char challenge[8];
+ int challengeInt[sizeof(challenge)];
+ chap_state cstate;
+ int i;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s <16-hexchar challenge> <password>\n",
+ argv[0]); exit(1);
+ }
+
+ sscanf(argv[1], "%2x%2x%2x%2x%2x%2x%2x%2x",
+ challengeInt + 0, challengeInt + 1, challengeInt + 2,
+ challengeInt + 3, challengeInt + 4, challengeInt + 5,
+ challengeInt + 6, challengeInt + 7);
+
+ for (i = 0; i < sizeof(challenge); i++)
+ challenge[i] = (u_char)challengeInt[i];
+
+ ChapMS(&cstate, challenge, sizeof(challenge), argv[2], strlen(argv[2]));
+ printf("Response length is %d, response is:", cstate.resp_length);
+
+ for (i = 0; i < cstate.resp_length; i++) {
+ if (i % 8 == 0)
+ putchar('\n');
+ printf("%02X ", (unsigned int)cstate.response[i]);
+ }
+
+ putchar('\n');
+
+ exit(0);
+}
+-------------
+
+This needs to link against chap_ms.o, md4.o, and the DES library. When
+you run it with the command line:
+
+ $ testchap 00000000000000000000000000000000 hello
+
+it should output the following:
+
+ Response length is 49, response is:
+ 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00
+ F4 D9 9D AF 82 64 DC 3C
+ 53 F9 BC 92 14 B5 5D 9E
+ 78 C4 21 48 9D B7 A8 B4
+ 01
+
+if not, then either the DES library is not working, the MD4 code isn't
+working, or there are some problems with the port of the code in
+chap_ms.c.
+
+
+STILL TO DO
+
+A site using only MS-CHAP to authenticate has no need to store cleartext
+passwords in the "chap-secrets" file. A utility that spits out the ASCII
+hex MD4 hash of a given password would be nice, and would allow that hash
+to be used in chap-secrets in place of the password. The code to do this
+could quite easily be lifted from chap_ms.c (you have to convert the
+password to Unicode before hashing it). The chap_ms.c file would also have
+to be changed to recognize a password hash (16 binary bytes == 32 ASCII hex
+characters) and skip the hashing stage.
+
+A server implementation would allow MS-CHAP to be used with Windows NT and
+Windows 95 clients for enhanced security. Some new command-line options
+would be required, as would code to generate the Challenge packet and
+verify the response. Most of the helper functions are in place, so this
+shouldn't be too hard for someone to add.
diff --git a/mdk-stage1/ppp/README.cbcp b/mdk-stage1/ppp/README.cbcp
new file mode 100644
index 000000000..6f7b7685e
--- /dev/null
+++ b/mdk-stage1/ppp/README.cbcp
@@ -0,0 +1,97 @@
+ Microsoft Call Back Configuration Protocol.
+ by Pedro Roque Marques
+ (updated by Paul Mackerras)
+
+The CBCP is a method by which the Microsoft Windows NT Server may
+implement additional security. It is possible to configure the server
+in such a manner so as to require that the client systems which
+connect with it are required that following a valid authentication to
+leave a method by which the number may be returned call.
+
+It is a requirement of servers so configured that the protocol be
+exchanged.
+
+So, this set of patches may be applied to the pppd process to enable
+the cbcp client *only* portion of the specification. It is primarily
+meant to permit connection with Windows NT Servers.
+
+The ietf-working specification may be obtained from ftp.microsoft.com
+in the developr/rfc directory.
+
+The ietf task group has decided to recommend that the LCP sequence be
+extended to permit the callback operation. For this reason, these
+patches are not 'part' of pppd but are an adjunct to the code.
+
+To enable CBCP support, all that is required is to change the
+appropriate Makefile in the pppd subdirectory to add "-DCBCP_SUPPORT"
+to the CFLAGS definition and add cbcp.o to the list of object files,
+and then recompile pppd. The patch below does this for Makefile.bsd
+and Makefile.linux.
+
+
+--------------------------------cut here-------------------------------
+diff -r -c ppp-2.3.orig/pppd/Makefile.bsd ppp-2.3/pppd/Makefile.bsd
+*** ppp-2.3.orig/pppd/Makefile.bsd Tue Oct 8 13:33:33 1996
+--- ppp-2.3/pppd/Makefile.bsd Fri Apr 11 23:59:15 1997
+***************
+*** 4,14 ****
+ # -D_BITYPES is for FreeBSD, which doesn't define anything to
+ # tell us that u_int32_t gets defined if <sys/types.h> is included.
+ # Remove for older *BSD systems for which this isn't true.
+! CFLAGS+= -g -I.. -DHAVE_PATHS_H -D_BITYPES
+
+ PROG= pppd
+ SRCS= main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
+! demand.c auth.c options.c sys-bsd.c
+ MAN= pppd.cat8
+ MAN8= pppd.8
+ BINMODE=4555
+--- 4,14 ----
+ # -D_BITYPES is for FreeBSD, which doesn't define anything to
+ # tell us that u_int32_t gets defined if <sys/types.h> is included.
+ # Remove for older *BSD systems for which this isn't true.
+! CFLAGS+= -I.. -DHAVE_PATHS_H -D_BITYPES -DCBCP_SUPPORT
+
+ PROG= pppd
+ SRCS= main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
+! demand.c auth.c options.c sys-bsd.c cbcp.c
+ MAN= pppd.cat8
+ MAN8= pppd.8
+ BINMODE=4555
+diff -r -c ppp-2.3.orig/pppd/Makefile.linux ppp-2.3/pppd/Makefile.linux
+*** ppp-2.3.orig/pppd/Makefile.linux Tue Oct 8 15:42:41 1996
+--- ppp-2.3/pppd/Makefile.linux Sat Apr 12 00:02:28 1997
+***************
+*** 14,20 ****
+ ipxcp.h cbcp.h
+ MANPAGES = pppd.8
+ PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
+! auth.o options.o demand.o sys-linux.o ipxcp.o
+
+ all: pppd
+
+--- 14,20 ----
+ ipxcp.h cbcp.h
+ MANPAGES = pppd.8
+ PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
+! auth.o options.o demand.o sys-linux.o ipxcp.o cbcp.o
+
+ all: pppd
+
+***************
+*** 36,42 ****
+ #INCLUDE_DIRS= -I/usr/include -I..
+ INCLUDE_DIRS=
+
+! COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE
+
+ CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
+
+--- 36,42 ----
+ #INCLUDE_DIRS= -I/usr/include -I..
+ INCLUDE_DIRS=
+
+! COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DCBCP_SUPPORT
+
+ CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
+
diff --git a/mdk-stage1/ppp/README.linux b/mdk-stage1/ppp/README.linux
new file mode 100644
index 000000000..62ed9ca7f
--- /dev/null
+++ b/mdk-stage1/ppp/README.linux
@@ -0,0 +1,297 @@
+ PPP for Linux
+ -------------
+
+ Paul Mackerras
+ 8 March 2001
+
+ for ppp-2.4.1
+
+1. Introduction
+---------------
+
+The Linux PPP implementation includes both kernel and user-level
+parts. This package contains the user-level part, which consists of
+the PPP daemon (pppd) and associated utilities. In the past this
+package has contained updated kernel drivers. This is no longer
+necessary, as the current 2.2 and 2.4 kernel sources contain
+up-to-date drivers.
+
+The Linux PPP implementation is capable of being used both for
+initiating PPP connections (as a `client') or for handling incoming
+PPP connections (as a `server'). Note that this is an operational
+distinction, based on how the connection is created, rather than a
+distinction that is made in the PPP protocols themselves.
+
+Mostly this package is used for PPP connections over modems connected
+via asynchronous serial ports, so this guide concentrates on this
+situation.
+
+The PPP protocol consists of two parts. One is a scheme for framing
+and encoding packets, the other is a series of protocols called LCP,
+IPCP, PAP and CHAP, for negotiating link options and for
+authentication. This package similarly consists of two parts: a
+kernel module which handles PPP's low-level framing protocol, and a
+user-level program called pppd which implements PPP's negotiation
+protocols.
+
+The kernel module assembles/disassembles PPP frames, handles error
+detection, and forwards packets between the serial port and either the
+kernel network code or the user-level program pppd. IP packets go
+directly to the kernel network code. So once pppd has negotiated the
+link, it in practice lies completely dormant until you want to take
+the link down, when it negotiates a graceful disconnect.
+
+
+2. Installation
+---------------
+
+2.1 Kernel driver
+
+Assuming you are running a recent 2.2 or 2.4 (or later) series kernel,
+the kernel source code will contain an up-to-date kernel PPP driver.
+If the PPP driver was included in your kernel configuration when your
+kernel was built, then you only need to install the user-level
+programs. Otherwise you will need to get the source tree for your
+kernel version, configure it with PPP included, and recompile. Most
+Linux distribution vendors ship kernels with PPP included in the
+configuration.
+
+The PPP driver can be either compiled into the kernel or compiled as a
+kernel module. If it is compiled into the kernel, the PPP driver is
+included in the kernel image which is loaded at boot time. If it is
+compiled as a module, the PPP driver is present in one or more files
+under /lib/modules and is loaded into the kernel when needed.
+
+The 2.2 series kernels contain an older version of the kernel PPP
+driver, one which doesn't support multilink. If you want multilink,
+you need to run the latest 2.4 series kernel. The kernel PPP driver
+was completely rewritten for the 2.4 series kernels to support
+multilink and to allow it to operate over diverse kinds of
+communication medium (the 2.2 driver only operates over serial ports
+and devices which look like serial ports, such as pseudo-ttys).
+
+Under the 2.2 kernels, if PPP is compiled as a module, the PPP driver
+modules should be present in the /lib/modules/`uname -r`/net directory
+(where `uname -r` represents the kernel version number). The PPP
+driver module itself is called ppp.o, and there will usually be
+compression modules there, ppp_deflate.o and bsd_comp.o, as well as
+slhc.o, which handles TCP/IP header compression. If the PPP driver is
+compiled into the kernel, the compression code will still be compiled
+as modules, for kernels before 2.2.17pre12. For 2.2.17pre12 and later,
+if the PPP driver is compiled in, the compression code will also.
+
+Under the 2.4 kernels, there are two PPP modules, ppp_generic.o and
+ppp_async.o, plus the compression modules (ppp_deflate.o, bsd_comp.o
+and slhc.o). If the PPP generic driver is compiled into the kernel,
+the other four can then be present either as modules or compiled into
+the kernel. There is a sixth module, ppp_synctty.o, which is used for
+synchronous tty devices such as high-speed WAN adaptors.
+
+
+2.2 User-level programs
+
+If you obtained this package in .rpm or .deb format, you simply follow
+the usual procedure for installing the package.
+
+If you are using the .tar.gz form of this package, then cd into the
+ppp-2.4.1b1 directory you obtained by unpacking the archive and issue
+the following commands:
+
+$ ./configure
+$ make
+# make install
+
+The `make install' has to be done as root. This makes and installs
+four programs and their man pages: pppd, chat, pppstats and pppdump.
+If the /etc/ppp configuration directory doesn't exist, the `make
+install' step will create it and install some default configuration
+files.
+
+
+2.3 System setup for 2.4 kernels
+
+Under the 2.4 series kernels, pppd needs to be able to open /dev/ppp,
+character device (108,0). If you are using devfs (the device
+filesystem), the /dev/ppp node will automagically appear when the
+ppp_generic module is loaded, or at startup if ppp_generic is compiled
+in.
+
+If you have ppp_generic as a module, and you are using devfsd (the
+devfs daemon), you will need to add a line like this to your
+/etc/devfsd.conf:
+
+LOOKUP ppp MODLOAD
+
+Otherwise you will need to create a /dev/ppp device node with the
+commands:
+
+# mknod /dev/ppp c 108 0
+# chmod 600 /dev/ppp
+
+If you use module autoloading and have PPP as a module, you will need
+to add the following to your /etc/modules.conf or /etc/conf.modules:
+
+alias /dev/ppp ppp_generic
+alias char-major-108 ppp_generic
+alias tty-ldisc-3 ppp_async
+alias tty-ldisc-14 ppp_synctty
+alias ppp-compress-21 bsd_comp
+alias ppp-compress-24 ppp_deflate
+alias ppp-compress-26 ppp_deflate
+
+
+2.4 System setup under 2.2 series kernels
+
+Under the 2.2 series kernels, you should add the following to your
+/etc/modules.conf or /etc/conf.modules:
+
+alias tty-ldisc-3 ppp
+alias ppp-compress-21 bsd_comp
+alias ppp-compress-24 ppp_deflate
+alias ppp-compress-26 ppp_deflate
+
+
+3. Getting help with problems
+-----------------------------
+
+If you have problems with your PPP setup, or you just want to ask some
+questions, or better yet if you can help others with their PPP
+questions, then you should join the linux-ppp mailing list. Send an
+email to majordomo@vger.kernel.org with a line in the body saying
+
+subscribe linux-ppp
+
+To leave the mailing list, send an email to majordomo@vger.kernel.org
+with a line in the body saying
+
+unsubscribe linux-ppp
+
+To send a message to the list, email it to linux-ppp@vger.kernel.org.
+You don't have to be subscribed to send messages to the list.
+
+You can also email me (paulus@linuxcare.com.au) but I am overloaded
+with email and I can't respond to most messages I get in a timely
+fashion.
+
+There are also several relevant news groups, such as comp.protocols.ppp,
+comp.os.linux.networking, or comp.os.linux.setup.
+
+
+4. Configuring your dial-out PPP connections
+--------------------------------------------
+
+Some Linux distribution makers include tools in their distributions
+for setting up PPP connections. For example, for Red Hat Linux and
+derivatives, you should probably use linuxconf or netcfg to set up
+your PPP connections.
+
+The two main windowing environments for Linux, KDE and Gnome, both
+come with GUI utilities for configuring and controlling PPP dial-out
+connections. They are convenient and relatively easy to configure.
+
+A third alternative is to use a PPP front-end package such as wvdial
+or ezppp. These also will handle most of the details of talking to
+the modem and setting up the PPP connection for you.
+
+Assuming that you don't want to use any of these tools, you want to
+set up the configuration manually yourself, then read on. This
+document gives a brief description and example. More details can be
+found by reading the pppd and chat man pages and the PPP-HOWTO.
+
+We assume that you have a modem that uses the Hayes-compatible AT
+command set connected to an async serial port (e.g. /dev/ttyS0) and
+that you are dialling out to an ISP.
+
+The trickiest and most variable part of setting up a dial-out PPP
+connection is the part which involves getting the modem to dial and
+then invoking PPP service at the far end. Generally, once both ends
+are talking PPP the rest is relatively straightforward.
+
+Now in fact pppd doesn't know anything about how to get modems to dial
+or what you have to say to the system at the far end to get it to talk
+PPP. That's handled by an external program such as chat, specified
+with the connect option to pppd. Chat takes a series of strings to
+expect from the modem interleaved with a series of strings to send to
+the modem. See the chat man page for more information. Here is a
+simple example for connecting to an ISP, assuming that the ISP's
+system starts talking PPP as soon as it answers the phone:
+
+pppd connect 'chat -v "" AT OK ATDT5551212 ~' \
+ /dev/ttyS0 57600 crtscts debug defaultroute
+
+Going through pppd's options in order:
+ connect 'chat ...' This gives a command to run to contact the
+ PPP server. Here the supplied 'chat' program is used to dial a
+ remote computer. The whole command is enclosed in single quotes
+ because pppd expects a one-word argument for the 'connect' option.
+ The options to 'chat' itself are:
+
+ -v verbose mode; log what we do to syslog
+ "" don't wait for any prompt, but instead...
+ AT send the string "AT"
+ OK expect the response "OK", then
+ ATDT5551212 dial the modem, then
+ ~ wait for a ~ character, indicating the start
+ of a PPP frame from the server
+
+ /dev/ttyS0 specifies which serial port the modem is connected to
+ 57600 specifies the baud rate to use
+ crtscts use hardware flow control using the RTS & CTS signals
+ debug log the PPP negotiation with syslog
+ defaultroute add default network route via the PPP link
+
+Pppd will write error messages and debugging logs to the syslogd
+daemon using the facility name "daemon". These messages may already
+be logged to the console or to a file like /var/log/messages; consult
+your /etc/syslog.conf file to see. If you want to make all pppd
+messages go to a file such as /var/log/ppp-debug, add the line
+
+daemon.* /var/log/ppp-debug
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ This is one or more tabs. Do not use spaces.
+
+to syslog.conf; make sure to put one or more TAB characters (not
+spaces!) between the two fields. Then you need to create an empty
+/var/log/ppp-debug file with a command such as
+
+ touch /var/log/ppp-debug
+
+and then restart syslogd, usually by sending it a SIGHUP signal with a
+command like this:
+
+ killall -HUP syslogd
+
+
+4.1 Is the link up?
+
+The main way to tell if your PPP link is up and operational is the
+ifconfig ("interface configuration") command. Type
+
+ /sbin/ifconfig
+
+at a shell prompt. It should print a list of interfaces including one
+like this example:
+
+ppp0 Link encap Point-to-Point Protocol
+ inet addr 192.76.32.3 P-t-P 129.67.1.165 Mask 255.255.255.0
+ UP POINTOPOINT RUNNING MTU 1500 Metric 1
+ RX packets 33 errors 0 dropped 0 overrun 0
+ TX packets 42 errors 0 dropped 0 overrun 0
+
+Assuming that ifconfig shows the ppp network interface, you can test
+the link using the ping command like this:
+
+ /sbin/ping -c 3 129.67.1.165
+
+where the address you give is the address shown as the P-t-P address
+in the ifconfig output. If the link is operating correctly, you
+should see output like this:
+
+ PING 129.67.1.165 (129.67.1.165): 56 data bytes
+ 64 bytes from 129.67.1.165: icmp_seq=0 ttl=255 time=268 ms
+ 64 bytes from 129.67.1.165: icmp_seq=1 ttl=255 time=247 ms
+ 64 bytes from 129.67.1.165: icmp_seq=2 ttl=255 time=266 ms
+ --- 129.67.1.165 ping statistics ---
+ 3 packets transmitted, 3 packets received, 0% packet loss
+ round-trip min/avg/max = 247/260/268 ms
+
diff --git a/mdk-stage1/ppp/README.sol2 b/mdk-stage1/ppp/README.sol2
new file mode 100644
index 000000000..4c862208f
--- /dev/null
+++ b/mdk-stage1/ppp/README.sol2
@@ -0,0 +1,220 @@
+This file describes the installation process for ppp-2.3 on systems
+running Solaris 2. The Solaris 2 and SVR4 ports share a lot of code
+but are not identical. The STREAMS kernel modules and driver for
+Solaris 2 are in the svr4 directory (and use some code from the
+modules directory).
+
+NOTE: Although the kernel driver and modules have been designed to
+operate correctly on SMP systems, they have not been extensively
+tested on SMP machines. Some users of SMP Solaris x86 systems have
+reported system problems apparently linked to the use of previous
+versions of this software. I believe these problems have been fixed.
+
+
+Installation.
+*************
+
+1. Run the configure script and make the user-level programs and the
+kernel modules.
+
+ ./configure
+ make
+
+If you wish to use gcc (or another compiler) instead of Sun's cc, edit
+the svr4/Makedefs file and uncomment the definition of CC. You can
+also change the options passed to the C compiler by editing the COPTS
+definition.
+
+2. Install the programs and kernel modules: as root, do
+
+ make install
+
+This installs pppd, chat and pppstats in /usr/local/bin and the kernel
+modules in /kernel/drv and /kernel/strmod, and creates the /etc/ppp
+directory and populates it with default configuration files. You can
+change the installation directories by editing svr4/Makedefs.
+
+If your system normally has only one network interface, the default
+Solaris 2 system startup scripts will disable IP forwarding in the IP
+kernel module. This will prevent the remote machine from using the
+local machine as a gateway to access other hosts. The solution is to
+create an /etc/ppp/ip-up script containing something like this:
+
+ #!/bin/sh
+ /usr/sbin/ndd -set /dev/ip ip_forwarding 1
+
+See the man page for ip(7p) for details.
+
+Dynamic STREAMS Re-Plumbing Support.
+************************************
+
+Solaris 8 includes dynamic re-plumbing support. With this, modules
+below ip can be inserted, or removed, without having the ip stream be
+unplumbed, and re-plumbed again. All states in ip for an interface
+will therefore now be preserved. Users can install (or upgrade)
+modules like firewall, bandwidth manager, cache manager, tunneling,
+etc., without shutting the machine down.
+
+To support this, ppp driver now uses /dev/udp instead of /dev/ip for
+the ip stream. The interface stream (where ip module pushed on top of
+ppp) is then I_PLINK'ed below the ip stream. /dev/udp is used because
+STREAMS will not let a driver be PLINK'ed under itself, and /dev/ip is
+typically the driver at the bottom of the tunneling interfaces
+stream. The mux ids of the ip streams are then added using
+SIOCSxIFMUXID ioctl.
+
+Users will be able to see the modules on the interface stream by, for
+example:
+
+ pikapon% ifconfig ppp modlist
+ 0 ip
+ 1 ppp
+
+Or arbitrarily if bandwidth manager and firewall modules are installed:
+
+ pikapon% ifconfig hme0 modlist
+ 0 arp
+ 1 ip
+ 2 ipqos
+ 3 firewall
+ 4 hme
+
+Snoop Support.
+**************
+
+This version includes support for /usr/sbin/snoop. Tests has been done
+on both Solaris 7 and 8. Only IPv4 and IPv6 packets will be sent up to
+stream(s) marked as promiscuous, e.g, snoop et al.
+
+Users will be able to see the packets on the ppp interface by, for example:
+
+ snoop -d ppp0
+
+See the man page for snoop(1M) for details.
+
+IPv6 Support.
+*************
+
+This is for Solaris 8 and later.
+
+This version has been tested under Solaris 8 running IPv6. As of now,
+interoperability testing has only been done between Solaris machines
+in terms of the IPV6 NCP. An additional command line option for the
+pppd daemon has been added: ipv6cp-use-persistent.
+
+By default, compilation for IPv6 support is not enabled. Uncomment
+the necessary lines in pppd/Makefile.sol2 to enable it. Once done, the
+quickest way to get IPv6 running is to add the following somewhere in
+the command line option:
+
+ +ipv6 ipv6cp-use-persistent
+
+The persistent id for the link-local address was added to conform to
+RFC 2472; such that if there's an EUI-48 available, use that to make
+up the EUI-64. As of now, the Solaris implementation extracts the
+EUI-48 id from the Ethernet's MAC address (the ethernet interface
+needs to be up). Future works might support other ways of obtaining a
+unique yet persistent id, such as EEPROM serial numbers, etc.
+
+There need not be any up/down scripts for ipv6, e.g. /etc/ppp/ipv6-up
+or /etc/ppp/ipv6-down, to trigger IPv6 neighbor discovery for auto
+configuration and routing. The in.ndpd daemon will perform all of the
+necessary jobs in the background. /etc/inet/ndpd.conf can be further
+customized to enable the machine as an IPv6 router. See the man page
+for in.ndpd(1M) and ndpd.conf(4) for details.
+
+Below is a sample output of "ifconfig -a" with persistent link-local
+address. Note the UNNUMBERED flag is set because hme0 and ppp0 both
+have identical link-local IPv6 addresses:
+
+lo0: flags=1000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4> mtu 8232 index 1
+ inet 127.0.0.1 netmask ff000000
+hme0: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 2
+ inet 129.146.86.248 netmask ffffff00 broadcast 129.146.86.255
+ ether 8:0:20:8d:38:c1
+lo0: flags=2000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv6> mtu 8252 index 1
+ inet6 ::1/128
+hme0: flags=2000841<UP,RUNNING,MULTICAST,IPv6> mtu 1500 index 2
+ ether 8:0:20:8d:38:c1
+ inet6 fe80::a00:20ff:fe8d:38c1/10
+hme0:1: flags=2080841<UP,RUNNING,MULTICAST,ADDRCONF,IPv6> mtu 1500 index 2
+ inet6 fec0::56:a00:20ff:fe8d:38c1/64
+hme0:2: flags=2080841<UP,RUNNING,MULTICAST,ADDRCONF,IPv6> mtu 1500 index 2
+ inet6 2000::56:a00:20ff:fe8d:38c1/64
+hme0:3: flags=2080841<UP,RUNNING,MULTICAST,ADDRCONF,IPv6> mtu 1500 index 2
+ inet6 2::56:a00:20ff:fe8d:38c1/64
+ppp0: flags=10008d1<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST,IPv4> mtu 1500 index 12
+ inet 172.16.1.1 --> 172.16.1.2 netmask ffffff00
+ppp0: flags=2202851<UP,POINTOPOINT,RUNNING,MULTICAST,UNNUMBERED,NONUD,IPv6> mtu 1500 index 12
+ inet6 fe80::a00:20ff:fe8d:38c1/10 --> fe80::a00:20ff:fe7a:24fb
+
+Note also that a plumbed ipv6 interface stream will exist throughout
+the entire PPP session in the case where the peer rejects IPV6CP,
+which further causes the interface state to stay down. Unplumbing will
+happen when the daemon exits. This is done by design and is not a bug.
+
+64-bit Support.
+***************
+
+This version has been tested under Solaris 7 (and Solaris 8 ) in both
+32- and 64-bits environments (Ultra class machines). Installing the
+package by executing "make install" will result in additional files
+residing in /kernel/drv/sparcv9 and /kernel/strmod/sparcv9
+subdirectories.
+
+64-bit modules and driver have been compiled and tested using Sun's cc.
+
+Synchronous Serial Support.
+***************************
+
+This version has working but limited support for the on-board
+synchronous HDLC interfaces. It has been tested with the /dev/se_hdlc
+and /dev/zsh drivers. Synchronous mode was tested with a Cisco
+router.
+
+There ppp daemon does not directly support controlling the serial
+interface. It relies on the /usr/sbin/syncinit command to initialize
+HDLC mode and clocking.
+
+Some bugs remain: large sized frames are not sent/received properly,
+and may be related to the IP mtu. This may be due to bugs in pppd
+itself, bugs in Solaris or the serial drivers. The /dev/zsh driver
+seems more larger and can send/receive larger frames than the
+/dev/se_hdlc driver. There is a confirmed bug with NRZ/NRZI mode in
+the /dev/se_hdlc driver, and Solaris patch 104596-11 is needed to
+correct it. (However this patch seems to introduce other serial
+problems. If you don't apply the patch, the workaround is to change
+the nrzi mode to yes or no, whichever works)
+
+How to start pppd with synchronous support:
+
+#!/bin/sh
+
+local=1.1.1.1 # your ip address here
+baud=38400 # needed, but ignored by serial driver
+
+# Change to the correct serial driver/port
+#dev=/dev/zsh0
+dev=/dev/se_hdlc0
+
+# Change the driver, nrzi mode, speed and clocking to match your setup
+# This configuration is for external clocking from the DCE
+connect="syncinit se_hdlc0 nrzi=no speed=64000 txc=rxc rxc=rxc"
+
+/usr/sbin/pppd $dev sync $baud novj noauth $local: connect "$connect"
+
+
+Sample Cisco router config excerpt:
+
+!
+! Cisco router setup as DCE with RS-232 DCE cable
+!
+!
+interface Serial0
+ ip address 1.1.1.2 255.255.255.0
+ encapsulation ppp
+ clockrate 64000
+ no nrzi-encoding
+ no shutdown
+!
+
diff --git a/mdk-stage1/ppp/README.sunos4 b/mdk-stage1/ppp/README.sunos4
new file mode 100644
index 000000000..b48aec377
--- /dev/null
+++ b/mdk-stage1/ppp/README.sunos4
@@ -0,0 +1,62 @@
+This file describes the installation process for ppp-2.3 on systems
+running SunOS 4.1.x (or the equivalent).
+
+The STREAMS modules in the sunos4 directory provide kernel support for
+PPP on SunOS 4.1.x systems. They have been tested under SunOS 4.1.3
+on a SparcStation 1+. They should work under earlier SunOS 4.1.x
+systems, but no guarantees are given.
+
+These modules are designed to be loaded into the running kernel using
+the `modload' command.
+
+
+Installation.
+*************
+
+1. Run the configure script and make the user-level programs and the
+kernel modules.
+
+ ./configure
+ make
+
+If you wish to compile using gcc instead of cc, edit the
+sunos4/Makedefs file and uncomment the line saying "CC = gcc". You
+can also change the C compiler options by editing the COPTS
+definition.
+
+2. Install the pppd, pppstats and chat programs and the loadable
+module object files (you need to be root to do this):
+
+ make install
+
+By default, the programs and the loadable module object files go into
+/usr/local/etc. Doing `make install' also copies a script called
+ppp.INSTALL into /dev, and makes ppp.LOAD, ppp.UNLOAD, ppp.MKDEV and
+ppp.RMDEV links to it. You can change the installation directories by
+editing sunos4/Makedefs.
+
+3. Load the ppp modules (you need to be root for this too):
+
+ /dev/ppp.LOAD
+
+You will want to do this in your /etc/rc.local file once you have
+everything installed. I suggest you put something like the following
+in /etc/rc.local (or /etc/loadable, if you have one):
+
+ if [ -f /dev/ppp.AUTOLOAD ]; then
+ /dev/ppp.LOAD
+ fi
+
+and then create a /dev/ppp.AUTOLOAD file with the command
+
+ touch /dev/ppp.AUTOLOAD
+
+It is not advisable to unload the "if_ppp" module, because it creates
+network interface units, and SunOS does not provide any way to destroy
+network interfaces once created. If you do unload it, the system will
+probably crash at some later stage.
+
+If you have previously had ppp-2.2 installed, you may have statements
+in your /etc/rc.local to load the ppp module. You will need to remove
+those. Having done this, you will need to reboot to remove the old
+ppp module and load the new modules.
diff --git a/mdk-stage1/ppp/SETUP b/mdk-stage1/ppp/SETUP
new file mode 100644
index 000000000..fb28a2138
--- /dev/null
+++ b/mdk-stage1/ppp/SETUP
@@ -0,0 +1,111 @@
+ Configuring a PPP link.
+
+After you have compiled and installed this package, there are some
+configuration files which will generally need to be set up. The
+pppd(8) man page is the best reference for the full details; this file
+outlines the configuration process for the most common case, where
+this package is being used to enable a machine to dial an ISP and
+connect to the internet. The FAQ and README.linux files also provide
+useful information about setting up PPP.
+
+Dialling an ISP.
+****************
+
+Usually, an ISP will assign an IP address to your machine, and will
+refuse to authenticate itself to you. Some ISPs require a username
+and password to be entered before PPP service commences, while others
+use PPP authentication (using either the PAP or CHAP protocols).
+
+The recommended way to set up to dial an ISP is for the system
+administrator to create a file under /etc/ppp/peers, named for the ISP
+that you will be dialling. For example, suppose the file is called
+/etc/ppp/peers/isp. This file would contain something like this:
+
+cua0 # modem is connected to /dev/cua0
+38400 # run the serial port at 38400 baud
+crtscts # use hardware flow control
+noauth # don't require the ISP to authenticate itself
+defaultroute # use the ISP as our default route
+connect '/usr/sbin/chat -v -f /etc/ppp/chat-isp'
+
+If there are any other pppd options that should apply when calling
+this ISP, they can also be placed in this file.
+
+The /etc/ppp/chat-isp file named in the last line contains the script
+for chat(8) to use to dial the ISP and go through any username/
+password authentication required before PPP service starts. Here is
+an example (for dialling an Annex terminal server):
+
+ABORT "NO CARRIER"
+ABORT "NO DIALTONE"
+ABORT "ERROR"
+ABORT "NO ANSWER"
+ABORT "BUSY"
+ABORT "Username/Password Incorrect"
+"" "at"
+OK "at&d2&c1"
+OK "atdt2479381"
+"name:" "^Uusername"
+"word:" "\qpassword"
+"annex" "ppp"
+"Switching to PPP-ppp-Switching to PPP"
+
+See the chat(8) man page for details of the script. If you are not
+sure how the initial dialog with your ISP will go, you could use
+a terminal emulator such as kermit or minicom to go through the
+process manually.
+
+If your ISP requires PAP or CHAP authentication, you will have to
+create a line in /etc/ppp/pap-secrets or /etc/ppp/chap-secrets like
+this:
+
+myhostname * "password"
+
+(Replace myhostname with the hostname of your machine.)
+
+At this point, you can initiate the link with the command:
+
+/usr/sbin/pppd call isp
+
+(N.B.: pppd might be installed in a different directory on some
+systems).
+
+This will return to the shell prompt immediately, as pppd will detach
+itself from its controlling terminal. (If you don't want it to do
+this, use the "nodetach" option.)
+
+Pppd will log messages describing the progress of the connection and
+any errors using the syslog facility (see the syslogd(8) and
+syslog.conf(5) man pages). Pppd issues messages using syslog facility
+daemon (or local2 if it has been compiled with debugging enabled);
+chat uses facility local2. It is often useful to see messages of
+priority notice or higher on the console. To see these, find the line
+in /etc/syslog.conf which has /dev/console on the right-hand side, and
+add `daemon.notice' on the left. This line should end up something
+like this:
+
+*.err;kern.debug;daemon,local2,auth.notice;mail.crit /dev/console
+
+If you want to see more messages from pppd, request messages of
+priority info or higher for facility daemon, like this:
+
+*.err;kern.debug;daemon.info;local2,auth.notice;mail.crit /dev/console
+
+It is also useful to add a line like this:
+
+daemon,local2.debug /etc/ppp/ppp-log
+
+If you do this, you will need to create an empty /etc/ppp/ppp-log
+file.
+
+After modifying syslog.conf, you will then need to send a HUP signal
+to syslogd (or reboot).
+
+When you wish terminate the PPP link, you should send a TERM or INTR
+signal to pppd. Pppd writes its process ID to a file called
+ppp<n>.pid in /var/run (or /etc/ppp on older systems such as SunOS or
+Ultrix). Here <n> is the PPP interface unit number, which will be 0
+unless you have more than one PPP link running simultaneously. Thus
+you can terminate the link with a command like
+
+ kill `cat /var/run/ppp0.pid`
diff --git a/mdk-stage1/ppp/chat/Makefile.linux b/mdk-stage1/ppp/chat/Makefile.linux
new file mode 100644
index 000000000..586cbd876
--- /dev/null
+++ b/mdk-stage1/ppp/chat/Makefile.linux
@@ -0,0 +1,27 @@
+# $Id$
+
+CDEF1= -DTERMIOS # Use the termios structure
+CDEF2= -DSIGTYPE=void # Standard definition
+CDEF3= -UNO_SLEEP # Use the usleep function
+CDEF4= -DFNDELAY=O_NDELAY # Old name value
+CDEFS= $(CDEF1) $(CDEF2) $(CDEF3) $(CDEF4)
+
+CFLAGS= $(RPM_OPT_FLAGS) $(CDEFS)
+
+INSTALL= install
+
+all: chat
+
+chat: chat.o
+ $(CC) -o chat chat.o
+
+chat.o: chat.c
+ $(CC) -c $(CFLAGS) -o chat.o chat.c
+
+install: chat
+ mkdir -p $(BINDIR)
+ $(INSTALL) -s -c chat $(BINDIR)
+ $(INSTALL) -c -m 644 chat.8 $(MANDIR)/man8
+
+clean:
+ rm -f chat.o chat *~
diff --git a/mdk-stage1/ppp/chat/Makefile.linux.makeopt b/mdk-stage1/ppp/chat/Makefile.linux.makeopt
new file mode 100644
index 000000000..974680a93
--- /dev/null
+++ b/mdk-stage1/ppp/chat/Makefile.linux.makeopt
@@ -0,0 +1,27 @@
+# $Id$
+
+CDEF1= -DTERMIOS # Use the termios structure
+CDEF2= -DSIGTYPE=void # Standard definition
+CDEF3= -UNO_SLEEP # Use the usleep function
+CDEF4= -DFNDELAY=O_NDELAY # Old name value
+CDEFS= $(CDEF1) $(CDEF2) $(CDEF3) $(CDEF4)
+
+CFLAGS= -O2 -g -pipe $(CDEFS)
+
+INSTALL= install
+
+all: chat
+
+chat: chat.o
+ $(CC) -o chat chat.o
+
+chat.o: chat.c
+ $(CC) -c $(CFLAGS) -o chat.o chat.c
+
+install: chat
+ mkdir -p $(BINDIR)
+ $(INSTALL) -s -c chat $(BINDIR)
+ $(INSTALL) -c -m 644 chat.8 $(MANDIR)/man8
+
+clean:
+ rm -f chat.o chat *~
diff --git a/mdk-stage1/ppp/chat/Makefile.sol2 b/mdk-stage1/ppp/chat/Makefile.sol2
new file mode 100644
index 000000000..f566cc68b
--- /dev/null
+++ b/mdk-stage1/ppp/chat/Makefile.sol2
@@ -0,0 +1,19 @@
+#
+# Makefile for chat on Solaris 2
+#
+
+include ../solaris/Makedefs
+
+CFLAGS = $(COPTS) -DNO_USLEEP -DSOL2
+
+all: chat
+
+chat: chat.o
+ $(CC) -o chat chat.o
+
+install: chat
+ $(INSTALL) -f $(BINDIR) chat
+ $(INSTALL) -m 444 -f $(MANDIR)/man8 chat.8
+
+clean:
+ rm -f *~ *.o chat
diff --git a/mdk-stage1/ppp/chat/Makefile.sunos4 b/mdk-stage1/ppp/chat/Makefile.sunos4
new file mode 100644
index 000000000..b3507360e
--- /dev/null
+++ b/mdk-stage1/ppp/chat/Makefile.sunos4
@@ -0,0 +1,19 @@
+#
+# Makefile for chat on suns
+#
+
+include ../sunos4/Makedefs
+
+CFLAGS = -DSUNOS $(COPTS)
+
+all: chat
+
+chat: chat.o
+ $(CC) -o chat chat.o
+
+install: chat
+ $(INSTALL) -c chat $(BINDIR)/chat
+ $(INSTALL) -c -m 444 chat.8 $(MANDIR)/man8/chat.8
+
+clean:
+ rm -f *~ *.o chat
diff --git a/mdk-stage1/ppp/chat/chat.8 b/mdk-stage1/ppp/chat/chat.8
new file mode 100644
index 000000000..8d2029609
--- /dev/null
+++ b/mdk-stage1/ppp/chat/chat.8
@@ -0,0 +1,515 @@
+.\" -*- nroff -*-
+.\" manual page [] for chat 1.8
+.\" $Id$
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH CHAT 8 "22 May 1999" "Chat Version 1.22"
+.SH NAME
+chat \- Automated conversational script with a modem
+.SH SYNOPSIS
+.B chat
+[
+.I options
+]
+.I script
+.SH DESCRIPTION
+.LP
+The \fIchat\fR program defines a conversational exchange between the
+computer and the modem. Its primary purpose is to establish the
+connection between the Point-to-Point Protocol Daemon (\fIpppd\fR) and
+the remote's \fIpppd\fR process.
+.SH OPTIONS
+.TP
+.B -f \fI<chat file>
+Read the chat script from the chat \fIfile\fR. The use of this option
+is mutually exclusive with the chat script parameters. The user must
+have read access to the file. Multiple lines are permitted in the
+file. Space or horizontal tab characters should be used to separate
+the strings.
+.TP
+.B -t \fI<timeout>
+Set the timeout for the expected string to be received. If the string
+is not received within the time limit then the reply string is not
+sent. An alternate reply may be sent or the script will fail if there
+is no alternate reply string. A failed script will cause the
+\fIchat\fR program to terminate with a non-zero error code.
+.TP
+.B -r \fI<report file>
+Set the file for output of the report strings. If you use the keyword
+\fIREPORT\fR, the resulting strings are written to this file. If this
+option is not used and you still use \fIREPORT\fR keywords, the
+\fIstderr\fR file is used for the report strings.
+.TP
+.B -e
+Start with the echo option turned on. Echoing may also be turned on
+or off at specific points in the chat script by using the \fIECHO\fR
+keyword. When echoing is enabled, all output from the modem is echoed
+to \fIstderr\fR.
+.TP
+.B -E
+Enables environment variable substituion within chat scripts using the
+standard \fI$xxx\fR syntax.
+.TP
+.B -v
+Request that the \fIchat\fR script be executed in a verbose mode. The
+\fIchat\fR program will then log the execution state of the chat
+script as well as all text received from the modem and the output
+strings sent to the modem. The default is to log through the SYSLOG;
+the logging method may be altered with the -S and -s flags.
+.TP
+.B -V
+Request that the \fIchat\fR script be executed in a stderr verbose
+mode. The \fIchat\fR program will then log all text received from the
+modem and the output strings sent to the modem to the stderr device. This
+device is usually the local console at the station running the chat or
+pppd program.
+.TP
+.B -s
+Use stderr. All log messages from '-v' and all error messages will be
+sent to stderr.
+.TP
+.B -S
+Do not use the SYSLOG. By default, error messages are sent to the
+SYSLOG. The use of -S will prevent both log messages from '-v' and
+error messages from being sent to the SYSLOG.
+.TP
+.B -T \fI<phone number>
+Pass in an arbitary string, usually a phone number, that will be
+substituted for the \\T substitution metacharacter in a send string.
+.TP
+.B -U \fI<phone number 2>
+Pass in a second string, usually a phone number, that will be
+substituted for the \\U substitution metacharacter in a send string.
+This is useful when dialing an ISDN terminal adapter that requires two
+numbers.
+.TP
+.B script
+If the script is not specified in a file with the \fI-f\fR option then
+the script is included as parameters to the \fIchat\fR program.
+.SH CHAT SCRIPT
+.LP
+The \fIchat\fR script defines the communications.
+.LP
+A script consists of one or more "expect-send" pairs of strings,
+separated by spaces, with an optional "subexpect-subsend" string pair,
+separated by a dash as in the following example:
+.IP
+ogin:-BREAK-ogin: ppp ssword: hello2u2
+.LP
+This line indicates that the \fIchat\fR program should expect the string
+"ogin:". If it fails to receive a login prompt within the time interval
+allotted, it is to send a break sequence to the remote and then expect the
+string "ogin:". If the first "ogin:" is received then the break sequence is
+not generated.
+.LP
+Once it received the login prompt the \fIchat\fR program will send the
+string ppp and then expect the prompt "ssword:". When it receives the
+prompt for the password, it will send the password hello2u2.
+.LP
+A carriage return is normally sent following the reply string. It is not
+expected in the "expect" string unless it is specifically requested by using
+the \\r character sequence.
+.LP
+The expect sequence should contain only what is needed to identify the
+string. Since it is normally stored on a disk file, it should not contain
+variable information. It is generally not acceptable to look for time
+strings, network identification strings, or other variable pieces of data as
+an expect string.
+.LP
+To help correct for characters which may be corrupted during the initial
+sequence, look for the string "ogin:" rather than "login:". It is possible
+that the leading "l" character may be received in error and you may never
+find the string even though it was sent by the system. For this reason,
+scripts look for "ogin:" rather than "login:" and "ssword:" rather than
+"password:".
+.LP
+A very simple script might look like this:
+.IP
+ogin: ppp ssword: hello2u2
+.LP
+In other words, expect ....ogin:, send ppp, expect ...ssword:, send hello2u2.
+.LP
+In actual practice, simple scripts are rare. At the vary least, you
+should include sub-expect sequences should the original string not be
+received. For example, consider the following script:
+.IP
+ogin:--ogin: ppp ssword: hello2u2
+.LP
+This would be a better script than the simple one used earlier. This would look
+for the same login: prompt, however, if one was not received, a single
+return sequence is sent and then it will look for login: again. Should line
+noise obscure the first login prompt then sending the empty line will
+usually generate a login prompt again.
+.SH COMMENTS
+Comments can be embedded in the chat script. A comment is a line which
+starts with the \fB#\fR (hash) character in column 1. Such comment
+lines are just ignored by the chat program. If a '#' character is to
+be expected as the first character of the expect sequence, you should
+quote the expect string.
+If you want to wait for a prompt that starts with a # (hash)
+character, you would have to write something like this:
+.IP
+# Now wait for the prompt and send logout string
+.br
+\'# ' logout
+.LP
+
+.SH SENDING DATA FROM A FILE
+If the string to send starts with an at sign (@), the rest of the
+string is taken to be the name of a file to read to get the string to
+send. If the last character of the data read is a newline, it is
+removed. The file can be a named pipe (or fifo) instead of a regular
+file. This provides a way for \fBchat\fR to communicate with another
+program, for example, a program to prompt the user and receive a
+password typed in.
+.LP
+
+.SH ABORT STRINGS
+Many modems will report the status of the call as a string. These
+strings may be \fBCONNECTED\fR or \fBNO CARRIER\fR or \fBBUSY\fR. It
+is often desirable to terminate the script should the modem fail to
+connect to the remote. The difficulty is that a script would not know
+exactly which modem string it may receive. On one attempt, it may
+receive \fBBUSY\fR while the next time it may receive \fBNO CARRIER\fR.
+.LP
+These "abort" strings may be specified in the script using the \fIABORT\fR
+sequence. It is written in the script as in the following example:
+.IP
+ABORT BUSY ABORT 'NO CARRIER' '' ATZ OK ATDT5551212 CONNECT
+.LP
+This sequence will expect nothing; and then send the string ATZ. The
+expected response to this is the string \fIOK\fR. When it receives \fIOK\fR,
+the string ATDT5551212 to dial the telephone. The expected string is
+\fICONNECT\fR. If the string \fICONNECT\fR is received the remainder of the
+script is executed. However, should the modem find a busy telephone, it will
+send the string \fIBUSY\fR. This will cause the string to match the abort
+character sequence. The script will then fail because it found a match to
+the abort string. If it received the string \fINO CARRIER\fR, it will abort
+for the same reason. Either string may be received. Either string will
+terminate the \fIchat\fR script.
+.SH CLR_ABORT STRINGS
+This sequence allows for clearing previously set \fBABORT\fR strings.
+\fBABORT\fR strings are kept in an array of a pre-determined size (at
+compilation time); \fBCLR_ABORT\fR will reclaim the space for cleared
+entries so that new strings can use that space.
+.SH SAY STRINGS
+The \fBSAY\fR directive allows the script to send strings to the user
+at the terminal via standard error. If \fBchat\fR is being run by
+pppd, and pppd is running as a daemon (detached from its controlling
+terminal), standard error will normally be redirected to the file
+/etc/ppp/connect-errors.
+.LP
+\fBSAY\fR strings must be enclosed in single or double quotes. If
+carriage return and line feed are needed in the string to be output,
+you must explicitely add them to your string.
+.LP
+The SAY strings could be used to give progress messages in sections of
+the script where you want to have 'ECHO OFF' but still let the user
+know what is happening. An example is:
+.IP
+ABORT BUSY
+.br
+ECHO OFF
+.br
+SAY "Dialling your ISP...\\n"
+.br
+\'' ATDT5551212
+.br
+TIMEOUT 120
+.br
+SAY "Waiting up to 2 minutes for connection ... "
+.br
+CONNECT ''
+.br
+SAY "Connected, now logging in ...\n"
+.br
+ogin: account
+.br
+ssword: pass
+.br
+$ \c
+SAY "Logged in OK ...\n"
+\fIetc ...\fR
+.LP
+This sequence will only present the SAY strings to the user and all
+the details of the script will remain hidden. For example, if the
+above script works, the user will see:
+.IP
+Dialling your ISP...
+.br
+Waiting up to 2 minutes for connection ... Connected, now logging in ...
+.br
+Logged in OK ...
+.LP
+
+.SH REPORT STRINGS
+A \fBreport\fR string is similar to the ABORT string. The difference
+is that the strings, and all characters to the next control character
+such as a carriage return, are written to the report file.
+.LP
+The report strings may be used to isolate the transmission rate of the
+modem's connect string and return the value to the chat user. The
+analysis of the report string logic occurs in conjunction with the
+other string processing such as looking for the expect string. The use
+of the same string for a report and abort sequence is probably not
+very useful, however, it is possible.
+.LP
+The report strings to no change the completion code of the program.
+.LP
+These "report" strings may be specified in the script using the \fIREPORT\fR
+sequence. It is written in the script as in the following example:
+.IP
+REPORT CONNECT ABORT BUSY '' ATDT5551212 CONNECT '' ogin: account
+.LP
+This sequence will expect nothing; and then send the string
+ATDT5551212 to dial the telephone. The expected string is
+\fICONNECT\fR. If the string \fICONNECT\fR is received the remainder
+of the script is executed. In addition the program will write to the
+expect-file the string "CONNECT" plus any characters which follow it
+such as the connection rate.
+.SH CLR_REPORT STRINGS
+This sequence allows for clearing previously set \fBREPORT\fR strings.
+\fBREPORT\fR strings are kept in an array of a pre-determined size (at
+compilation time); \fBCLR_REPORT\fR will reclaim the space for cleared
+entries so that new strings can use that space.
+.SH ECHO
+The echo options controls whether the output from the modem is echoed
+to \fIstderr\fR. This option may be set with the \fI-e\fR option, but
+it can also be controlled by the \fIECHO\fR keyword. The "expect-send"
+pair \fIECHO\fR \fION\fR enables echoing, and \fIECHO\fR \fIOFF\fR
+disables it. With this keyword you can select which parts of the
+conversation should be visible. For instance, with the following
+script:
+.IP
+ABORT 'BUSY'
+.br
+ABORT 'NO CARRIER'
+.br
+'' ATZ
+.br
+OK\\r\\n ATD1234567
+.br
+\\r\\n \\c
+.br
+ECHO ON
+.br
+CONNECT \\c
+.br
+ogin: account
+.LP
+all output resulting from modem configuration and dialing is not visible,
+but starting with the \fICONNECT\fR (or \fIBUSY\fR) message, everything
+will be echoed.
+.SH HANGUP
+The HANGUP options control whether a modem hangup should be considered
+as an error or not. This option is useful in scripts for dialling
+systems which will hang up and call your system back. The HANGUP
+options can be \fBON\fR or \fBOFF\fR.
+.br
+When HANGUP is set OFF and the modem hangs up (e.g., after the first
+stage of logging in to a callback system), \fBchat\fR will continue
+running the script (e.g., waiting for the incoming call and second
+stage login prompt). As soon as the incoming call is connected, you
+should use the \fBHANGUP ON\fR directive to reinstall normal hang up
+signal behavior. Here is an (simple) example script:
+.IP
+ABORT 'BUSY'
+.br
+'' ATZ
+.br
+OK\\r\\n ATD1234567
+.br
+\\r\\n \\c
+.br
+CONNECT \\c
+.br
+\'Callback login:' call_back_ID
+.br
+HANGUP OFF
+.br
+ABORT "Bad Login"
+.br
+\'Callback Password:' Call_back_password
+.br
+TIMEOUT 120
+.br
+CONNECT \\c
+.br
+HANGUP ON
+.br
+ABORT "NO CARRIER"
+.br
+ogin:--BREAK--ogin: real_account
+.br
+\fIetc ...\fR
+.LP
+.SH TIMEOUT
+The initial timeout value is 45 seconds. This may be changed using the \fB-t\fR
+parameter.
+.LP
+To change the timeout value for the next expect string, the following
+example may be used:
+.IP
+ATZ OK ATDT5551212 CONNECT TIMEOUT 10 ogin:--ogin: TIMEOUT 5 assword: hello2u2
+.LP
+This will change the timeout to 10 seconds when it expects the login:
+prompt. The timeout is then changed to 5 seconds when it looks for the
+password prompt.
+.LP
+The timeout, once changed, remains in effect until it is changed again.
+.SH SENDING EOT
+The special reply string of \fIEOT\fR indicates that the chat program
+should send an EOT character to the remote. This is normally the
+End-of-file character sequence. A return character is not sent
+following the EOT.
+.PR
+The EOT sequence may be embedded into the send string using the
+sequence \fI^D\fR.
+.SH GENERATING BREAK
+The special reply string of \fIBREAK\fR will cause a break condition
+to be sent. The break is a special signal on the transmitter. The
+normal processing on the receiver is to change the transmission rate.
+It may be used to cycle through the available transmission rates on
+the remote until you are able to receive a valid login prompt.
+.PR
+The break sequence may be embedded into the send string using the
+\fI\\K\fR sequence.
+.SH ESCAPE SEQUENCES
+The expect and reply strings may contain escape sequences. All of the
+sequences are legal in the reply string. Many are legal in the expect.
+Those which are not valid in the expect sequence are so indicated.
+.TP
+.B ''
+Expects or sends a null string. If you send a null string then it will still
+send the return character. This sequence may either be a pair of apostrophe
+or quote characters.
+.TP
+.B \\\\b
+represents a backspace character.
+.TP
+.B \\\\c
+Suppresses the newline at the end of the reply string. This is the only
+method to send a string without a trailing return character. It must
+be at the end of the send string. For example,
+the sequence hello\\c will simply send the characters h, e, l, l, o.
+.I (not valid in expect.)
+.TP
+.B \\\\d
+Delay for one second. The program uses sleep(1) which will delay to a
+maximum of one second.
+.I (not valid in expect.)
+.TP
+.B \\\\K
+Insert a BREAK
+.I (not valid in expect.)
+.TP
+.B \\\\n
+Send a newline or linefeed character.
+.TP
+.B \\\\N
+Send a null character. The same sequence may be represented by \\0.
+.I (not valid in expect.)
+.TP
+.B \\\\p
+Pause for a fraction of a second. The delay is 1/10th of a second.
+.I (not valid in expect.)
+.TP
+.B \\\\q
+Suppress writing the string to the SYSLOG file. The string ?????? is
+written to the log in its place.
+.I (not valid in expect.)
+.TP
+.B \\\\r
+Send or expect a carriage return.
+.TP
+.B \\\\s
+Represents a space character in the string. This may be used when it
+is not desirable to quote the strings which contains spaces. The
+sequence 'HI TIM' and HI\\sTIM are the same.
+.TP
+.B \\\\t
+Send or expect a tab character.
+.TP
+.B \\\\T
+Send the phone number string as specified with the \fI-T\fR option
+.I (not valid in expect.)
+.TP
+.B \\\\U
+Send the phone number 2 string as specified with the \fI-U\fR option
+.I (not valid in expect.)
+.TP
+.B \\\\\\\\
+Send or expect a backslash character.
+.TP
+.B \\\\ddd
+Collapse the octal digits (ddd) into a single ASCII character and send that
+character.
+.I (some characters are not valid in expect.)
+.TP
+.B \^^C
+Substitute the sequence with the control character represented by C.
+For example, the character DC1 (17) is shown as \^^Q.
+.I (some characters are not valid in expect.)
+.SH ENVIRONMENT VARIABLES
+Environment variables are available within chat scripts, if the \fI-E\fR
+option was specified in the command line. The metacharacter \fI$\fR is used
+to introduce the name of the environment variable to substitute. If the
+substition fails, because the requested environment variable is not set,
+\fInothing\fR is replaced for the variable.
+.SH TERMINATION CODES
+The \fIchat\fR program will terminate with the following completion
+codes.
+.TP
+.B 0
+The normal termination of the program. This indicates that the script
+was executed without error to the normal conclusion.
+.TP
+.B 1
+One or more of the parameters are invalid or an expect string was too
+large for the internal buffers. This indicates that the program as not
+properly executed.
+.TP
+.B 2
+An error occurred during the execution of the program. This may be due
+to a read or write operation failing for some reason or chat receiving
+a signal such as SIGINT.
+.TP
+.B 3
+A timeout event occurred when there was an \fIexpect\fR string without
+having a "-subsend" string. This may mean that you did not program the
+script correctly for the condition or that some unexpected event has
+occurred and the expected string could not be found.
+.TP
+.B 4
+The first string marked as an \fIABORT\fR condition occurred.
+.TP
+.B 5
+The second string marked as an \fIABORT\fR condition occurred.
+.TP
+.B 6
+The third string marked as an \fIABORT\fR condition occurred.
+.TP
+.B 7
+The fourth string marked as an \fIABORT\fR condition occurred.
+.TP
+.B ...
+The other termination codes are also strings marked as an \fIABORT\fR
+condition.
+.LP
+Using the termination code, it is possible to determine which event
+terminated the script. It is possible to decide if the string "BUSY"
+was received from the modem as opposed to "NO DIAL TONE". While the
+first event may be retried, the second will probably have little
+chance of succeeding during a retry.
+.SH SEE ALSO
+Additional information about \fIchat\fR scripts may be found with UUCP
+documentation. The \fIchat\fR script was taken from the ideas proposed
+by the scripts used by the \fIuucico\fR program.
+.LP
+uucico(1), uucp(1)
+.SH COPYRIGHT
+The \fIchat\fR program is in public domain. This is not the GNU public
+license. If it breaks then you get to keep both pieces.
diff --git a/mdk-stage1/ppp/chat/chat.c b/mdk-stage1/ppp/chat/chat.c
new file mode 100644
index 000000000..1b22907a8
--- /dev/null
+++ b/mdk-stage1/ppp/chat/chat.c
@@ -0,0 +1,1756 @@
+/*
+ * Chat -- a program for automatic session establishment (i.e. dial
+ * the phone and log in).
+ *
+ * Standard termination codes:
+ * 0 - successful completion of the script
+ * 1 - invalid argument, expect string too large, etc.
+ * 2 - error on an I/O operation or fatal error condition.
+ * 3 - timeout waiting for a simple string.
+ * 4 - the first string declared as "ABORT"
+ * 5 - the second string declared as "ABORT"
+ * 6 - ... and so on for successive ABORT strings.
+ *
+ * This software is in the public domain.
+ *
+ * -----------------
+ * 22-May-99 added environment substitutuion, enabled with -E switch.
+ * Andreas Arens <andras@cityweb.de>.
+ *
+ * 12-May-99 added a feature to read data to be sent from a file,
+ * if the send string starts with @. Idea from gpk <gpk@onramp.net>.
+ *
+ * added -T and -U option and \T and \U substitution to pass a phone
+ * number into chat script. Two are needed for some ISDN TA applications.
+ * Keith Dart <kdart@cisco.com>
+ *
+ *
+ * Added SAY keyword to send output to stderr.
+ * This allows to turn ECHO OFF and to output specific, user selected,
+ * text to give progress messages. This best works when stderr
+ * exists (i.e.: pppd in nodetach mode).
+ *
+ * Added HANGUP directives to allow for us to be called
+ * back. When HANGUP is set to NO, chat will not hangup at HUP signal.
+ * We rely on timeouts in that case.
+ *
+ * Added CLR_ABORT to clear previously set ABORT string. This has been
+ * dictated by the HANGUP above as "NO CARRIER" (for example) must be
+ * an ABORT condition until we know the other host is going to close
+ * the connection for call back. As soon as we have completed the
+ * first stage of the call back sequence, "NO CARRIER" is a valid, non
+ * fatal string. As soon as we got called back (probably get "CONNECT"),
+ * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
+ * Note that CLR_ABORT packs the abort_strings[] array so that we do not
+ * have unused entries not being reclaimed.
+ *
+ * In the same vein as above, added CLR_REPORT keyword.
+ *
+ * Allow for comments. Line starting with '#' are comments and are
+ * ignored. If a '#' is to be expected as the first character, the
+ * expect string must be quoted.
+ *
+ *
+ * Francis Demierre <Francis@SwissMail.Com>
+ * Thu May 15 17:15:40 MET DST 1997
+ *
+ *
+ * Added -r "report file" switch & REPORT keyword.
+ * Robert Geer <bgeer@xmission.com>
+ *
+ * Added -s "use stderr" and -S "don't use syslog" switches.
+ * June 18, 1997
+ * Karl O. Pinc <kop@meme.com>
+ *
+ *
+ * Added -e "echo" switch & ECHO keyword
+ * Dick Streefland <dicks@tasking.nl>
+ *
+ *
+ * Considerable updates and modifications by
+ * Al Longyear <longyear@pobox.com>
+ * Paul Mackerras <paulus@cs.anu.edu.au>
+ *
+ *
+ * The original author is:
+ *
+ * Karl Fox <karl@MorningStar.Com>
+ * Morning Star Technologies, Inc.
+ * 1760 Zollinger Road
+ * Columbus, OH 43221
+ * (614)451-1883
+ *
+ */
+
+#ifndef __STDC__
+#define const
+#endif
+
+#ifndef lint
+static const char rcsid[] = "$Id$";
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#ifndef TERMIO
+#undef TERMIOS
+#define TERMIOS
+#endif
+
+#ifdef TERMIO
+#include <termio.h>
+#endif
+#ifdef TERMIOS
+#include <termios.h>
+#endif
+
+#define STR_LEN 1024
+
+#ifndef SIGTYPE
+#define SIGTYPE void
+#endif
+
+#undef __P
+#undef __V
+
+#ifdef __STDC__
+#include <stdarg.h>
+#define __V(x) x
+#define __P(x) x
+#else
+#include <varargs.h>
+#define __V(x) (va_alist) va_dcl
+#define __P(x) ()
+#define const
+#endif
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+#ifdef SUNOS
+extern int sys_nerr;
+extern char *sys_errlist[];
+#define memmove(to, from, n) bcopy(from, to, n)
+#define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\
+ "unknown error")
+#endif
+
+/*************** Micro getopt() *********************************************/
+#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
+ (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
+ &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
+#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
+ (_O=4,(char*)0):(char*)0)
+#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0)
+#define ARG(c,v) (c?(--c,*v++):(char*)0)
+
+static int _O = 0; /* Internal state */
+/*************** Micro getopt() *********************************************/
+
+char *program_name;
+
+#define MAX_ABORTS 50
+#define MAX_REPORTS 50
+#define DEFAULT_CHAT_TIMEOUT 45
+
+int echo = 0;
+int verbose = 0;
+int to_log = 1;
+int to_stderr = 0;
+int Verbose = 0;
+int quiet = 0;
+int report = 0;
+int use_env = 0;
+int exit_code = 0;
+FILE* report_fp = (FILE *) 0;
+char *report_file = (char *) 0;
+char *chat_file = (char *) 0;
+char *phone_num = (char *) 0;
+char *phone_num2 = (char *) 0;
+int timeout = DEFAULT_CHAT_TIMEOUT;
+
+int have_tty_parameters = 0;
+
+#ifdef TERMIO
+#define term_parms struct termio
+#define get_term_param(param) ioctl(0, TCGETA, param)
+#define set_term_param(param) ioctl(0, TCSETA, param)
+struct termio saved_tty_parameters;
+#endif
+
+#ifdef TERMIOS
+#define term_parms struct termios
+#define get_term_param(param) tcgetattr(0, param)
+#define set_term_param(param) tcsetattr(0, TCSANOW, param)
+struct termios saved_tty_parameters;
+#endif
+
+char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
+ fail_buffer[50];
+int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
+int clear_abort_next = 0;
+
+char *report_string[MAX_REPORTS] ;
+char report_buffer[50] ;
+int n_reports = 0, report_next = 0, report_gathering = 0 ;
+int clear_report_next = 0;
+
+int say_next = 0, hup_next = 0;
+
+void *dup_mem __P((void *b, size_t c));
+void *copy_of __P((char *s));
+void usage __P((void));
+void logf __P((const char *fmt, ...));
+void fatal __P((int code, const char *fmt, ...));
+SIGTYPE sigalrm __P((int signo));
+SIGTYPE sigint __P((int signo));
+SIGTYPE sigterm __P((int signo));
+SIGTYPE sighup __P((int signo));
+void unalarm __P((void));
+void init __P((void));
+void set_tty_parameters __P((void));
+void echo_stderr __P((int));
+void break_sequence __P((void));
+void terminate __P((int status));
+void do_file __P((char *chat_file));
+int get_string __P((register char *string));
+int put_string __P((register char *s));
+int write_char __P((int c));
+int put_char __P((int c));
+int get_char __P((void));
+void chat_send __P((register char *s));
+char *character __P((int c));
+void chat_expect __P((register char *s));
+char *clean __P((register char *s, int sending));
+void break_sequence __P((void));
+void terminate __P((int status));
+void pack_array __P((char **array, int end));
+char *expect_strtok __P((char *, char *));
+int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */
+
+int main __P((int, char *[]));
+
+void *dup_mem(b, c)
+void *b;
+size_t c;
+{
+ void *ans = malloc (c);
+ if (!ans)
+ fatal(2, "memory error!");
+
+ memcpy (ans, b, c);
+ return ans;
+}
+
+void *copy_of (s)
+char *s;
+{
+ return dup_mem (s, strlen (s) + 1);
+}
+
+/*
+ * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \
+ * [ -r report-file ] \
+ * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
+ *
+ * Perform a UUCP-dialer-like chat script on stdin and stdout.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int option;
+ char *arg;
+
+ program_name = *argv;
+ tzset();
+
+ while ((option = OPTION(argc, argv)) != 0) {
+ switch (option) {
+ case 'e':
+ ++echo;
+ break;
+
+ case 'E':
+ ++use_env;
+ break;
+
+ case 'v':
+ ++verbose;
+ break;
+
+ case 'V':
+ ++Verbose;
+ break;
+
+ case 's':
+ ++to_stderr;
+ break;
+
+ case 'S':
+ to_log = 0;
+ break;
+
+ case 'f':
+ if ((arg = OPTARG(argc, argv)) != NULL)
+ chat_file = copy_of(arg);
+ else
+ usage();
+ break;
+
+ case 't':
+ if ((arg = OPTARG(argc, argv)) != NULL)
+ timeout = atoi(arg);
+ else
+ usage();
+ break;
+
+ case 'r':
+ arg = OPTARG (argc, argv);
+ if (arg) {
+ if (report_fp != NULL)
+ fclose (report_fp);
+ report_file = copy_of (arg);
+ report_fp = fopen (report_file, "a");
+ if (report_fp != NULL) {
+ if (verbose)
+ fprintf (report_fp, "Opening \"%s\"...\n",
+ report_file);
+ report = 1;
+ }
+ }
+ break;
+
+ case 'T':
+ if ((arg = OPTARG(argc, argv)) != NULL)
+ phone_num = copy_of(arg);
+ else
+ usage();
+ break;
+
+ case 'U':
+ if ((arg = OPTARG(argc, argv)) != NULL)
+ phone_num2 = copy_of(arg);
+ else
+ usage();
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+/*
+ * Default the report file to the stderr location
+ */
+ if (report_fp == NULL)
+ report_fp = stderr;
+
+ if (to_log) {
+#ifdef ultrix
+ openlog("chat", LOG_PID);
+#else
+ openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
+
+ if (verbose)
+ setlogmask(LOG_UPTO(LOG_INFO));
+ else
+ setlogmask(LOG_UPTO(LOG_WARNING));
+#endif
+ }
+
+ init();
+
+ if (chat_file != NULL) {
+ arg = ARG(argc, argv);
+ if (arg != NULL)
+ usage();
+ else
+ do_file (chat_file);
+ } else {
+ while ((arg = ARG(argc, argv)) != NULL) {
+ chat_expect(arg);
+
+ if ((arg = ARG(argc, argv)) != NULL)
+ chat_send(arg);
+ }
+ }
+
+ terminate(0);
+ return 0;
+}
+
+/*
+ * Process a chat script when read from a file.
+ */
+
+void do_file (chat_file)
+char *chat_file;
+{
+ int linect, sendflg;
+ char *sp, *arg, quote;
+ char buf [STR_LEN];
+ FILE *cfp;
+
+ cfp = fopen (chat_file, "r");
+ if (cfp == NULL)
+ fatal(1, "%s -- open failed: %m", chat_file);
+
+ linect = 0;
+ sendflg = 0;
+
+ while (fgets(buf, STR_LEN, cfp) != NULL) {
+ sp = strchr (buf, '\n');
+ if (sp)
+ *sp = '\0';
+
+ linect++;
+ sp = buf;
+
+ /* lines starting with '#' are comments. If a real '#'
+ is to be expected, it should be quoted .... */
+ if ( *sp == '#' )
+ continue;
+
+ while (*sp != '\0') {
+ if (*sp == ' ' || *sp == '\t') {
+ ++sp;
+ continue;
+ }
+
+ if (*sp == '"' || *sp == '\'') {
+ quote = *sp++;
+ arg = sp;
+ while (*sp != quote) {
+ if (*sp == '\0')
+ fatal(1, "unterminated quote (line %d)", linect);
+
+ if (*sp++ == '\\') {
+ if (*sp != '\0')
+ ++sp;
+ }
+ }
+ }
+ else {
+ arg = sp;
+ while (*sp != '\0' && *sp != ' ' && *sp != '\t')
+ ++sp;
+ }
+
+ if (*sp != '\0')
+ *sp++ = '\0';
+
+ if (sendflg)
+ chat_send (arg);
+ else
+ chat_expect (arg);
+ sendflg = !sendflg;
+ }
+ }
+ fclose (cfp);
+}
+
+/*
+ * We got an error parsing the command line.
+ */
+void usage()
+{
+ fprintf(stderr, "\
+Usage: %s [-e] [-E] [-v] [-V] [-t timeout] [-r report-file]\n\
+ [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name);
+ exit(1);
+}
+
+char line[1024];
+
+/*
+ * Send a message to syslog and/or stderr.
+ */
+void logf __V((const char *fmt, ...))
+{
+ va_list args;
+
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ char *fmt;
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+
+ vfmtmsg(line, sizeof(line), fmt, args);
+ if (to_log)
+ syslog(LOG_INFO, "%s", line);
+ if (to_stderr)
+ fprintf(stderr, "%s\n", line);
+}
+
+/*
+ * Print an error message and terminate.
+ */
+
+void fatal __V((int code, const char *fmt, ...))
+{
+ va_list args;
+
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ int code;
+ char *fmt;
+ va_start(args);
+ code = va_arg(args, int);
+ fmt = va_arg(args, char *);
+#endif
+
+ vfmtmsg(line, sizeof(line), fmt, args);
+ if (to_log)
+ syslog(LOG_ERR, "%s", line);
+ if (to_stderr)
+ fprintf(stderr, "%s\n", line);
+ terminate(code);
+}
+
+int alarmed = 0;
+
+SIGTYPE sigalrm(signo)
+int signo;
+{
+ int flags;
+
+ alarm(1);
+ alarmed = 1; /* Reset alarm to avoid race window */
+ signal(SIGALRM, sigalrm); /* that can cause hanging in read() */
+
+ if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+ fatal(2, "Can't get file mode flags on stdin: %m");
+
+ if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
+ fatal(2, "Can't set file mode flags on stdin: %m");
+
+ if (verbose)
+ logf("alarm");
+}
+
+void unalarm()
+{
+ int flags;
+
+ if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+ fatal(2, "Can't get file mode flags on stdin: %m");
+
+ if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
+ fatal(2, "Can't set file mode flags on stdin: %m");
+}
+
+SIGTYPE sigint(signo)
+int signo;
+{
+ fatal(2, "SIGINT");
+}
+
+SIGTYPE sigterm(signo)
+int signo;
+{
+ fatal(2, "SIGTERM");
+}
+
+SIGTYPE sighup(signo)
+int signo;
+{
+ fatal(2, "SIGHUP");
+}
+
+void init()
+{
+ signal(SIGINT, sigint);
+ signal(SIGTERM, sigterm);
+ signal(SIGHUP, sighup);
+
+ set_tty_parameters();
+ signal(SIGALRM, sigalrm);
+ alarm(0);
+ alarmed = 0;
+}
+
+void set_tty_parameters()
+{
+#if defined(get_term_param)
+ term_parms t;
+
+ if (get_term_param (&t) < 0)
+ fatal(2, "Can't get terminal parameters: %m");
+
+ saved_tty_parameters = t;
+ have_tty_parameters = 1;
+
+ t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
+ t.c_oflag = 0;
+ t.c_lflag = 0;
+ t.c_cc[VERASE] =
+ t.c_cc[VKILL] = 0;
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+
+ if (set_term_param (&t) < 0)
+ fatal(2, "Can't set terminal parameters: %m");
+#endif
+}
+
+void break_sequence()
+{
+#ifdef TERMIOS
+ tcsendbreak (0, 0);
+#endif
+}
+
+void terminate(status)
+int status;
+{
+ static int terminating = 0;
+
+ if (terminating)
+ exit(status);
+ terminating = 1;
+ echo_stderr(-1);
+/*
+ * Allow the last of the report string to be gathered before we terminate.
+ */
+ if (report_gathering) {
+ int c, rep_len;
+
+ rep_len = strlen(report_buffer);
+ while (rep_len + 1 <= sizeof(report_buffer)) {
+ alarm(1);
+ c = get_char();
+ alarm(0);
+ if (c < 0 || iscntrl(c))
+ break;
+ report_buffer[rep_len] = c;
+ ++rep_len;
+ }
+ report_buffer[rep_len] = 0;
+ fprintf (report_fp, "chat: %s\n", report_buffer);
+ }
+ if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
+ if (verbose)
+ fprintf (report_fp, "Closing \"%s\".\n", report_file);
+ fclose (report_fp);
+ report_fp = (FILE *) NULL;
+ }
+
+#if defined(get_term_param)
+ if (have_tty_parameters) {
+ if (set_term_param (&saved_tty_parameters) < 0)
+ fatal(2, "Can't restore terminal parameters: %m");
+ }
+#endif
+
+ exit(status);
+}
+
+/*
+ * 'Clean up' this string.
+ */
+char *clean(s, sending)
+register char *s;
+int sending; /* set to 1 when sending (putting) this string. */
+{
+ char temp[STR_LEN], env_str[STR_LEN], cur_chr;
+ register char *s1, *phchar;
+ int add_return = sending;
+#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
+#define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \
+ || (((chr) >= 'a') && ((chr) <= 'z')) \
+ || (((chr) >= 'A') && ((chr) <= 'Z')) \
+ || (chr) == '_')
+
+ s1 = temp;
+ while (*s) {
+ cur_chr = *s++;
+ if (cur_chr == '^') {
+ cur_chr = *s++;
+ if (cur_chr == '\0') {
+ *s1++ = '^';
+ break;
+ }
+ cur_chr &= 0x1F;
+ if (cur_chr != 0) {
+ *s1++ = cur_chr;
+ }
+ continue;
+ }
+
+ if (use_env && cur_chr == '$') { /* ARI */
+ phchar = env_str;
+ while (isalnumx(*s))
+ *phchar++ = *s++;
+ *phchar = '\0';
+ phchar = getenv(env_str);
+ if (phchar)
+ while (*phchar)
+ *s1++ = *phchar++;
+ continue;
+ }
+
+ if (cur_chr != '\\') {
+ *s1++ = cur_chr;
+ continue;
+ }
+
+ cur_chr = *s++;
+ if (cur_chr == '\0') {
+ if (sending) {
+ *s1++ = '\\';
+ *s1++ = '\\';
+ }
+ break;
+ }
+
+ switch (cur_chr) {
+ case 'b':
+ *s1++ = '\b';
+ break;
+
+ case 'c':
+ if (sending && *s == '\0')
+ add_return = 0;
+ else
+ *s1++ = cur_chr;
+ break;
+
+ case '\\':
+ case 'K':
+ case 'p':
+ case 'd':
+ if (sending)
+ *s1++ = '\\';
+ *s1++ = cur_chr;
+ break;
+
+ case 'T':
+ if (sending && phone_num) {
+ for (phchar = phone_num; *phchar != '\0'; phchar++)
+ *s1++ = *phchar;
+ }
+ else {
+ *s1++ = '\\';
+ *s1++ = 'T';
+ }
+ break;
+
+ case 'U':
+ if (sending && phone_num2) {
+ for (phchar = phone_num2; *phchar != '\0'; phchar++)
+ *s1++ = *phchar;
+ }
+ else {
+ *s1++ = '\\';
+ *s1++ = 'U';
+ }
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 'r':
+ *s1++ = '\r';
+ break;
+
+ case 'n':
+ *s1++ = '\n';
+ break;
+
+ case 's':
+ *s1++ = ' ';
+ break;
+
+ case 't':
+ *s1++ = '\t';
+ break;
+
+ case 'N':
+ if (sending) {
+ *s1++ = '\\';
+ *s1++ = '\0';
+ }
+ else
+ *s1++ = 'N';
+ break;
+
+ case '$': /* ARI */
+ if (use_env) {
+ *s1++ = cur_chr;
+ break;
+ }
+ /* FALL THROUGH */
+
+ default:
+ if (isoctal (cur_chr)) {
+ cur_chr &= 0x07;
+ if (isoctal (*s)) {
+ cur_chr <<= 3;
+ cur_chr |= *s++ - '0';
+ if (isoctal (*s)) {
+ cur_chr <<= 3;
+ cur_chr |= *s++ - '0';
+ }
+ }
+
+ if (cur_chr != 0 || sending) {
+ if (sending && (cur_chr == '\\' || cur_chr == 0))
+ *s1++ = '\\';
+ *s1++ = cur_chr;
+ }
+ break;
+ }
+
+ if (sending)
+ *s1++ = '\\';
+ *s1++ = cur_chr;
+ break;
+ }
+ }
+
+ if (add_return)
+ *s1++ = '\r';
+
+ *s1++ = '\0'; /* guarantee closure */
+ *s1++ = '\0'; /* terminate the string */
+ return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
+}
+
+/*
+ * A modified version of 'strtok'. This version skips \ sequences.
+ */
+
+char *expect_strtok (s, term)
+ char *s, *term;
+{
+ static char *str = "";
+ int escape_flag = 0;
+ char *result;
+
+/*
+ * If a string was specified then do initial processing.
+ */
+ if (s)
+ str = s;
+
+/*
+ * If this is the escape flag then reset it and ignore the character.
+ */
+ if (*str)
+ result = str;
+ else
+ result = (char *) 0;
+
+ while (*str) {
+ if (escape_flag) {
+ escape_flag = 0;
+ ++str;
+ continue;
+ }
+
+ if (*str == '\\') {
+ ++str;
+ escape_flag = 1;
+ continue;
+ }
+
+/*
+ * If this is not in the termination string, continue.
+ */
+ if (strchr (term, *str) == (char *) 0) {
+ ++str;
+ continue;
+ }
+
+/*
+ * This is the terminator. Mark the end of the string and stop.
+ */
+ *str++ = '\0';
+ break;
+ }
+ return (result);
+}
+
+/*
+ * Process the expect string
+ */
+
+void chat_expect (s)
+char *s;
+{
+ char *expect;
+ char *reply;
+
+ if (strcmp(s, "HANGUP") == 0) {
+ ++hup_next;
+ return;
+ }
+
+ if (strcmp(s, "ABORT") == 0) {
+ ++abort_next;
+ return;
+ }
+
+ if (strcmp(s, "CLR_ABORT") == 0) {
+ ++clear_abort_next;
+ return;
+ }
+
+ if (strcmp(s, "REPORT") == 0) {
+ ++report_next;
+ return;
+ }
+
+ if (strcmp(s, "CLR_REPORT") == 0) {
+ ++clear_report_next;
+ return;
+ }
+
+ if (strcmp(s, "TIMEOUT") == 0) {
+ ++timeout_next;
+ return;
+ }
+
+ if (strcmp(s, "ECHO") == 0) {
+ ++echo_next;
+ return;
+ }
+
+ if (strcmp(s, "SAY") == 0) {
+ ++say_next;
+ return;
+ }
+
+/*
+ * Fetch the expect and reply string.
+ */
+ for (;;) {
+ expect = expect_strtok (s, "-");
+ s = (char *) 0;
+
+ if (expect == (char *) 0)
+ return;
+
+ reply = expect_strtok (s, "-");
+
+/*
+ * Handle the expect string. If successful then exit.
+ */
+ if (get_string (expect))
+ return;
+
+/*
+ * If there is a sub-reply string then send it. Otherwise any condition
+ * is terminal.
+ */
+ if (reply == (char *) 0 || exit_code != 3)
+ break;
+
+ chat_send (reply);
+ }
+
+/*
+ * The expectation did not occur. This is terminal.
+ */
+ if (fail_reason)
+ logf("Failed (%s)", fail_reason);
+ else
+ logf("Failed");
+ terminate(exit_code);
+}
+
+/*
+ * Translate the input character to the appropriate string for printing
+ * the data.
+ */
+
+char *character(c)
+int c;
+{
+ static char string[10];
+ char *meta;
+
+ meta = (c & 0x80) ? "M-" : "";
+ c &= 0x7F;
+
+ if (c < 32)
+ sprintf(string, "%s^%c", meta, (int)c + '@');
+ else if (c == 127)
+ sprintf(string, "%s^?", meta);
+ else
+ sprintf(string, "%s%c", meta, c);
+
+ return (string);
+}
+
+/*
+ * process the reply string
+ */
+void chat_send (s)
+register char *s;
+{
+ char file_data[STR_LEN];
+
+ if (say_next) {
+ say_next = 0;
+ s = clean(s, 1);
+ write(2, s, strlen(s));
+ free(s);
+ return;
+ }
+
+ if (hup_next) {
+ hup_next = 0;
+ if (strcmp(s, "OFF") == 0)
+ signal(SIGHUP, SIG_IGN);
+ else
+ signal(SIGHUP, sighup);
+ return;
+ }
+
+ if (echo_next) {
+ echo_next = 0;
+ echo = (strcmp(s, "ON") == 0);
+ return;
+ }
+
+ if (abort_next) {
+ char *s1;
+
+ abort_next = 0;
+
+ if (n_aborts >= MAX_ABORTS)
+ fatal(2, "Too many ABORT strings");
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s)
+ || strlen(s1) + 1 > sizeof(fail_buffer))
+ fatal(1, "Illegal or too-long ABORT string ('%v')", s);
+
+ abort_string[n_aborts++] = s1;
+
+ if (verbose)
+ logf("abort on (%v)", s);
+ return;
+ }
+
+ if (clear_abort_next) {
+ char *s1;
+ int i;
+ int old_max;
+ int pack = 0;
+
+ clear_abort_next = 0;
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s)
+ || strlen(s1) + 1 > sizeof(fail_buffer))
+ fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
+
+ old_max = n_aborts;
+ for (i=0; i < n_aborts; i++) {
+ if ( strcmp(s1,abort_string[i]) == 0 ) {
+ free(abort_string[i]);
+ abort_string[i] = NULL;
+ pack++;
+ n_aborts--;
+ if (verbose)
+ logf("clear abort on (%v)", s);
+ }
+ }
+ free(s1);
+ if (pack)
+ pack_array(abort_string,old_max);
+ return;
+ }
+
+ if (report_next) {
+ char *s1;
+
+ report_next = 0;
+ if (n_reports >= MAX_REPORTS)
+ fatal(2, "Too many REPORT strings");
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+ fatal(1, "Illegal or too-long REPORT string ('%v')", s);
+
+ report_string[n_reports++] = s1;
+
+ if (verbose)
+ logf("report (%v)", s);
+ return;
+ }
+
+ if (clear_report_next) {
+ char *s1;
+ int i;
+ int old_max;
+ int pack = 0;
+
+ clear_report_next = 0;
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+ fatal(1, "Illegal or too-long REPORT string ('%v')", s);
+
+ old_max = n_reports;
+ for (i=0; i < n_reports; i++) {
+ if ( strcmp(s1,report_string[i]) == 0 ) {
+ free(report_string[i]);
+ report_string[i] = NULL;
+ pack++;
+ n_reports--;
+ if (verbose)
+ logf("clear report (%v)", s);
+ }
+ }
+ free(s1);
+ if (pack)
+ pack_array(report_string,old_max);
+
+ return;
+ }
+
+ if (timeout_next) {
+ timeout_next = 0;
+ timeout = atoi(s);
+
+ if (timeout <= 0)
+ timeout = DEFAULT_CHAT_TIMEOUT;
+
+ if (verbose)
+ logf("timeout set to %d seconds", timeout);
+
+ return;
+ }
+
+ /*
+ * The syntax @filename means read the string to send from the
+ * file `filename'.
+ */
+ if (s[0] == '@') {
+ /* skip the @ and any following white-space */
+ char *fn = s;
+ while (*++fn == ' ' || *fn == '\t')
+ ;
+
+ if (*fn != 0) {
+ FILE *f;
+ int n = 0;
+
+ /* open the file and read until STR_LEN-1 bytes or end-of-file */
+ f = fopen(fn, "r");
+ if (f == NULL)
+ fatal(1, "%s -- open failed: %m", fn);
+ while (n < STR_LEN - 1) {
+ int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f);
+ if (nr < 0)
+ fatal(1, "%s -- read error", fn);
+ if (nr == 0)
+ break;
+ n += nr;
+ }
+ fclose(f);
+
+ /* use the string we got as the string to send,
+ but trim off the final newline if any. */
+ if (n > 0 && file_data[n-1] == '\n')
+ --n;
+ file_data[n] = 0;
+ s = file_data;
+ }
+ }
+
+ if (strcmp(s, "EOT") == 0)
+ s = "^D\\c";
+ else if (strcmp(s, "BREAK") == 0)
+ s = "\\K\\c";
+
+ if (!put_string(s))
+ fatal(1, "Failed");
+}
+
+int get_char()
+{
+ int status;
+ char c;
+
+ status = read(0, &c, 1);
+
+ switch (status) {
+ case 1:
+ return ((int)c & 0x7F);
+
+ default:
+ logf("warning: read() on stdin returned %d", status);
+
+ case -1:
+ if ((status = fcntl(0, F_GETFL, 0)) == -1)
+ fatal(2, "Can't get file mode flags on stdin: %m");
+
+ if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
+ fatal(2, "Can't set file mode flags on stdin: %m");
+
+ return (-1);
+ }
+}
+
+int put_char(c)
+int c;
+{
+ int status;
+ char ch = c;
+
+ usleep(10000); /* inter-character typing delay (?) */
+
+ status = write(1, &ch, 1);
+
+ switch (status) {
+ case 1:
+ return (0);
+
+ default:
+ logf("warning: write() on stdout returned %d", status);
+
+ case -1:
+ if ((status = fcntl(0, F_GETFL, 0)) == -1)
+ fatal(2, "Can't get file mode flags on stdin, %m");
+
+ if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
+ fatal(2, "Can't set file mode flags on stdin: %m");
+
+ return (-1);
+ }
+}
+
+int write_char (c)
+int c;
+{
+ if (alarmed || put_char(c) < 0) {
+ alarm(0);
+ alarmed = 0;
+
+ if (verbose) {
+ if (errno == EINTR || errno == EWOULDBLOCK)
+ logf(" -- write timed out");
+ else
+ logf(" -- write failed: %m");
+ }
+ return (0);
+ }
+ return (1);
+}
+
+int put_string (s)
+register char *s;
+{
+ quiet = 0;
+ s = clean(s, 1);
+
+ if (verbose) {
+ if (quiet)
+ logf("send (??????)");
+ else
+ logf("send (%v)", s);
+ }
+
+ alarm(timeout); alarmed = 0;
+
+ while (*s) {
+ register char c = *s++;
+
+ if (c != '\\') {
+ if (!write_char (c))
+ return 0;
+ continue;
+ }
+
+ c = *s++;
+ switch (c) {
+ case 'd':
+ sleep(1);
+ break;
+
+ case 'K':
+ break_sequence();
+ break;
+
+ case 'p':
+ usleep(10000); /* 1/100th of a second (arg is microseconds) */
+ break;
+
+ default:
+ if (!write_char (c))
+ return 0;
+ break;
+ }
+ }
+
+ alarm(0);
+ alarmed = 0;
+ return (1);
+}
+
+/*
+ * Echo a character to stderr.
+ * When called with -1, a '\n' character is generated when
+ * the cursor is not at the beginning of a line.
+ */
+void echo_stderr(n)
+int n;
+{
+ static int need_lf;
+ char *s;
+
+ switch (n) {
+ case '\r': /* ignore '\r' */
+ break;
+ case -1:
+ if (need_lf == 0)
+ break;
+ /* fall through */
+ case '\n':
+ write(2, "\n", 1);
+ need_lf = 0;
+ break;
+ default:
+ s = character(n);
+ write(2, s, strlen(s));
+ need_lf = 1;
+ break;
+ }
+}
+
+/*
+ * 'Wait for' this string to appear on this file descriptor.
+ */
+int get_string(string)
+register char *string;
+{
+ char temp[STR_LEN];
+ int c, printed = 0, len, minlen;
+ register char *s = temp, *end = s + STR_LEN;
+ char *logged = temp;
+
+ fail_reason = (char *)0;
+ string = clean(string, 0);
+ len = strlen(string);
+ minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
+
+ if (verbose)
+ logf("expect (%v)", string);
+
+ if (len > STR_LEN) {
+ logf("expect string is too long");
+ exit_code = 1;
+ return 0;
+ }
+
+ if (len == 0) {
+ if (verbose)
+ logf("got it");
+ return (1);
+ }
+
+ alarm(timeout);
+ alarmed = 0;
+
+ while ( ! alarmed && (c = get_char()) >= 0) {
+ int n, abort_len, report_len;
+
+ if (echo)
+ echo_stderr(c);
+ if (verbose && c == '\n') {
+ if (s == logged)
+ logf(""); /* blank line */
+ else
+ logf("%0.*v", s - logged, logged);
+ logged = s + 1;
+ }
+
+ *s++ = c;
+
+ if (verbose && s >= logged + 80) {
+ logf("%0.*v", s - logged, logged);
+ logged = s;
+ }
+
+ if (Verbose) {
+ if (c == '\n')
+ fputc( '\n', stderr );
+ else if (c != '\r')
+ fprintf( stderr, "%s", character(c) );
+ }
+
+ if (!report_gathering) {
+ for (n = 0; n < n_reports; ++n) {
+ if ((report_string[n] != (char*) NULL) &&
+ s - temp >= (report_len = strlen(report_string[n])) &&
+ strncmp(s - report_len, report_string[n], report_len) == 0) {
+ time_t time_now = time ((time_t*) NULL);
+ struct tm* tm_now = localtime (&time_now);
+
+ strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
+ strcat (report_buffer, report_string[n]);
+
+ report_string[n] = (char *) NULL;
+ report_gathering = 1;
+ break;
+ }
+ }
+ }
+ else {
+ if (!iscntrl (c)) {
+ int rep_len = strlen (report_buffer);
+ report_buffer[rep_len] = c;
+ report_buffer[rep_len + 1] = '\0';
+ }
+ else {
+ report_gathering = 0;
+ fprintf (report_fp, "chat: %s\n", report_buffer);
+ }
+ }
+
+ if (s - temp >= len &&
+ c == string[len - 1] &&
+ strncmp(s - len, string, len) == 0) {
+ if (verbose) {
+ if (s > logged)
+ logf("%0.*v", s - logged, logged);
+ logf(" -- got it\n");
+ }
+
+ alarm(0);
+ alarmed = 0;
+ return (1);
+ }
+
+ for (n = 0; n < n_aborts; ++n) {
+ if (s - temp >= (abort_len = strlen(abort_string[n])) &&
+ strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
+ if (verbose) {
+ if (s > logged)
+ logf("%0.*v", s - logged, logged);
+ logf(" -- failed");
+ }
+
+ alarm(0);
+ alarmed = 0;
+ exit_code = n + 4;
+ strcpy(fail_reason = fail_buffer, abort_string[n]);
+ return (0);
+ }
+ }
+
+ if (s >= end) {
+ if (logged < s - minlen) {
+ if (verbose)
+ logf("%0.*v", s - logged, logged);
+ logged = s;
+ }
+ s -= minlen;
+ memmove(temp, s, minlen);
+ logged = temp + (logged - s);
+ s = temp + minlen;
+ }
+
+ if (alarmed && verbose)
+ logf("warning: alarm synchronization problem");
+ }
+
+ alarm(0);
+
+ if (verbose && printed) {
+ if (alarmed)
+ logf(" -- read timed out");
+ else
+ logf(" -- read failed: %m");
+ }
+
+ exit_code = 3;
+ alarmed = 0;
+ return (0);
+}
+
+/*
+ * Gross kludge to handle Solaris versions >= 2.6 having usleep.
+ */
+#ifdef SOL2
+#include <sys/param.h>
+#if MAXUID > 65536 /* then this is Solaris 2.6 or later */
+#undef NO_USLEEP
+#endif
+#endif /* SOL2 */
+
+#ifdef NO_USLEEP
+#include <sys/types.h>
+#include <sys/time.h>
+
+/*
+ usleep -- support routine for 4.2BSD system call emulations
+ last edit: 29-Oct-1984 D A Gwyn
+ */
+
+extern int select();
+
+int
+usleep( usec ) /* returns 0 if ok, else -1 */
+ long usec; /* delay in microseconds */
+{
+ static struct { /* `timeval' */
+ long tv_sec; /* seconds */
+ long tv_usec; /* microsecs */
+ } delay; /* _select() timeout */
+
+ delay.tv_sec = usec / 1000000L;
+ delay.tv_usec = usec % 1000000L;
+
+ return select(0, (long *)0, (long *)0, (long *)0, &delay);
+}
+#endif
+
+void
+pack_array (array, end)
+ char **array; /* The address of the array of string pointers */
+ int end; /* The index of the next free entry before CLR_ */
+{
+ int i, j;
+
+ for (i = 0; i < end; i++) {
+ if (array[i] == NULL) {
+ for (j = i+1; j < end; ++j)
+ if (array[j] != NULL)
+ array[i++] = array[j];
+ for (; i < end; ++i)
+ array[i] = NULL;
+ break;
+ }
+ }
+}
+
+/*
+ * vfmtmsg - format a message into a buffer. Like vsprintf except we
+ * also specify the length of the output buffer, and we handle the
+ * %m (error message) format.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int
+vfmtmsg(buf, buflen, fmt, args)
+ char *buf;
+ int buflen;
+ const char *fmt;
+ va_list args;
+{
+ int c, i, n;
+ int width, prec, fillch;
+ int base, len, neg, quoted;
+ unsigned long val = 0;
+ char *str, *buf0;
+ const char *f;
+ unsigned char *p;
+ char num[32];
+ static char hexchars[] = "0123456789abcdef";
+
+ buf0 = buf;
+ --buflen;
+ while (buflen > 0) {
+ for (f = fmt; *f != '%' && *f != 0; ++f)
+ ;
+ if (f > fmt) {
+ len = f - fmt;
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, fmt, len);
+ buf += len;
+ buflen -= len;
+ fmt = f;
+ }
+ if (*fmt == 0)
+ break;
+ c = *++fmt;
+ width = prec = 0;
+ fillch = ' ';
+ if (c == '0') {
+ fillch = '0';
+ c = *++fmt;
+ }
+ if (c == '*') {
+ width = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ width = width * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if (c == '.') {
+ c = *++fmt;
+ if (c == '*') {
+ prec = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ prec = prec * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ }
+ str = 0;
+ base = 0;
+ neg = 0;
+ ++fmt;
+ switch (c) {
+ case 'd':
+ i = va_arg(args, int);
+ if (i < 0) {
+ neg = 1;
+ val = -i;
+ } else
+ val = i;
+ base = 10;
+ break;
+ case 'o':
+ val = va_arg(args, unsigned int);
+ base = 8;
+ break;
+ case 'x':
+ val = va_arg(args, unsigned int);
+ base = 16;
+ break;
+ case 'p':
+ val = (unsigned long) va_arg(args, void *);
+ base = 16;
+ neg = 2;
+ break;
+ case 's':
+ str = va_arg(args, char *);
+ break;
+ case 'c':
+ num[0] = va_arg(args, int);
+ num[1] = 0;
+ str = num;
+ break;
+ case 'm':
+ str = strerror(errno);
+ break;
+ case 'v': /* "visible" string */
+ case 'q': /* quoted string */
+ quoted = c == 'q';
+ p = va_arg(args, unsigned char *);
+ if (fillch == '0' && prec > 0) {
+ n = prec;
+ } else {
+ n = strlen((char *)p);
+ if (prec > 0 && prec < n)
+ n = prec;
+ }
+ while (n > 0 && buflen > 0) {
+ c = *p++;
+ --n;
+ if (!quoted && c >= 0x80) {
+ OUTCHAR('M');
+ OUTCHAR('-');
+ c -= 0x80;
+ }
+ if (quoted && (c == '"' || c == '\\'))
+ OUTCHAR('\\');
+ if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+ if (quoted) {
+ OUTCHAR('\\');
+ switch (c) {
+ case '\t': OUTCHAR('t'); break;
+ case '\n': OUTCHAR('n'); break;
+ case '\b': OUTCHAR('b'); break;
+ case '\f': OUTCHAR('f'); break;
+ default:
+ OUTCHAR('x');
+ OUTCHAR(hexchars[c >> 4]);
+ OUTCHAR(hexchars[c & 0xf]);
+ }
+ } else {
+ if (c == '\t')
+ OUTCHAR(c);
+ else {
+ OUTCHAR('^');
+ OUTCHAR(c ^ 0x40);
+ }
+ }
+ } else
+ OUTCHAR(c);
+ }
+ continue;
+ default:
+ *buf++ = '%';
+ if (c != '%')
+ --fmt; /* so %z outputs %z etc. */
+ --buflen;
+ continue;
+ }
+ if (base != 0) {
+ str = num + sizeof(num);
+ *--str = 0;
+ while (str > num + neg) {
+ *--str = hexchars[val % base];
+ val = val / base;
+ if (--prec <= 0 && val == 0)
+ break;
+ }
+ switch (neg) {
+ case 1:
+ *--str = '-';
+ break;
+ case 2:
+ *--str = 'x';
+ *--str = '0';
+ break;
+ }
+ len = num + sizeof(num) - 1 - str;
+ } else {
+ len = strlen(str);
+ if (prec > 0 && len > prec)
+ len = prec;
+ }
+ if (width > 0) {
+ if (width > buflen)
+ width = buflen;
+ if ((n = width - len) > 0) {
+ buflen -= n;
+ for (; n > 0; --n)
+ *buf++ = fillch;
+ }
+ }
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, str, len);
+ buf += len;
+ buflen -= len;
+ }
+ *buf = 0;
+ return buf - buf0;
+}
diff --git a/mdk-stage1/ppp/common/zlib.c b/mdk-stage1/ppp/common/zlib.c
new file mode 100644
index 000000000..503076886
--- /dev/null
+++ b/mdk-stage1/ppp/common/zlib.c
@@ -0,0 +1,5376 @@
+/*
+ * This file is derived from various .h and .c files from the zlib-1.0.4
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets. See zlib.h for conditions of
+ * distribution and use.
+ *
+ * Changes that have been made include:
+ * - added Z_PACKET_FLUSH (see zlib.h for details)
+ * - added inflateIncomp and deflateOutputPending
+ * - allow strm->next_out to be NULL, meaning discard the output
+ *
+ * $Id$
+ */
+
+/*
+ * ==FILEVERSION 971210==
+ *
+ * This marker is used by the Linux installation script to determine
+ * whether an up-to-date version of this file is already installed.
+ */
+
+#define NO_DUMMY_DECL
+#define NO_ZCFUNCS
+#define MY_ZCALLOC
+
+#if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL))
+#define inflate inflate_ppp /* FreeBSD already has an inflate :-( */
+#endif
+
+
+/* +++ zutil.h */
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* From: zutil.h,v 1.16 1996/07/24 13:41:13 me Exp $ */
+
+#ifndef _Z_UTIL_H
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#if defined(KERNEL) || defined(_KERNEL)
+/* Assume this is a *BSD or SVR4 kernel */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/systm.h>
+#undef u
+# define HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy((s), (d), (n))
+# define memset(d, v, n) bzero((d), (n))
+# define memcmp bcmp
+
+#else
+#if defined(__KERNEL__)
+/* Assume this is a Linux kernel */
+#include <linux/string.h>
+#define HAVE_MEMCPY
+
+#else /* not kernel */
+
+#if defined(MSDOS)||defined(VMS)||defined(CRAY)||defined(WIN32)||defined(RISCOS)
+# include <stddef.h>
+# include <errno.h>
+#else
+ extern int errno;
+#endif
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+#endif
+#endif /* __KERNEL__ */
+#endif /* _KERNEL || KERNEL */
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+#ifdef MSDOS
+# define OS_CODE 0x00
+# ifdef __TURBOC__
+# include <alloc.h>
+# else /* MSC or DJGPP */
+# include <malloc.h>
+# endif
+#endif
+
+#ifdef OS2
+# define OS_CODE 0x06
+#endif
+
+#ifdef WIN32 /* Window 95 & Windows NT */
+# define OS_CODE 0x0b
+#endif
+
+#if defined(VAXC) || defined(VMS)
+# define OS_CODE 0x02
+# define FOPEN(name, mode) \
+ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#ifdef AMIGA
+# define OS_CODE 0x01
+#endif
+
+#if defined(ATARI) || defined(atarist)
+# define OS_CODE 0x05
+#endif
+
+#ifdef MACOS
+# define OS_CODE 0x07
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+# define OS_CODE 0x0F
+#endif
+
+#ifdef TOPS20
+# define OS_CODE 0x0a
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+ /* Common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef FOPEN
+# define FOPEN(name, mode) fopen((name), (mode))
+#endif
+
+ /* functions */
+
+#ifdef HAVE_STRERROR
+ extern char *strerror OF((int));
+# define zstrerror(errnum) strerror(errnum)
+#else
+# define zstrerror(errnum) ""
+#endif
+
+#if defined(pyr)
+# define NO_MEMCPY
+#endif
+#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(_MSC_VER)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+ * You may have to use the same strategy for Borland C (untested).
+ */
+# define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+# define zmemcpy _fmemcpy
+# define zmemcmp _fmemcmp
+# define zmemzero(dest, len) _fmemset(dest, 0, len)
+# else
+# define zmemcpy memcpy
+# define zmemcmp memcmp
+# define zmemzero(dest, len) memset(dest, 0, len)
+# endif
+#else
+ extern void zmemcpy OF((Bytef* dest, Bytef* source, uInt len));
+ extern int zmemcmp OF((Bytef* s1, Bytef* s2, uInt len));
+ extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG_ZLIB
+# include <stdio.h>
+# ifndef verbose
+# define verbose 0
+# endif
+ extern void z_error OF((char *m));
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) fprintf x
+# define Tracev(x) {if (verbose) fprintf x ;}
+# define Tracevv(x) {if (verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+typedef uLong (*check_func) OF((uLong check, const Bytef *buf, uInt len));
+
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void zcfree OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* _Z_UTIL_H */
+/* --- zutil.h */
+
+/* +++ deflate.h */
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-1996 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* From: deflate.h,v 1.10 1996/07/02 12:41:00 me Exp $ */
+
+#ifndef _DEFLATE_H
+#define _DEFLATE_H
+
+/* #include "zutil.h" */
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE 42
+#define BUSY_STATE 113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct deflate_state {
+ z_streamp strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ ulg pending_buf_size; /* size of pending_buf */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ int pending; /* nb of bytes in the pending buffer */
+ int noheader; /* suppress zlib header and adler32 */
+ Byte data_type; /* UNKNOWN, BINARY or ASCII */
+ Byte method; /* STORED (for zip only) or DEFLATED */
+ int last_flush; /* value of flush param for previous deflate call */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to supress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *l_buf; /* buffer for literals or lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt last_lit; /* running index in l_buf */
+
+ ushf *d_buf;
+ /* Buffer for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ ulg compressed_len; /* total bit length of compressed file */
+ uInt matches; /* number of string matches in current block */
+ int last_eob_len; /* bit length of EOB code for last block */
+
+#ifdef DEBUG_ZLIB
+ ulg bits_sent; /* bit length of the compressed data */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+ /* in trees.c */
+void _tr_init OF((deflate_state *s));
+int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+ulg _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+void _tr_align OF((deflate_state *s));
+void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+void _tr_stored_type_only OF((deflate_state *));
+
+#endif
+/* --- deflate.h */
+
+/* +++ deflate.c */
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ * Available in ftp://ds.internic.net/rfc/rfc1951.txt
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* From: deflate.c,v 1.15 1996/07/24 13:40:58 me Exp $ */
+
+/* #include "deflate.h" */
+
+char deflate_copyright[] = " deflate 1.0.4 Copyright 1995-1996 Jean-loup Gailly ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ * Function prototypes.
+ */
+typedef enum {
+ need_more, /* block not completed, need more input or more output */
+ block_done, /* block flush performed */
+ finish_started, /* finish started, need only more output at next deflate */
+ finish_done /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast OF((deflate_state *s, int flush));
+local block_state deflate_slow OF((deflate_state *s, int flush));
+local void lm_init OF((deflate_state *s));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_streamp strm));
+local int read_buf OF((z_streamp strm, charf *buf, unsigned size));
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+ uInt longest_match OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match OF((deflate_state *s, IPos cur_match));
+#endif
+
+#ifdef DEBUG_ZLIB
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+ compress_func func;
+} config;
+
+local config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8, deflate_fast},
+/* 3 */ {4, 6, 32, 32, deflate_fast},
+
+/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32, deflate_slow},
+/* 6 */ {8, 16, 128, 128, deflate_slow},
+/* 7 */ {8, 32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+#ifndef NO_DUMMY_DECL
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+#endif
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((charf *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int deflateInit_(strm, level, version, stream_size)
+ z_streamp strm;
+ int level;
+ const char *version;
+ int stream_size;
+{
+ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY, version, stream_size);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+ version, stream_size)
+ z_streamp strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char *version;
+ int stream_size;
+{
+ deflate_state *s;
+ int noheader = 0;
+ static char* my_version = ZLIB_VERSION;
+
+ ushf *overlay;
+ /* We overlay pending_buf and d_buf+l_buf. This works since the average
+ * output size for (length,distance) codes is <= 24 bits.
+ */
+
+ if (version == Z_NULL || version[0] != my_version[0] ||
+ stream_size != sizeof(z_stream)) {
+ return Z_VERSION_ERROR;
+ }
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+#ifndef NO_ZCFUNCS
+ if (strm->zalloc == Z_NULL) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == Z_NULL) strm->zfree = zcfree;
+#endif
+
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+
+ if (windowBits < 0) { /* undocumented feature: suppress zlib header */
+ noheader = 1;
+ windowBits = -windowBits;
+ }
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+ strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
+ return Z_STREAM_ERROR;
+ }
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->noheader = noheader;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+ s->pending_buf = (uchf *) overlay;
+ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int deflateSetDictionary (strm, dictionary, dictLength)
+ z_streamp strm;
+ const Bytef *dictionary;
+ uInt dictLength;
+{
+ deflate_state *s;
+ uInt length = dictLength;
+ uInt n;
+ IPos hash_head = 0;
+
+ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL)
+ return Z_STREAM_ERROR;
+
+ s = (deflate_state *) strm->state;
+ if (s->status != INIT_STATE) return Z_STREAM_ERROR;
+
+ strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+ if (length < MIN_MATCH) return Z_OK;
+ if (length > MAX_DIST(s)) {
+ length = MAX_DIST(s);
+#ifndef USE_DICT_HEAD
+ dictionary += dictLength - length; /* use the tail of the dictionary */
+#endif
+ }
+ zmemcpy((charf *)s->window, dictionary, length);
+ s->strstart = length;
+ s->block_start = (long)length;
+
+ /* Insert all strings in the hash table (except for the last two bytes).
+ * s->lookahead stays null, so s->ins_h will be recomputed at the next
+ * call of fill_window.
+ */
+ s->ins_h = s->window[0];
+ UPDATE_HASH(s, s->ins_h, s->window[1]);
+ for (n = 0; n <= length - MIN_MATCH; n++) {
+ INSERT_STRING(s, n, hash_head);
+ }
+ if (hash_head) hash_head = 0; /* to make compiler happy */
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int deflateReset (strm)
+ z_streamp strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->noheader < 0) {
+ s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */
+ }
+ s->status = s->noheader ? BUSY_STATE : INIT_STATE;
+ strm->adler = 1;
+ s->last_flush = Z_NO_FLUSH;
+
+ _tr_init(s);
+ lm_init(s);
+
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int deflateParams(strm, level, strategy)
+ z_streamp strm;
+ int level;
+ int strategy;
+{
+ deflate_state *s;
+ compress_func func;
+ int err = Z_OK;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = (deflate_state *) strm->state;
+
+ if (level == Z_DEFAULT_COMPRESSION) {
+ level = 6;
+ }
+ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
+ return Z_STREAM_ERROR;
+ }
+ func = configuration_table[s->level].func;
+
+ if (func != configuration_table[level].func && strm->total_in != 0) {
+ /* Flush the last buffer: */
+ err = deflate(strm, Z_PARTIAL_FLUSH);
+ }
+ if (s->level != level) {
+ s->level = level;
+ s->max_lazy_match = configuration_table[level].max_lazy;
+ s->good_match = configuration_table[level].good_length;
+ s->nice_match = configuration_table[level].nice_length;
+ s->max_chain_length = configuration_table[level].max_chain;
+ }
+ s->strategy = strategy;
+ return err;
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+ z_streamp strm;
+{
+ deflate_state *s = (deflate_state *) strm->state;
+ unsigned len = s->pending;
+
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ if (strm->next_out != Z_NULL) {
+ zmemcpy(strm->next_out, s->pending_out, len);
+ strm->next_out += len;
+ }
+ s->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ s->pending -= len;
+ if (s->pending == 0) {
+ s->pending_out = s->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int deflate (strm, flush)
+ z_streamp strm;
+ int flush;
+{
+ int old_flush; /* value of flush param for previous deflate call */
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ flush > Z_FINISH || flush < 0) {
+ return Z_STREAM_ERROR;
+ }
+ s = (deflate_state *) strm->state;
+
+ if ((strm->next_in == Z_NULL && strm->avail_in != 0) ||
+ (s->status == FINISH_STATE && flush != Z_FINISH)) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ s->strm = strm; /* just in case */
+ old_flush = s->last_flush;
+ s->last_flush = flush;
+
+ /* Write the zlib header */
+ if (s->status == INIT_STATE) {
+
+ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+ uInt level_flags = (s->level-1) >> 1;
+
+ if (level_flags > 3) level_flags = 3;
+ header |= (level_flags << 6);
+ if (s->strstart != 0) header |= PRESET_DICT;
+ header += 31 - (header % 31);
+
+ s->status = BUSY_STATE;
+ putShortMSB(s, header);
+
+ /* Save the adler32 of the preset dictionary: */
+ if (s->strstart != 0) {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ strm->adler = 1L;
+ }
+
+ /* Flush as much pending output as possible */
+ if (s->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ s->last_flush = -1;
+ return Z_OK;
+ }
+
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Z_FINISH, we keep
+ * returning Z_STREAM_END instead of Z_BUFF_ERROR.
+ */
+ } else if (strm->avail_in == 0 && flush <= old_flush &&
+ flush != Z_FINISH) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (s->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || s->lookahead != 0 ||
+ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+ block_state bstate;
+
+ bstate = (*(configuration_table[s->level].func))(s, flush);
+
+ if (bstate == finish_started || bstate == finish_done) {
+ s->status = FINISH_STATE;
+ }
+ if (bstate == need_more || bstate == finish_started) {
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+ }
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+ if (bstate == block_done) {
+ if (flush == Z_PARTIAL_FLUSH) {
+ _tr_align(s);
+ } else if (flush == Z_PACKET_FLUSH) {
+ /* Output just the 3-bit `stored' block type value,
+ but not a zero length. */
+ _tr_stored_type_only(s);
+ } else { /* FULL_FLUSH or SYNC_FLUSH */
+ _tr_stored_block(s, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(s); /* forget history */
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+ return Z_OK;
+ }
+ }
+ }
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (s->noheader) return Z_STREAM_END;
+
+ /* Write the zlib trailer (adler32) */
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ s->noheader = -1; /* write the trailer only once! */
+ return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int deflateEnd (strm)
+ z_streamp strm;
+{
+ int status;
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = (deflate_state *) strm->state;
+
+ status = s->status;
+ if (status != INIT_STATE && status != BUSY_STATE &&
+ status != FINISH_STATE) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* Deallocate in reverse order of allocations: */
+ TRY_FREE(strm, s->pending_buf);
+ TRY_FREE(strm, s->head);
+ TRY_FREE(strm, s->prev);
+ TRY_FREE(strm, s->window);
+
+ ZFREE(strm, s);
+ strm->state = Z_NULL;
+
+ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ */
+int deflateCopy (dest, source)
+ z_streamp dest;
+ z_streamp source;
+{
+ deflate_state *ds;
+ deflate_state *ss;
+ ushf *overlay;
+
+ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ ss = (deflate_state *) source->state;
+
+ zmemcpy(dest, source, sizeof(*dest));
+
+ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+ if (ds == Z_NULL) return Z_MEM_ERROR;
+ dest->state = (struct internal_state FAR *) ds;
+ zmemcpy(ds, ss, sizeof(*ds));
+ ds->strm = dest;
+
+ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
+ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
+ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+ ds->pending_buf = (uchf *) overlay;
+
+ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+ ds->pending_buf == Z_NULL) {
+ deflateEnd (dest);
+ return Z_MEM_ERROR;
+ }
+ /* ??? following zmemcpy doesn't work for 16-bit MSDOS */
+ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+ ds->l_desc.dyn_tree = ds->dyn_ltree;
+ ds->d_desc.dyn_tree = ds->dyn_dtree;
+ ds->bl_desc.dyn_tree = ds->bl_tree;
+
+ return Z_OK;
+}
+
+/* ===========================================================================
+ * Return the number of bytes of output which are immediately available
+ * for output from the decompressor.
+ */
+int deflateOutputPending (strm)
+ z_streamp strm;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return 0;
+
+ return ((deflate_state *)(strm->state))->pending;
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+ z_streamp strm;
+ charf *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ if (!((deflate_state *)(strm->state))->noheader) {
+ strm->adler = adler32(strm->adler, strm->next_in, len);
+ }
+ zmemcpy(buf, strm->next_in, len);
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+}
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ int nice_match = s->nice_match; /* stop if match long enough */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2:
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ if ((uInt)best_len <= s->lookahead) return best_len;
+ return s->lookahead;
+}
+#endif /* ASMV */
+
+#ifdef DEBUG_ZLIB
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (zmemcmp((charf *)s->window + match,
+ (charf *)s->window + start, length) != EQUAL) {
+ fprintf(stderr, " start %u, match %u, length %d\n",
+ start, match, length);
+ do {
+ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+ } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (z_verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if strstart == 0
+ * and lookahead == 1 (input done one byte at time)
+ */
+ more--;
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ } else if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ zmemcpy((charf *)s->window, (charf *)s->window+wsize,
+ (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, (charf *)s->window + s->strstart + s->lookahead,
+ more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead >= MIN_MATCH) {
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, eof) { \
+ _tr_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), \
+ (ulg)((long)s->strstart - s->block_start), \
+ (eof)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, eof) { \
+ FLUSH_BLOCK_ONLY(s, eof); \
+ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ ulg max_block_size = 0xffff;
+ ulg max_start;
+
+ if (max_block_size > s->pending_buf_size - 5) {
+ max_block_size = s->pending_buf_size - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for (;;) {
+ /* Fill the window as much as possible: */
+ if (s->lookahead <= 1) {
+
+ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+ s->block_start >= (long)s->w_size, "slide too late");
+
+ fill_window(s);
+ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+ Assert(s->block_start >= 0L, "block gone");
+
+ s->strstart += s->lookahead;
+ s->lookahead = 0;
+
+ /* Emit a stored block if pending_buf will be full: */
+ max_start = s->block_start + max_block_size;
+ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ s->lookahead = (uInt)(s->strstart - max_start);
+ s->strstart = (uInt)max_start;
+ FLUSH_BLOCK(s, 0);
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+ FLUSH_BLOCK(s, 0);
+ }
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ bflush = _tr_tally(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in hash table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ bflush = _tr_tally (s, 0, s->window[s->strstart]);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED ||
+ (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR))) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ bflush = _tr_tally(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, 0);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ if (_tr_tally (s, 0, s->window[s->strstart-1])) {
+ FLUSH_BLOCK_ONLY(s, 0);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return need_more;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally (s, 0, s->window[s->strstart-1]);
+ s->match_available = 0;
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+/* --- deflate.c */
+
+/* +++ trees.c */
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-1996 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* From: trees.c,v 1.11 1996/07/24 13:41:06 me Exp $ */
+
+/* #include "deflate.h" */
+
+#ifdef DEBUG_ZLIB
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+local uch dist_code[512];
+/* distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+local uch length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+struct static_tree_desc_s {
+ ct_data *static_tree; /* static tree or NULL */
+ intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local static_tree_desc static_bl_desc =
+{(ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+ ct_data *dtree));
+local void set_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifndef DEBUG_ZLIB
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG_ZLIB */
+# define send_code(s, c, tree) \
+ { if (verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+#define d_code(dist) \
+ ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. dist_code[256] and dist_code[257] are never
+ * used.
+ */
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG_ZLIB
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracevv((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (value << s->bi_valid);
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG_ZLIB */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (val << s->bi_valid);\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG_ZLIB */
+
+
+#define MAX(a,b) (a >= b ? a : b)
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables. In a multi-threaded environment,
+ * this function may be called by two threads concurrently, but this is
+ * harmless since both invocations do exactly the same thing.
+ */
+local void tr_static_init()
+{
+ static int static_init_done = 0;
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ if (static_init_done) return;
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "tr_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+ }
+ static_init_done = 1;
+}
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void _tr_init(s)
+ deflate_state *s;
+{
+ tr_static_init();
+
+ s->compressed_len = 0L;
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = &static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = &static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = &static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG_ZLIB
+ s->bits_sent = 0L;
+#endif
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ ct_data *stree = desc->stat_desc->static_tree;
+ intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if (tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void _tr_stored_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
+ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* Send just the `stored block' type code without any length bytes or data.
+ */
+void _tr_stored_type_only(s)
+ deflate_state *s;
+{
+ send_bits(s, (STORED_BLOCK << 1), 3);
+ bi_windup(s);
+ s->compressed_len = (s->compressed_len + 3) & ~7L;
+}
+
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void _tr_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+ bi_flush(s);
+ /* Of the 10 bits for the empty block, we have already sent
+ * (10 - bi_valid) bits. The lookahead for the last real code (before
+ * the EOB of the previous block) was thus at least one plus the length
+ * of the EOB plus what we have just sent of the empty static block.
+ */
+ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+ s->compressed_len += 10L;
+ bi_flush(s);
+ }
+ s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file. This function
+ * returns the total compressed length for the file so far.
+ */
+ulg _tr_flush_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex = 0; /* index of last bit length code of non zero freq */
+
+ /* Build the Huffman trees unless a stored block is forced */
+ if (s->level > 0) {
+
+ /* Check if the file is ascii or binary */
+ if (s->data_type == Z_UNKNOWN) set_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute first the block length in bytes*/
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->last_lit));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ } else {
+ Assert(buf != (char*)0, "lost buf");
+ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+ }
+
+ /* If compression failed and this is the first and last block,
+ * and if the .zip file can be seeked (to rewrite the local header),
+ * the whole file is transformed into a stored file:
+ */
+#ifdef STORED_FILE_OK
+# ifdef FORCE_STORED_FILE
+ if (eof && s->compressed_len == 0L) { /* force stored file */
+# else
+ if (stored_len <= opt_lenb && eof && s->compressed_len==0L && seekable()) {
+# endif
+ /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */
+ if (buf == (charf*)0) error ("block vanished");
+
+ copy_block(s, buf, (unsigned)stored_len, 0); /* without header */
+ s->compressed_len = stored_len << 3;
+ s->method = STORED;
+ } else
+#endif /* STORED_FILE_OK */
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) { /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+ /* 4: two words for the lengths */
+#endif
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ _tr_stored_block(s, buf, stored_len, eof);
+
+#ifdef FORCE_STATIC
+ } else if (static_lenb >= 0) { /* force static trees */
+#else
+ } else if (static_lenb == opt_lenb) {
+#endif
+ send_bits(s, (STATIC_TREES<<1)+eof, 3);
+ compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+ s->compressed_len += 3 + s->static_len;
+ } else {
+ send_bits(s, (DYN_TREES<<1)+eof, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+ s->compressed_len += 3 + s->opt_len;
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ init_block(s);
+
+ if (eof) {
+ bi_windup(s);
+ s->compressed_len += 7; /* align on byte boundary */
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*eof));
+
+ return s->compressed_len >> 3;
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int _tr_tally (s, dist, lc)
+ deflate_state *s;
+ unsigned dist; /* distance of matched string */
+ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->d_buf[s->last_lit] = (ush)dist;
+ s->l_buf[s->last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
+
+ s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+
+ /* Try to guess if it is profitable to stop the current block here */
+ if (s->level > 2 && (s->last_lit & 0xfff) == 0) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)s->last_lit*8L;
+ ulg in_length = (ulg)((long)s->strstart - s->block_start);
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)s->dyn_dtree[dcode].Freq *
+ (5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+ s->last_lit, in_length, out_length,
+ 100L - out_length*100L/in_length));
+ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+ }
+ return (s->last_lit == s->lit_bufsize-1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ ct_data *ltree; /* literal tree */
+ ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->last_lit != 0) do {
+ dist = s->d_buf[lx];
+ lc = s->l_buf[lx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow");
+
+ } while (lx < s->last_lit);
+
+ send_code(s, END_BLOCK, ltree);
+ s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+local void set_data_type(s)
+ deflate_state *s;
+{
+ int n = 0;
+ unsigned ascii_freq = 0;
+ unsigned bin_freq = 0;
+ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq;
+ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq;
+ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
+ s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII);
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG_ZLIB
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG_ZLIB
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG_ZLIB
+ s->bits_sent += (ulg)len<<3;
+#endif
+ /* bundle up the put_byte(s, *buf++) calls */
+ zmemcpy(&s->pending_buf[s->pending], buf, len);
+ s->pending += len;
+}
+/* --- trees.c */
+
+/* +++ inflate.c */
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+
+/* +++ infblock.h */
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+extern inflate_blocks_statef * inflate_blocks_new OF((
+ z_streamp z,
+ check_func c, /* check function */
+ uInt w)); /* window size */
+
+extern int inflate_blocks OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int)); /* initial return code */
+
+extern void inflate_blocks_reset OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ uLongf *)); /* check value on output */
+
+extern int inflate_blocks_free OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ uLongf *)); /* check value on output */
+
+extern void inflate_set_dictionary OF((
+ inflate_blocks_statef *s,
+ const Bytef *d, /* dictionary */
+ uInt n)); /* dictionary length */
+
+extern int inflate_addhistory OF((
+ inflate_blocks_statef *,
+ z_streamp));
+
+extern int inflate_packet_flush OF((
+ inflate_blocks_statef *));
+/* --- infblock.h */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_blocks_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* inflate private state */
+struct internal_state {
+
+ /* mode */
+ enum {
+ METHOD, /* waiting for method byte */
+ FLAG, /* waiting for flag byte */
+ DICT4, /* four dictionary check bytes to go */
+ DICT3, /* three dictionary check bytes to go */
+ DICT2, /* two dictionary check bytes to go */
+ DICT1, /* one dictionary check byte to go */
+ DICT0, /* waiting for inflateSetDictionary */
+ BLOCKS, /* decompressing blocks */
+ CHECK4, /* four check bytes to go */
+ CHECK3, /* three check bytes to go */
+ CHECK2, /* two check bytes to go */
+ CHECK1, /* one check byte to go */
+ DONE, /* finished check, done */
+ BAD} /* got an error--stay here */
+ mode; /* current inflate mode */
+
+ /* mode dependent information */
+ union {
+ uInt method; /* if FLAGS, method byte */
+ struct {
+ uLong was; /* computed check value */
+ uLong need; /* stream check value */
+ } check; /* if CHECK, check values to compare */
+ uInt marker; /* if BAD, inflateSync's marker bytes count */
+ } sub; /* submode */
+
+ /* mode independent information */
+ int nowrap; /* flag for no wrapper */
+ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
+ inflate_blocks_statef
+ *blocks; /* current inflate_blocks state */
+
+};
+
+
+int inflateReset(z)
+z_streamp z;
+{
+ uLong c;
+
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->total_in = z->total_out = 0;
+ z->msg = Z_NULL;
+ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+ inflate_blocks_reset(z->state->blocks, z, &c);
+ Trace((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+
+int inflateEnd(z)
+z_streamp z;
+{
+ uLong c;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->blocks != Z_NULL)
+ inflate_blocks_free(z->state->blocks, z, &c);
+ ZFREE(z, z->state);
+ z->state = Z_NULL;
+ Trace((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+
+int inflateInit2_(z, w, version, stream_size)
+z_streamp z;
+int w;
+const char *version;
+int stream_size;
+{
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != sizeof(z_stream))
+ return Z_VERSION_ERROR;
+
+ /* initialize state */
+ if (z == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->msg = Z_NULL;
+#ifndef NO_ZCFUNCS
+ if (z->zalloc == Z_NULL)
+ {
+ z->zalloc = zcalloc;
+ z->opaque = (voidpf)0;
+ }
+ if (z->zfree == Z_NULL) z->zfree = zcfree;
+#endif
+ if ((z->state = (struct internal_state FAR *)
+ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
+ return Z_MEM_ERROR;
+ z->state->blocks = Z_NULL;
+
+ /* handle undocumented nowrap option (no zlib header or check) */
+ z->state->nowrap = 0;
+ if (w < 0)
+ {
+ w = - w;
+ z->state->nowrap = 1;
+ }
+
+ /* set window size */
+ if (w < 8 || w > 15)
+ {
+ inflateEnd(z);
+ return Z_STREAM_ERROR;
+ }
+ z->state->wbits = (uInt)w;
+
+ /* create inflate_blocks state */
+ if ((z->state->blocks =
+ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w))
+ == Z_NULL)
+ {
+ inflateEnd(z);
+ return Z_MEM_ERROR;
+ }
+ Trace((stderr, "inflate: allocated\n"));
+
+ /* reset state */
+ inflateReset(z);
+ return Z_OK;
+}
+
+
+int inflateInit_(z, version, stream_size)
+z_streamp z;
+const char *version;
+int stream_size;
+{
+ return inflateInit2_(z, DEF_WBITS, version, stream_size);
+}
+
+
+#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;}
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+int inflate(z, f)
+z_streamp z;
+int f;
+{
+ int r;
+ uInt b;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL || f < 0)
+ return Z_STREAM_ERROR;
+ r = Z_BUF_ERROR;
+ while (1) switch (z->state->mode)
+ {
+ case METHOD:
+ NEEDBYTE
+ if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"unknown compression method";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"invalid window size";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ z->state->mode = FLAG;
+ case FLAG:
+ NEEDBYTE
+ b = NEXTBYTE;
+ if (((z->state->sub.method << 8) + b) % 31)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect header check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Trace((stderr, "inflate: zlib header ok\n"));
+ if (!(b & PRESET_DICT))
+ {
+ z->state->mode = BLOCKS;
+ break;
+ }
+ z->state->mode = DICT4;
+ case DICT4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = DICT3;
+ case DICT3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = DICT2;
+ case DICT2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = DICT1;
+ case DICT1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+ z->adler = z->state->sub.check.need;
+ z->state->mode = DICT0;
+ return Z_NEED_DICT;
+ case DICT0:
+ z->state->mode = BAD;
+ z->msg = (char*)"need dictionary";
+ z->state->sub.marker = 0; /* can try inflateSync */
+ return Z_STREAM_ERROR;
+ case BLOCKS:
+ r = inflate_blocks(z->state->blocks, z, r);
+ if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0)
+ r = inflate_packet_flush(z->state->blocks);
+ if (r == Z_DATA_ERROR)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0; /* can try inflateSync */
+ break;
+ }
+ if (r != Z_STREAM_END)
+ return r;
+ r = Z_OK;
+ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+ if (z->state->nowrap)
+ {
+ z->state->mode = DONE;
+ break;
+ }
+ z->state->mode = CHECK4;
+ case CHECK4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = CHECK3;
+ case CHECK3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = CHECK2;
+ case CHECK2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = CHECK1;
+ case CHECK1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+
+ if (z->state->sub.check.was != z->state->sub.check.need)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect data check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Trace((stderr, "inflate: zlib check ok\n"));
+ z->state->mode = DONE;
+ case DONE:
+ return Z_STREAM_END;
+ case BAD:
+ return Z_DATA_ERROR;
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ empty:
+ if (f != Z_PACKET_FLUSH)
+ return r;
+ z->state->mode = BAD;
+ z->msg = (char *)"need more for packet flush";
+ z->state->sub.marker = 0; /* can try inflateSync */
+ return Z_DATA_ERROR;
+}
+
+
+int inflateSetDictionary(z, dictionary, dictLength)
+z_streamp z;
+const Bytef *dictionary;
+uInt dictLength;
+{
+ uInt length = dictLength;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0)
+ return Z_STREAM_ERROR;
+
+ if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR;
+ z->adler = 1L;
+
+ if (length >= ((uInt)1<<z->state->wbits))
+ {
+ length = (1<<z->state->wbits)-1;
+ dictionary += dictLength - length;
+ }
+ inflate_set_dictionary(z->state->blocks, dictionary, length);
+ z->state->mode = BLOCKS;
+ return Z_OK;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output. The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS). On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+
+int inflateIncomp(z)
+z_stream *z;
+{
+ if (z->state->mode != BLOCKS)
+ return Z_DATA_ERROR;
+ return inflate_addhistory(z->state->blocks, z);
+}
+
+
+int inflateSync(z)
+z_streamp z;
+{
+ uInt n; /* number of bytes to look at */
+ Bytef *p; /* pointer to bytes */
+ uInt m; /* number of marker bytes found in a row */
+ uLong r, w; /* temporaries to save total_in and total_out */
+
+ /* set up */
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->mode != BAD)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0;
+ }
+ if ((n = z->avail_in) == 0)
+ return Z_BUF_ERROR;
+ p = z->next_in;
+ m = z->state->sub.marker;
+
+ /* search */
+ while (n && m < 4)
+ {
+ if (*p == (Byte)(m < 2 ? 0 : 0xff))
+ m++;
+ else if (*p)
+ m = 0;
+ else
+ m = 4 - m;
+ p++, n--;
+ }
+
+ /* restore */
+ z->total_in += p - z->next_in;
+ z->next_in = p;
+ z->avail_in = n;
+ z->state->sub.marker = m;
+
+ /* return no joy or set up to restart on a new block */
+ if (m != 4)
+ return Z_DATA_ERROR;
+ r = z->total_in; w = z->total_out;
+ inflateReset(z);
+ z->total_in = r; z->total_out = w;
+ z->state->mode = BLOCKS;
+ return Z_OK;
+}
+
+#undef NEEDBYTE
+#undef NEXTBYTE
+/* --- inflate.c */
+
+/* +++ infblock.c */
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "infblock.h" */
+
+/* +++ inftrees.h */
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+ that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+ union {
+ struct {
+ Byte Exop; /* number of extra bits or operation */
+ Byte Bits; /* number of bits in this code or subcode */
+ } what;
+ Bytef *pad; /* pad structure to a power of 2 (4 bytes for */
+ } word; /* 16-bit, 8 bytes for 32-bit machines) */
+ union {
+ uInt Base; /* literal, length base, or distance base */
+ inflate_huft *Next; /* pointer to next level of table */
+ } more;
+};
+
+#ifdef DEBUG_ZLIB
+ extern uInt inflate_hufts;
+#endif
+
+extern int inflate_trees_bits OF((
+ uIntf *, /* 19 code lengths */
+ uIntf *, /* bits tree desired/actual depth */
+ inflate_huft * FAR *, /* bits tree result */
+ z_streamp )); /* for zalloc, zfree functions */
+
+extern int inflate_trees_dynamic OF((
+ uInt, /* number of literal/length codes */
+ uInt, /* number of distance codes */
+ uIntf *, /* that many (total) code lengths */
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *, /* distance tree result */
+ z_streamp )); /* for zalloc, zfree functions */
+
+extern int inflate_trees_fixed OF((
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *)); /* distance tree result */
+
+extern int inflate_trees_free OF((
+ inflate_huft *, /* tables to free */
+ z_streamp )); /* for zfree function */
+
+/* --- inftrees.h */
+
+/* +++ infcodes.h */
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+extern inflate_codes_statef *inflate_codes_new OF((
+ uInt, uInt,
+ inflate_huft *, inflate_huft *,
+ z_streamp ));
+
+extern int inflate_codes OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+extern void inflate_codes_free OF((
+ inflate_codes_statef *,
+ z_streamp ));
+
+/* --- infcodes.h */
+
+/* +++ infutil.h */
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+#ifndef _INFUTIL_H
+#define _INFUTIL_H
+
+typedef enum {
+ TYPE, /* get type bits (3, including end bit) */
+ LENS, /* get lengths for stored */
+ STORED, /* processing stored block */
+ TABLE, /* get table lengths */
+ BTREE, /* get bit lengths tree for a dynamic block */
+ DTREE, /* get length, distance trees for a dynamic block */
+ CODES, /* processing fixed or dynamic block */
+ DRY, /* output remaining window bytes */
+ DONEB, /* finished last block, done */
+ BADB} /* got a data error--stuck here */
+inflate_block_mode;
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+ /* mode */
+ inflate_block_mode mode; /* current inflate_block mode */
+
+ /* mode dependent information */
+ union {
+ uInt left; /* if STORED, bytes left to copy */
+ struct {
+ uInt table; /* table lengths (14 bits) */
+ uInt index; /* index into blens (or border) */
+ uIntf *blens; /* bit lengths of codes */
+ uInt bb; /* bit length tree depth */
+ inflate_huft *tb; /* bit length decoding tree */
+ } trees; /* if DTREE, decoding info for trees */
+ struct {
+ inflate_huft *tl;
+ inflate_huft *td; /* trees to free */
+ inflate_codes_statef
+ *codes;
+ } decode; /* if CODES, current state */
+ } sub; /* submode */
+ uInt last; /* true if this block is the last block */
+
+ /* mode independent information */
+ uInt bitk; /* bits in bit buffer */
+ uLong bitb; /* bit buffer */
+ Bytef *window; /* sliding window */
+ Bytef *end; /* one byte after sliding window */
+ Bytef *read; /* window read pointer */
+ Bytef *write; /* window write pointer */
+ check_func checkfn; /* check function */
+ uLong check; /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/* update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/* get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/* output bytes */
+#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
+#define WWRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WWRAP if(m==0){FLUSH WWRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/* load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
+extern uInt inflate_mask[17];
+
+/* copy as much as possible from the sliding window to the output area */
+extern int inflate_flush OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#endif
+/* --- infutil.h */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local const uInt border[] = { /* Order of the bit length code lengths */
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+ Notes beyond the 1.93a appnote.txt:
+
+ 1. Distance pointers never point before the beginning of the output
+ stream.
+ 2. Distance pointers can point back across blocks, up to 32k away.
+ 3. There is an implied maximum of 7 bits for the bit length table and
+ 15 bits for the actual data.
+ 4. If only one code exists, then it is encoded using one bit. (Zero
+ would be more efficient, but perhaps a little confusing.) If two
+ codes exist, they are coded using one bit each (0 and 1).
+ 5. There is no way of sending zero distance codes--a dummy must be
+ sent if there are none. (History: a pre 2.0 version of PKZIP would
+ store blocks with no distance codes, but this was discovered to be
+ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
+ zero distance codes, which is sent as one code of zero bits in
+ length.
+ 6. There are up to 286 literal/length codes. Code 256 represents the
+ end-of-block. Note however that the static length tree defines
+ 288 codes just to fill out the Huffman codes. Codes 286 and 287
+ cannot be used though, since there is no length base or extra bits
+ defined for them. Similarily, there are up to 30 distance codes.
+ However, static trees define 32 codes (all 5 bits) to fill out the
+ Huffman codes, but the last two had better not show up in the data.
+ 7. Unzip can check dynamic Huffman blocks for complete code sets.
+ The exception is that a single code would not be complete (see #4).
+ 8. The five bits following the block type is really the number of
+ literal codes sent minus 257.
+ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+ (1+6+6). Therefore, to output three times the length, you output
+ three codes (1+1+1), whereas to output four times the same length,
+ you only need two codes (1+3). Hmm.
+ 10. In the tree reconstruction algorithm, Code = Code + Increment
+ only if BitLength(i) is not zero. (Pretty obvious.)
+ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
+ 12. Note: length code 284 can represent 227-258, but length code 285
+ really is 258. The last length deserves its own, short code
+ since it gets used a lot in very redundant files. The length
+ 258 is special since 258 - 3 (the min match length) is 255.
+ 13. The literal/length and distance code bit lengths are read as a
+ single stream of lengths. It is possible (and advantageous) for
+ a repeat code (16, 17, or 18) to go across the boundary between
+ the two sets of lengths.
+ */
+
+
+void inflate_blocks_reset(s, z, c)
+inflate_blocks_statef *s;
+z_streamp z;
+uLongf *c;
+{
+ if (s->checkfn != Z_NULL)
+ *c = s->check;
+ if (s->mode == BTREE || s->mode == DTREE)
+ ZFREE(z, s->sub.trees.blens);
+ if (s->mode == CODES)
+ {
+ inflate_codes_free(s->sub.decode.codes, z);
+ inflate_trees_free(s->sub.decode.td, z);
+ inflate_trees_free(s->sub.decode.tl, z);
+ }
+ s->mode = TYPE;
+ s->bitk = 0;
+ s->bitb = 0;
+ s->read = s->write = s->window;
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(0L, Z_NULL, 0);
+ Trace((stderr, "inflate: blocks reset\n"));
+}
+
+
+inflate_blocks_statef *inflate_blocks_new(z, c, w)
+z_streamp z;
+check_func c;
+uInt w;
+{
+ inflate_blocks_statef *s;
+
+ if ((s = (inflate_blocks_statef *)ZALLOC
+ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
+ return s;
+ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
+ {
+ ZFREE(z, s);
+ return Z_NULL;
+ }
+ s->end = s->window + w;
+ s->checkfn = c;
+ s->mode = TYPE;
+ Trace((stderr, "inflate: blocks allocated\n"));
+ inflate_blocks_reset(s, z, &s->check);
+ return s;
+}
+
+
+#ifdef DEBUG_ZLIB
+ extern uInt inflate_hufts;
+#endif
+int inflate_blocks(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt t; /* temporary storage */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input based on current state */
+ while (1) switch (s->mode)
+ {
+ case TYPE:
+ NEEDBITS(3)
+ t = (uInt)b & 7;
+ s->last = t & 1;
+ switch (t >> 1)
+ {
+ case 0: /* stored */
+ Trace((stderr, "inflate: stored block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ t = k & 7; /* go to byte boundary */
+ DUMPBITS(t)
+ s->mode = LENS; /* get length of stored block */
+ break;
+ case 1: /* fixed */
+ Trace((stderr, "inflate: fixed codes block%s\n",
+ s->last ? " (last)" : ""));
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+
+ inflate_trees_fixed(&bl, &bd, &tl, &td);
+ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+ if (s->sub.decode.codes == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.decode.tl = Z_NULL; /* don't try to free these */
+ s->sub.decode.td = Z_NULL;
+ }
+ DUMPBITS(3)
+ s->mode = CODES;
+ break;
+ case 2: /* dynamic */
+ Trace((stderr, "inflate: dynamic codes block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ s->mode = TABLE;
+ break;
+ case 3: /* illegal */
+ DUMPBITS(3)
+ s->mode = BADB;
+ z->msg = (char*)"invalid block type";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ break;
+ case LENS:
+ NEEDBITS(32)
+ if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
+ {
+ s->mode = BADB;
+ z->msg = (char*)"invalid stored block lengths";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ s->sub.left = (uInt)b & 0xffff;
+ b = k = 0; /* dump bits */
+ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
+ s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE);
+ break;
+ case STORED:
+ if (n == 0)
+ LEAVE
+ NEEDOUT
+ t = s->sub.left;
+ if (t > n) t = n;
+ if (t > m) t = m;
+ zmemcpy(q, p, t);
+ p += t; n -= t;
+ q += t; m -= t;
+ if ((s->sub.left -= t) != 0)
+ break;
+ Tracev((stderr, "inflate: stored end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ s->mode = s->last ? DRY : TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14)
+ s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+ {
+ s->mode = BADB;
+ z->msg = (char*)"too many length or distance symbols";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+#endif
+ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+ if (t < 19)
+ t = 19;
+ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ DUMPBITS(14)
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ s->mode = BTREE;
+ case BTREE:
+ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+ {
+ NEEDBITS(3)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+ DUMPBITS(3)
+ }
+ while (s->sub.trees.index < 19)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+ s->sub.trees.bb = 7;
+ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+ &s->sub.trees.tb, z);
+ if (t != Z_OK)
+ {
+ ZFREE(z, s->sub.trees.blens);
+ r = t;
+ if (r == Z_DATA_ERROR)
+ s->mode = BADB;
+ LEAVE
+ }
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: bits tree ok\n"));
+ s->mode = DTREE;
+ case DTREE:
+ while (t = s->sub.trees.table,
+ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+ {
+ inflate_huft *h;
+ uInt i, j, c;
+
+ t = s->sub.trees.bb;
+ NEEDBITS(t)
+ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+ t = h->word.what.Bits;
+ c = h->more.Base;
+ if (c < 16)
+ {
+ DUMPBITS(t)
+ s->sub.trees.blens[s->sub.trees.index++] = c;
+ }
+ else /* c == 16..18 */
+ {
+ i = c == 18 ? 7 : c - 14;
+ j = c == 18 ? 11 : 3;
+ NEEDBITS(t + i)
+ DUMPBITS(t)
+ j += (uInt)b & inflate_mask[i];
+ DUMPBITS(i)
+ i = s->sub.trees.index;
+ t = s->sub.trees.table;
+ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+ (c == 16 && i < 1))
+ {
+ inflate_trees_free(s->sub.trees.tb, z);
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = BADB;
+ z->msg = (char*)"invalid bit length repeat";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+ do {
+ s->sub.trees.blens[i++] = c;
+ } while (--j);
+ s->sub.trees.index = i;
+ }
+ }
+ inflate_trees_free(s->sub.trees.tb, z);
+ s->sub.trees.tb = Z_NULL;
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+ inflate_codes_statef *c;
+
+ bl = 9; /* must be <= 9 for lookahead assumptions */
+ bd = 6; /* must be <= 9 for lookahead assumptions */
+ t = s->sub.trees.table;
+#ifdef DEBUG_ZLIB
+ inflate_hufts = 0;
+#endif
+ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+ s->sub.trees.blens, &bl, &bd, &tl, &td, z);
+ ZFREE(z, s->sub.trees.blens);
+ if (t != Z_OK)
+ {
+ if (t == (uInt)Z_DATA_ERROR)
+ s->mode = BADB;
+ r = t;
+ LEAVE
+ }
+ Tracev((stderr, "inflate: trees ok, %d * %d bytes used\n",
+ inflate_hufts, sizeof(inflate_huft)));
+ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+ {
+ inflate_trees_free(td, z);
+ inflate_trees_free(tl, z);
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.decode.codes = c;
+ s->sub.decode.tl = tl;
+ s->sub.decode.td = td;
+ }
+ s->mode = CODES;
+ case CODES:
+ UPDATE
+ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+ return inflate_flush(s, z, r);
+ r = Z_OK;
+ inflate_codes_free(s->sub.decode.codes, z);
+ inflate_trees_free(s->sub.decode.td, z);
+ inflate_trees_free(s->sub.decode.tl, z);
+ LOAD
+ Tracev((stderr, "inflate: codes end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ if (!s->last)
+ {
+ s->mode = TYPE;
+ break;
+ }
+ if (k > 7) /* return unused byte, if any */
+ {
+ Assert(k < 16, "inflate_codes grabbed too many bytes")
+ k -= 8;
+ n++;
+ p--; /* can always return one */
+ }
+ s->mode = DRY;
+ case DRY:
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ s->mode = DONEB;
+ case DONEB:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADB:
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+int inflate_blocks_free(s, z, c)
+inflate_blocks_statef *s;
+z_streamp z;
+uLongf *c;
+{
+ inflate_blocks_reset(s, z, c);
+ ZFREE(z, s->window);
+ ZFREE(z, s);
+ Trace((stderr, "inflate: blocks freed\n"));
+ return Z_OK;
+}
+
+
+void inflate_set_dictionary(s, d, n)
+inflate_blocks_statef *s;
+const Bytef *d;
+uInt n;
+{
+ zmemcpy((charf *)s->window, d, n);
+ s->read = s->write = s->window + n;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output. The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS). On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+int inflate_addhistory(s, z)
+inflate_blocks_statef *s;
+z_stream *z;
+{
+ uLong b; /* bit buffer */ /* NOT USED HERE */
+ uInt k; /* bits in bit buffer */ /* NOT USED HERE */
+ uInt t; /* temporary storage */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ if (s->read != s->write)
+ return Z_STREAM_ERROR;
+ if (s->mode != TYPE)
+ return Z_DATA_ERROR;
+
+ /* we're ready to rock */
+ LOAD
+ /* while there is input ready, copy to output buffer, moving
+ * pointers as needed.
+ */
+ while (n) {
+ t = n; /* how many to do */
+ /* is there room until end of buffer? */
+ if (t > m) t = m;
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(s->check, q, t);
+ zmemcpy(q, p, t);
+ q += t;
+ p += t;
+ n -= t;
+ z->total_out += t;
+ s->read = q; /* drag read pointer forward */
+/* WWRAP */ /* expand WWRAP macro by hand to handle s->read */
+ if (q == s->end) {
+ s->read = q = s->window;
+ m = WAVAIL;
+ }
+ }
+ UPDATE
+ return Z_OK;
+}
+
+
+/*
+ * At the end of a Deflate-compressed PPP packet, we expect to have seen
+ * a `stored' block type value but not the (zero) length bytes.
+ */
+int inflate_packet_flush(s)
+ inflate_blocks_statef *s;
+{
+ if (s->mode != LENS)
+ return Z_DATA_ERROR;
+ s->mode = TYPE;
+ return Z_OK;
+}
+/* --- infblock.c */
+
+/* +++ inftrees.c */
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "inftrees.h" */
+
+char inflate_copyright[] = " inflate 1.0.4 Copyright 1995-1996 Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+ uIntf *, /* code lengths in bits */
+ uInt, /* number of codes */
+ uInt, /* number of "simple" codes */
+ const uIntf *, /* list of base values for non-simple codes */
+ const uIntf *, /* list of extra bits for non-simple codes */
+ inflate_huft * FAR*,/* result: starting table */
+ uIntf *, /* maximum lookup bits (returns actual) */
+ z_streamp )); /* for zalloc function */
+
+local voidpf falloc OF((
+ voidpf, /* opaque pointer (not used) */
+ uInt, /* number of items */
+ uInt)); /* size of item */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ /* see note #13 above about 258 */
+local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
+local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+local const uInt cpdext[30] = { /* Extra bits for distance codes */
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+
+/*
+ Huffman code decoding is performed using a multi-level table lookup.
+ The fastest way to decode is to simply build a lookup table whose
+ size is determined by the longest code. However, the time it takes
+ to build this table can also be a factor if the data being decoded
+ is not very long. The most common codes are necessarily the
+ shortest codes, so those codes dominate the decoding time, and hence
+ the speed. The idea is you can have a shorter table that decodes the
+ shorter, more probable codes, and then point to subsidiary tables for
+ the longer codes. The time it costs to decode the longer codes is
+ then traded against the time it takes to make longer tables.
+
+ This results of this trade are in the variables lbits and dbits
+ below. lbits is the number of bits the first level table for literal/
+ length codes can decode in one step, and dbits is the same thing for
+ the distance codes. Subsequent tables are also less than or equal to
+ those sizes. These values may be adjusted either when all of the
+ codes are shorter than that, in which case the longest code length in
+ bits is used, or when the shortest code is *longer* than the requested
+ table size, in which case the length of the shortest code in bits is
+ used.
+
+ There are two different values for the two tables, since they code a
+ different number of possibilities each. The literal/length table
+ codes 286 possible values, or in a flat code, a little over eight
+ bits. The distance table codes 30 possible values, or a little less
+ than five bits, flat. The optimum values for speed end up being
+ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+ The optimum values may differ though from machine to machine, and
+ possibly even between compilers. Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15 /* maximum bit length of any code */
+#define N_MAX 288 /* maximum number of codes in any set */
+
+#ifdef DEBUG_ZLIB
+ uInt inflate_hufts;
+#endif
+
+local int huft_build(b, n, s, d, e, t, m, zs)
+uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
+uInt n; /* number of codes (assumed <= N_MAX) */
+uInt s; /* number of simple-valued codes (0..s-1) */
+const uIntf *d; /* list of base values for non-simple codes */
+const uIntf *e; /* list of extra bits for non-simple codes */
+inflate_huft * FAR *t; /* result: starting table */
+uIntf *m; /* maximum lookup bits, returns actual */
+z_streamp zs; /* for zalloc function */
+/* Given a list of code lengths and a maximum table size, make a set of
+ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
+ if the given code set is incomplete (the tables are still built in this
+ case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of
+ lengths), or Z_MEM_ERROR if not enough memory. */
+{
+
+ uInt a; /* counter for codes of length k */
+ uInt c[BMAX+1]; /* bit length count table */
+ uInt f; /* i repeats in table every f entries */
+ int g; /* maximum code length */
+ int h; /* table level */
+ register uInt i; /* counter, current code */
+ register uInt j; /* counter */
+ register int k; /* number of bits in current code */
+ int l; /* bits per table (returned in m) */
+ register uIntf *p; /* pointer into c[], b[], or v[] */
+ inflate_huft *q; /* points to current table */
+ struct inflate_huft_s r; /* table entry for structure assignment */
+ inflate_huft *u[BMAX]; /* table stack */
+ uInt v[N_MAX]; /* values in order of bit length */
+ register int w; /* bits before this table == (l * h) */
+ uInt x[BMAX+1]; /* bit offsets, then code stack */
+ uIntf *xp; /* pointer into x */
+ int y; /* number of dummy codes added */
+ uInt z; /* number of entries in current table */
+
+
+ /* Generate counts for each bit length */
+ p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+ C4 /* clear c[]--assume BMAX+1 is 16 */
+ p = b; i = n;
+ do {
+ c[*p++]++; /* assume all entries <= BMAX */
+ } while (--i);
+ if (c[0] == n) /* null input--all zero length codes */
+ {
+ *t = (inflate_huft *)Z_NULL;
+ *m = 0;
+ return Z_OK;
+ }
+
+
+ /* Find minimum and maximum length, bound *m by those */
+ l = *m;
+ for (j = 1; j <= BMAX; j++)
+ if (c[j])
+ break;
+ k = j; /* minimum code length */
+ if ((uInt)l < j)
+ l = j;
+ for (i = BMAX; i; i--)
+ if (c[i])
+ break;
+ g = i; /* maximum code length */
+ if ((uInt)l > i)
+ l = i;
+ *m = l;
+
+
+ /* Adjust last length count to fill out codes, if needed */
+ for (y = 1 << j; j < i; j++, y <<= 1)
+ if ((y -= c[j]) < 0)
+ return Z_DATA_ERROR;
+ if ((y -= c[i]) < 0)
+ return Z_DATA_ERROR;
+ c[i] += y;
+
+
+ /* Generate starting offsets into the value table for each length */
+ x[1] = j = 0;
+ p = c + 1; xp = x + 2;
+ while (--i) { /* note that i == g from above */
+ *xp++ = (j += *p++);
+ }
+
+
+ /* Make a table of values in order of bit lengths */
+ p = b; i = 0;
+ do {
+ if ((j = *p++) != 0)
+ v[x[j]++] = i;
+ } while (++i < n);
+ n = x[g]; /* set n to length of v */
+
+
+ /* Generate the Huffman codes and for each, make the table entries */
+ x[0] = i = 0; /* first Huffman code is zero */
+ p = v; /* grab values in bit order */
+ h = -1; /* no tables yet--level -1 */
+ w = -l; /* bits decoded == (l * h) */
+ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
+ q = (inflate_huft *)Z_NULL; /* ditto */
+ z = 0; /* ditto */
+
+ /* go through the bit lengths (k already is bits in shortest code) */
+ for (; k <= g; k++)
+ {
+ a = c[k];
+ while (a--)
+ {
+ /* here i is the Huffman code of length k bits for value *p */
+ /* make tables up to required level */
+ while (k > w + l)
+ {
+ h++;
+ w += l; /* previous table always l bits */
+
+ /* compute minimum size table less than or equal to l bits */
+ z = g - w;
+ z = z > (uInt)l ? l : z; /* table size upper limit */
+ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
+ { /* too few codes for k-w bit table */
+ f -= a + 1; /* deduct codes from patterns left */
+ xp = c + k;
+ if (j < z)
+ while (++j < z) /* try smaller tables up to z bits */
+ {
+ if ((f <<= 1) <= *++xp)
+ break; /* enough codes to use up j bits */
+ f -= *xp; /* else deduct codes from patterns */
+ }
+ }
+ z = 1 << j; /* table entries for j-bit table */
+
+ /* allocate and link in new table */
+ if ((q = (inflate_huft *)ZALLOC
+ (zs,z + 1,sizeof(inflate_huft))) == Z_NULL)
+ {
+ if (h)
+ inflate_trees_free(u[0], zs);
+ return Z_MEM_ERROR; /* not enough memory */
+ }
+#ifdef DEBUG_ZLIB
+ inflate_hufts += z + 1;
+#endif
+ *t = q + 1; /* link to list for huft_free() */
+ *(t = &(q->next)) = Z_NULL;
+ u[h] = ++q; /* table starts after link */
+
+ /* connect to last table, if there is one */
+ if (h)
+ {
+ x[h] = i; /* save pattern for backing up */
+ r.bits = (Byte)l; /* bits to dump before this table */
+ r.exop = (Byte)j; /* bits in this table */
+ r.next = q; /* pointer to this table */
+ j = i >> (w - l); /* (get around Turbo C bug) */
+ u[h-1][j] = r; /* connect to last table */
+ }
+ }
+
+ /* set up table entry in r */
+ r.bits = (Byte)(k - w);
+ if (p >= v + n)
+ r.exop = 128 + 64; /* out of values--invalid code */
+ else if (*p < s)
+ {
+ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
+ r.base = *p++; /* simple code is just the value */
+ }
+ else
+ {
+ r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
+ r.base = d[*p++ - s];
+ }
+
+ /* fill code-like entries with r */
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f)
+ q[j] = r;
+
+ /* backwards increment the k-bit code i */
+ for (j = 1 << (k - 1); i & j; j >>= 1)
+ i ^= j;
+ i ^= j;
+
+ /* backup over finished tables */
+ while ((i & ((1 << w) - 1)) != x[h])
+ {
+ h--; /* don't need to update q */
+ w -= l;
+ }
+ }
+ }
+
+
+ /* Return Z_BUF_ERROR if we were given an incomplete table */
+ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+int inflate_trees_bits(c, bb, tb, z)
+uIntf *c; /* 19 code lengths */
+uIntf *bb; /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+z_streamp z; /* for zfree function */
+{
+ int r;
+
+ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z);
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed dynamic bit lengths tree";
+ else if (r == Z_BUF_ERROR || *bb == 0)
+ {
+ inflate_trees_free(*tb, z);
+ z->msg = (char*)"incomplete dynamic bit lengths tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+}
+
+
+int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z)
+uInt nl; /* number of literal/length codes */
+uInt nd; /* number of distance codes */
+uIntf *c; /* that many (total) code lengths */
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+z_streamp z; /* for zfree function */
+{
+ int r;
+
+ /* build literal/length tree */
+ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z);
+ if (r != Z_OK || *bl == 0)
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed literal/length tree";
+ else if (r != Z_MEM_ERROR)
+ {
+ inflate_trees_free(*tl, z);
+ z->msg = (char*)"incomplete literal/length tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+ }
+
+ /* build distance tree */
+ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z);
+ if (r != Z_OK || (*bd == 0 && nl > 257))
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed distance tree";
+ else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+ r = Z_OK;
+ }
+#else
+ inflate_trees_free(*td, z);
+ z->msg = (char*)"incomplete distance tree";
+ r = Z_DATA_ERROR;
+ }
+ else if (r != Z_MEM_ERROR)
+ {
+ z->msg = (char*)"empty distance tree with lengths";
+ r = Z_DATA_ERROR;
+ }
+ inflate_trees_free(*tl, z);
+ return r;
+#endif
+ }
+
+ /* done */
+ return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+local int fixed_built = 0;
+#define FIXEDH 530 /* number of hufts used by fixed tables */
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+
+
+local voidpf falloc(q, n, s)
+voidpf q; /* opaque pointer */
+uInt n; /* number of items */
+uInt s; /* size of item */
+{
+ Assert(s == sizeof(inflate_huft) && n <= *(intf *)q,
+ "inflate_trees falloc overflow");
+ *(intf *)q -= n+s-s; /* s-s to avoid warning */
+ return (voidpf)(fixed_mem + *(intf *)q);
+}
+
+
+int inflate_trees_fixed(bl, bd, tl, td)
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+{
+ /* build fixed tables if not already (multiple overlapped executions ok) */
+ if (!fixed_built)
+ {
+ int k; /* temporary variable */
+ unsigned c[288]; /* length list for huft_build */
+ z_stream z; /* for falloc function */
+ int f = FIXEDH; /* number of hufts left in fixed_mem */
+
+ /* set up fake z_stream for memory routines */
+ z.zalloc = falloc;
+ z.zfree = Z_NULL;
+ z.opaque = (voidpf)&f;
+
+ /* literal table */
+ for (k = 0; k < 144; k++)
+ c[k] = 8;
+ for (; k < 256; k++)
+ c[k] = 9;
+ for (; k < 280; k++)
+ c[k] = 7;
+ for (; k < 288; k++)
+ c[k] = 8;
+ fixed_bl = 7;
+ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z);
+
+ /* distance table */
+ for (k = 0; k < 30; k++)
+ c[k] = 5;
+ fixed_bd = 5;
+ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z);
+
+ /* done */
+ Assert(f == 0, "invalid build of fixed tables");
+ fixed_built = 1;
+ }
+ *bl = fixed_bl;
+ *bd = fixed_bd;
+ *tl = fixed_tl;
+ *td = fixed_td;
+ return Z_OK;
+}
+
+
+int inflate_trees_free(t, z)
+inflate_huft *t; /* table to free */
+z_streamp z; /* for zfree function */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+ list of the tables it made, with the links in a dummy first entry of
+ each table. */
+{
+ register inflate_huft *p, *q, *r;
+
+ /* Reverse linked list */
+ p = Z_NULL;
+ q = t;
+ while (q != Z_NULL)
+ {
+ r = (q - 1)->next;
+ (q - 1)->next = p;
+ p = q;
+ q = r;
+ }
+ /* Go through linked list, freeing from the malloced (t[-1]) address. */
+ while (p != Z_NULL)
+ {
+ q = (--p)->next;
+ ZFREE(z,p);
+ p = q;
+ }
+ return Z_OK;
+}
+/* --- inftrees.c */
+
+/* +++ infcodes.c */
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "inftrees.h" */
+/* #include "infblock.h" */
+/* #include "infcodes.h" */
+/* #include "infutil.h" */
+
+/* +++ inffast.h */
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+extern int inflate_fast OF((
+ uInt,
+ uInt,
+ inflate_huft *,
+ inflate_huft *,
+ inflate_blocks_statef *,
+ z_streamp ));
+/* --- inffast.h */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+ /* mode */
+ enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ START, /* x: set up for LEN */
+ LEN, /* i: get length/literal/eob next */
+ LENEXT, /* i: getting length extra (have base) */
+ DIST, /* i: get distance next */
+ DISTEXT, /* i: getting distance extra */
+ COPY, /* o: copying bytes in window, waiting for space */
+ LIT, /* o: got literal, waiting for output space */
+ WASH, /* o: got eob, possibly still output waiting */
+ END, /* x: got eob and all data flushed */
+ BADCODE} /* x: got error */
+ mode; /* current inflate_codes mode */
+
+ /* mode dependent information */
+ uInt len;
+ union {
+ struct {
+ inflate_huft *tree; /* pointer into tree */
+ uInt need; /* bits needed */
+ } code; /* if LEN or DIST, where in tree */
+ uInt lit; /* if LIT, literal */
+ struct {
+ uInt get; /* bits to get for extra */
+ uInt dist; /* distance back to copy from */
+ } copy; /* if EXT or COPY, where and how much */
+ } sub; /* submode */
+
+ /* mode independent information */
+ Byte lbits; /* ltree bits decoded per branch */
+ Byte dbits; /* dtree bits decoder per branch */
+ inflate_huft *ltree; /* literal/length/eob tree */
+ inflate_huft *dtree; /* distance tree */
+
+};
+
+
+inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+z_streamp z;
+{
+ inflate_codes_statef *c;
+
+ if ((c = (inflate_codes_statef *)
+ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+ {
+ c->mode = START;
+ c->lbits = (Byte)bl;
+ c->dbits = (Byte)bd;
+ c->ltree = tl;
+ c->dtree = td;
+ Tracev((stderr, "inflate: codes new\n"));
+ }
+ return c;
+}
+
+
+int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt j; /* temporary storage */
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ Bytef *f; /* pointer to copy strings from */
+ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input and output based on current state */
+ while (1) switch (c->mode)
+ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ case START: /* x: set up for LEN */
+#ifndef SLOW
+ if (m >= 258 && n >= 10)
+ {
+ UPDATE
+ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+ LOAD
+ if (r != Z_OK)
+ {
+ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+ break;
+ }
+ }
+#endif /* !SLOW */
+ c->sub.code.need = c->lbits;
+ c->sub.code.tree = c->ltree;
+ c->mode = LEN;
+ case LEN: /* i: get length/literal/eob next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e == 0) /* literal */
+ {
+ c->sub.lit = t->base;
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", t->base));
+ c->mode = LIT;
+ break;
+ }
+ if (e & 16) /* length */
+ {
+ c->sub.copy.get = e & 15;
+ c->len = t->base;
+ c->mode = LENEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t->next;
+ break;
+ }
+ if (e & 32) /* end of block */
+ {
+ Tracevv((stderr, "inflate: end of block\n"));
+ c->mode = WASH;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid literal/length code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case LENEXT: /* i: getting length extra (have base) */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->len += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ c->sub.code.need = c->dbits;
+ c->sub.code.tree = c->dtree;
+ Tracevv((stderr, "inflate: length %u\n", c->len));
+ c->mode = DIST;
+ case DIST: /* i: get distance next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e & 16) /* distance */
+ {
+ c->sub.copy.get = e & 15;
+ c->sub.copy.dist = t->base;
+ c->mode = DISTEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t->next;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid distance code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case DISTEXT: /* i: getting distance extra */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->sub.copy.dist += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
+ c->mode = COPY;
+ case COPY: /* o: copying bytes in window, waiting for space */
+#ifndef __TURBOC__ /* Turbo C bug for following expression */
+ f = (uInt)(q - s->window) < c->sub.copy.dist ?
+ s->end - (c->sub.copy.dist - (q - s->window)) :
+ q - c->sub.copy.dist;
+#else
+ f = q - c->sub.copy.dist;
+ if ((uInt)(q - s->window) < c->sub.copy.dist)
+ f = s->end - (c->sub.copy.dist - (uInt)(q - s->window));
+#endif
+ while (c->len)
+ {
+ NEEDOUT
+ OUTBYTE(*f++)
+ if (f == s->end)
+ f = s->window;
+ c->len--;
+ }
+ c->mode = START;
+ break;
+ case LIT: /* o: got literal, waiting for output space */
+ NEEDOUT
+ OUTBYTE(c->sub.lit)
+ c->mode = START;
+ break;
+ case WASH: /* o: got eob, possibly more output */
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ c->mode = END;
+ case END:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADCODE: /* x: got error */
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_streamp z;
+{
+ ZFREE(z, c);
+ Tracev((stderr, "inflate: codes free\n"));
+}
+/* --- infcodes.c */
+
+/* +++ infutil.c */
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "infblock.h" */
+/* #include "inftrees.h" */
+/* #include "infcodes.h" */
+/* #include "infutil.h" */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* And'ing with mask[n] masks the lower n bits */
+uInt inflate_mask[17] = {
+ 0x0000,
+ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+
+/* copy as much as possible from the sliding window to the output area */
+int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt n;
+ Bytef *p;
+ Bytef *q;
+
+ /* local copies of source and destination pointers */
+ p = z->next_out;
+ q = s->read;
+
+ /* compute number of bytes to copy as far as end of window */
+ n = (uInt)((q <= s->write ? s->write : s->end) - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy as far as end of window */
+ if (p != Z_NULL) {
+ zmemcpy(p, q, n);
+ p += n;
+ }
+ q += n;
+
+ /* see if more to copy at beginning of window */
+ if (q == s->end)
+ {
+ /* wrap pointers */
+ q = s->window;
+ if (s->write == s->end)
+ s->write = s->window;
+
+ /* compute bytes to copy */
+ n = (uInt)(s->write - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy */
+ if (p != Z_NULL) {
+ zmemcpy(p, q, n);
+ p += n;
+ }
+ q += n;
+ }
+
+ /* update pointers */
+ z->next_out = p;
+ s->read = q;
+
+ /* done */
+ return r;
+}
+/* --- infutil.c */
+
+/* +++ inffast.c */
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "inftrees.h" */
+/* #include "infblock.h" */
+/* #include "infcodes.h" */
+/* #include "infutil.h" */
+/* #include "inffast.h" */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;}
+
+/* Called with number of bytes left to write in window at least 258
+ (the maximum string length) and number of input bytes available
+ at least ten. The ten bytes are six bytes for the longest length/
+ distance pair plus four bytes for overloading the bit buffer. */
+
+int inflate_fast(bl, bd, tl, td, s, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+inflate_blocks_statef *s;
+z_streamp z;
+{
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ uInt ml; /* mask for literal/length tree */
+ uInt md; /* mask for distance tree */
+ uInt c; /* bytes to copy */
+ uInt d; /* distance back to copy from */
+ Bytef *r; /* copy source pointer */
+
+ /* load input, output, bit values */
+ LOAD
+
+ /* initialize masks */
+ ml = inflate_mask[bl];
+ md = inflate_mask[bd];
+
+ /* do until not enough input or output space for fast loop */
+ do { /* assume called with m >= 258 && n >= 10 */
+ /* get literal/length code */
+ GRABBITS(20) /* max bits for literal/length code */
+ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ continue;
+ }
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits for length */
+ e &= 15;
+ c = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * length %u\n", c));
+
+ /* decode distance base of block to copy */
+ GRABBITS(15); /* max bits for distance code */
+ e = (t = td + ((uInt)b & md))->exop;
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits to add to distance base */
+ e &= 15;
+ GRABBITS(e) /* get extra bits (up to 13) */
+ d = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * distance %u\n", d));
+
+ /* do the copy */
+ m -= c;
+ if ((uInt)(q - s->window) >= d) /* offset before dest */
+ { /* just copy */
+ r = q - d;
+ *q++ = *r++; c--; /* minimum count is three, */
+ *q++ = *r++; c--; /* so unroll loop a little */
+ }
+ else /* else offset after destination */
+ {
+ e = d - (uInt)(q - s->window); /* bytes from offset to end */
+ r = s->end - e; /* pointer to offset */
+ if (c > e) /* if source crosses, */
+ {
+ c -= e; /* copy to end of window */
+ do {
+ *q++ = *r++;
+ } while (--e);
+ r = s->window; /* copy rest from start of window */
+ }
+ }
+ do { /* copy all or what's left */
+ *q++ = *r++;
+ } while (--c);
+ break;
+ }
+ else if ((e & 64) == 0)
+ e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop;
+ else
+ {
+ z->msg = (char*)"invalid distance code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ break;
+ }
+ if ((e & 64) == 0)
+ {
+ if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ break;
+ }
+ }
+ else if (e & 32)
+ {
+ Tracevv((stderr, "inflate: * end of block\n"));
+ UNGRAB
+ UPDATE
+ return Z_STREAM_END;
+ }
+ else
+ {
+ z->msg = (char*)"invalid literal/length code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ } while (m >= 258 && n >= 10);
+
+ /* not enough input or output--restore pointers and return */
+ UNGRAB
+ UPDATE
+ return Z_OK;
+}
+/* --- inffast.c */
+
+/* +++ zutil.c */
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: zutil.c,v 1.17 1996/07/24 13:41:12 me Exp $ */
+
+#ifdef DEBUG_ZLIB
+#include <stdio.h>
+#endif
+
+/* #include "zutil.h" */
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef STDC
+extern void exit OF((int));
+#endif
+
+const char *z_errmsg[10] = {
+"need dictionary", /* Z_NEED_DICT 2 */
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char *zlibVersion()
+{
+ return ZLIB_VERSION;
+}
+
+#ifdef DEBUG_ZLIB
+void z_error (m)
+ char *m;
+{
+ fprintf(stderr, "%s\n", m);
+ exit(1);
+}
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+ Bytef* dest;
+ Bytef* source;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = *source++; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+ Bytef* s1;
+ Bytef* s2;
+ uInt len;
+{
+ uInt j;
+
+ for (j = 0; j < len; j++) {
+ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+ }
+ return 0;
+}
+
+void zmemzero(dest, len)
+ Bytef* dest;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = 0; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+#endif
+
+#ifdef __TURBOC__
+#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__)
+/* Small and medium model in Turbo C are for now limited to near allocation
+ * with reduced MAX_WBITS and MAX_MEM_LEVEL
+ */
+# define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+ voidpf org_ptr;
+ voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ voidpf buf = opaque; /* just to make some compilers happy */
+ ulg bsize = (ulg)items*size;
+
+ /* If we allocate less than 65520 bytes, we assume that farmalloc
+ * will return a usable pointer which doesn't have to be normalized.
+ */
+ if (bsize < 65520L) {
+ buf = farmalloc(bsize);
+ if (*(ush*)&buf != 0) return buf;
+ } else {
+ buf = farmalloc(bsize + 16L);
+ }
+ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+ table[next_ptr].org_ptr = buf;
+
+ /* Normalize the pointer to seg:0 */
+ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+ *(ush*)&buf = 0;
+ table[next_ptr++].new_ptr = buf;
+ return buf;
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ int n;
+ if (*(ush*)&ptr != 0) { /* object < 64K */
+ farfree(ptr);
+ return;
+ }
+ /* Find the original pointer */
+ for (n = 0; n < next_ptr; n++) {
+ if (ptr != table[n].new_ptr) continue;
+
+ farfree(table[n].org_ptr);
+ while (++n < next_ptr) {
+ table[n-1] = table[n];
+ }
+ next_ptr--;
+ return;
+ }
+ ptr = opaque; /* just to make some compilers happy */
+ Assert(0, "zcfree: ptr not found");
+}
+#endif
+#endif /* __TURBOC__ */
+
+
+#if defined(M_I86) && !defined(__32BIT__)
+/* Microsoft C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER < 600))
+# define _halloc halloc
+# define _hfree hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ return _halloc((long)items, size);
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ _hfree(ptr);
+}
+
+#endif /* MSC */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp calloc OF((uInt items, uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+ voidpf opaque;
+ unsigned items;
+ unsigned size;
+{
+ if (opaque) items += size - size; /* make compiler happy */
+ return (voidpf)calloc(items, size);
+}
+
+void zcfree (opaque, ptr)
+ voidpf opaque;
+ voidpf ptr;
+{
+ free(ptr);
+ if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
+/* --- zutil.c */
+
+/* +++ adler32.c */
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: adler32.c,v 1.10 1996/05/22 11:52:18 me Exp $ */
+
+/* #include "zlib.h" */
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+/* ========================================================================= */
+uLong adler32(adler, buf, len)
+ uLong adler;
+ const Bytef *buf;
+ uInt len;
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == Z_NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ buf += 16;
+ k -= 16;
+ }
+ if (k != 0) do {
+ s1 += *buf++;
+ s2 += s1;
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ return (s2 << 16) | s1;
+}
+/* --- adler32.c */
diff --git a/mdk-stage1/ppp/common/zlib.h b/mdk-stage1/ppp/common/zlib.h
new file mode 100644
index 000000000..188ddaff9
--- /dev/null
+++ b/mdk-stage1/ppp/common/zlib.h
@@ -0,0 +1,1010 @@
+/* $Id$ */
+
+/*
+ * This file is derived from zlib.h and zconf.h from the zlib-1.0.4
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.
+ */
+
+/*
+ * ==FILEVERSION 971127==
+ *
+ * This marker is used by the Linux installation script to determine
+ * whether an up-to-date version of this file is already installed.
+ */
+
+
+/* +++ zlib.h */
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.0.4, Jul 24th, 1996.
+
+ Copyright (C) 1995-1996 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ gzip@prep.ai.mit.edu madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* +++ zconf.h */
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: zconf.h,v 1.20 1996/07/02 15:09:28 me Exp $ */
+
+#ifndef _ZCONF_H
+#define _ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateReset z_inflateReset
+# define compress z_compress
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
+# ifndef __32BIT__
+# define __32BIT__
+# endif
+#endif
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#if defined(MSDOS) && !defined(__32BIT__)
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC)
+# define STDC
+#endif
+#if (defined(__STDC__) || defined(__cplusplus)) && !defined(STDC)
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2 */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ 1 << (windowBits+2) + 1 << (memLevel+9)
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR __far
+# else
+# define FAR far
+# endif
+#endif
+#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
+# ifndef __32BIT__
+# define SMALL_MEDIUM
+# define FAR __far
+# endif
+#endif
+#ifndef FAR
+# define FAR
+#endif
+
+typedef unsigned char Byte; /* 8 bits */
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#if defined(__BORLANDC__) && defined(SMALL_MEDIUM)
+ /* Borland C/C++ ignores FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+
+/* Compile with -DZLIB_DLL for Windows DLL support */
+#if (defined(_WINDOWS) || defined(WINDOWS)) && defined(ZLIB_DLL)
+# include <windows.h>
+# define EXPORT WINAPI
+#else
+# define EXPORT
+#endif
+
+#endif /* _ZCONF_H */
+/* --- zconf.h */
+
+#define ZLIB_VERSION "1.0.4P"
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms may be added later and will have the same
+ stream interface.
+
+ For compression the application must provide the output buffer and
+ may optionally provide the input buffer for optimization. For decompression,
+ the application must provide the input buffer and may optionally provide
+ the output buffer for optimization.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The library does not install any signal handler. It is recommended to
+ add at least a handler for SIGSEGV when decompressing; the library checks
+ the consistency of the input data whenever possible but may go nuts
+ for some forms of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: ascii or binary */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1
+#define Z_PACKET_FLUSH 2
+#define Z_SYNC_FLUSH 3
+#define Z_FULL_FLUSH 4
+#define Z_FINISH 5
+/* Allowed flush values; see deflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_ASCII 1
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+ /* basic functions */
+
+extern const char * EXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+extern int EXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+extern int EXPORT deflate OF((z_streamp strm, int flush));
+/*
+ Performs one or both of the following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ If the parameter flush is set to Z_PARTIAL_FLUSH, the current compression
+ block is terminated and flushed to the output buffer so that the
+ decompressor can get all input data available so far. For method 9, a future
+ variant on method 8, the current block will be flushed but not terminated.
+ Z_SYNC_FLUSH has the same effect as partial flush except that the compressed
+ output is byte aligned (the compressor can clear its internal bit buffer)
+ and the current block is always terminated; this can be useful if the
+ compressor has to be restarted from scratch after an interruption (in which
+ case the internal state of the compressor may be lost).
+ If flush is set to Z_FULL_FLUSH, the compression block is terminated, a
+ special marker is output and the compression dictionary is discarded; this
+ is useful to allow the decompressor to synchronize if one compressed block
+ has been damaged (see inflateSync below). Flushing degrades compression and
+ so should be used only when necessary. Using Z_FULL_FLUSH too often can
+ seriously degrade the compression. If deflate returns with avail_out == 0,
+ this function must be called again with the same value of the flush
+ parameter and more output space (updated avail_out), until the flush is
+ complete (deflate returns with non-zero avail_out).
+
+ If the parameter flush is set to Z_PACKET_FLUSH, the compression
+ block is terminated, and a zero-length stored block is output,
+ omitting the length bytes (the effect of this is that the 3-bit type
+ code 000 for a stored block is output, and the output is then
+ byte-aligned). This is designed for use at the end of a PPP packet.
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ 0.1% larger than avail_in plus 12 bytes. If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() may update data_type if it can make a good guess about
+ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible.
+*/
+
+
+extern int EXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+extern int EXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller. If
+ zalloc and zfree are set to Z_NULL, inflateInit updates them to use default
+ allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_VERSION_ERROR if the zlib library version is incompatible
+ with the version assumed by the caller. msg is set to null if there is no
+ error message. inflateInit does not perform any decompression: this will be
+ done by inflate().
+*/
+
+
+extern int EXPORT inflate OF((z_streamp strm, int flush));
+/*
+ Performs one or both of the following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH,
+ inflate flushes as much output as possible to the output buffer. The
+ flushing behavior of inflate is not specified for values of the flush
+ parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the
+ current implementation actually flushes as much output as possible
+ anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data
+ has been consumed, it is expecting to see the length field of a stored
+ block; if not, it returns Z_DATA_ERROR.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster routine
+ may be used for the single inflate() call.
+
+ inflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if the end of the
+ compressed data has been reached and all uncompressed output has been
+ produced, Z_NEED_DICT if a preset dictionary is needed at this point (see
+ inflateSetDictionary below), Z_DATA_ERROR if the input data was corrupted,
+ Z_STREAM_ERROR if the stream structure was inconsistent (for example if
+ next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in
+ the output buffer when Z_FINISH is used. In the Z_DATA_ERROR case, the
+ application may then call inflateSync to look for a good compression block.
+ In the Z_NEED_DICT case, strm->adler is set to the Adler32 value of the
+ dictionary chosen by the compressor.
+*/
+
+
+extern int EXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+extern int EXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library. (Method 9 will allow a 64K history buffer and
+ partial block flushes.)
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library (the value 16 will be allowed for method 9). Larger
+ values of this parameter result in better compression at the expense of
+ memory usage. The default value is 15 if deflateInit is used instead.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match). Filtered data consists mostly of small values with a
+ somewhat random distribution. In this case, the compression algorithm is
+ tuned to compress them better. The effect of Z_FILTERED is to force more
+ Huffman coding and less string matching; it is somewhat intermediate
+ between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
+ the compression ratio but not the correctness of the compressed output even
+ if it is not set appropriately.
+
+ If next_in is not null, the library will use this buffer to hold also
+ some history information; the buffer must either hold the entire input
+ data, or have at least 1<<(windowBits+1) bytes and be writable. If next_in
+ is null, the library will allocate its own history buffer (and leave next_in
+ null). next_out need not be provided here but must be provided by the
+ application for the next call of deflate().
+
+ If the history buffer is provided by the application, next_in must
+ must never be changed by the application since the compressor maintains
+ information inside this buffer from call to call; the application
+ must provide more input only by increasing avail_in. next_in is always
+ reset by the library in this case.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+ an invalid method). msg is set to null if there is no error message.
+ deflateInit2 does not perform any compression: this will be done by
+ deflate().
+*/
+
+extern int EXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary (history buffer) from the given
+ byte sequence without producing any compressed output. This function must
+ be called immediately after deflateInit or deflateInit2, before any call
+ of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and
+ can be predicted with good accuracy; the data can then be compressed better
+ than with the default empty dictionary. In this version of the library,
+ only the last 32K bytes of the dictionary are used.
+ Upon return of this function, strm->adler is set to the Adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The Adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.)
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state
+ is inconsistent (for example if deflate has already been called for this
+ stream). deflateSetDictionary does not perform any compression: this will
+ be done by deflate().
+*/
+
+extern int EXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream. If
+ the source stream is using an application-supplied history buffer, a new
+ buffer is allocated for the destination stream. The compressed output
+ buffer is always application-supplied. It's the responsibility of the
+ application to provide the correct values of next_out and avail_out for the
+ next call of deflate.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+extern int EXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int EXPORT deflateParams OF((z_streamp strm, int level, int strategy));
+/*
+ Dynamically update the compression level and compression strategy.
+ This can be used to switch between compression and straight copy of
+ the input data, or to switch to a different kind of input data requiring
+ a different strategy. If the compression level is changed, the input
+ available so far is compressed with the old level (and may be flushed);
+ the new level will take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+extern int EXPORT deflateOutputPending OF((z_streamp strm));
+/*
+ Returns the number of bytes of output which are immediately
+ available from the compressor (i.e. without any further input
+ or flush).
+*/
+
+/*
+extern int EXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with more compression options. The
+ fields next_out, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library (the value 16 will be allowed soon). The
+ default value is 15 if inflateInit is used instead. If a compressed stream
+ with a larger window size is given as input, inflate() will return with
+ the error code Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ If next_out is not null, the library will use this buffer for the history
+ buffer; the buffer must either be large enough to hold the entire output
+ data, or have at least 1<<windowBits bytes. If next_out is null, the
+ library will allocate its own buffer (and leave next_out null). next_in
+ need not be provided here but must be provided by the application for the
+ next call of inflate().
+
+ If the history buffer is provided by the application, next_out must
+ never be changed by the application since the decompressor maintains
+ history information inside this buffer from call to call; the application
+ can only reset next_out to the beginning of the history buffer when
+ avail_out is zero and all output has been consumed.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+ windowBits < 8). msg is set to null if there is no error message.
+ inflateInit2 does not perform any decompression: this will be done by
+ inflate().
+*/
+
+extern int EXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary (history buffer) from the given
+ uncompressed byte sequence. This function must be called immediately after
+ a call of inflate if this call returned Z_NEED_DICT. The dictionary chosen
+ by the compressor can be determined from the Adler32 value returned by this
+ call of inflate. The compressor and decompressor must use exactly the same
+ dictionary (see deflateSetDictionary).
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect Adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+extern int EXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until the special marker (see deflate()
+ above) can be found, or until all available input is skipped. No output
+ is provided.
+
+ inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no marker has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+extern int EXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int inflateIncomp OF((z_stream *strm));
+/*
+ This function adds the data at next_in (avail_in bytes) to the output
+ history without performing any output. There must be no pending output,
+ and the decompressor must be expecting to see the start of a block.
+ Calling this function is equivalent to decompressing a stored block
+ containing the data at next_in (except that the data is not output).
+*/
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level, window size,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+extern int EXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least 0.1% larger than
+ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+extern int EXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+
+
+typedef voidp gzFile;
+
+extern gzFile EXPORT gzopen OF((const char *path, const char *mode));
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9"). gzopen can be used to read a file which is not in gzip format;
+ in this case gzread will directly read from the file without decompression.
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR).
+*/
+
+extern gzFile EXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+extern int EXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+extern int EXPORT gzwrite OF((gzFile file, const voidp buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+extern int EXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+extern int EXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+extern const char * EXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+extern uLong EXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+extern uLong EXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running crc with the bytes buf[0..len-1] and return the updated
+ crc. If buf is NULL, this function returns the required initial value
+ for the crc. Pre- and post-conditioning (one's complement) is performed
+ within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+extern int EXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+extern int EXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+extern int EXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel, int strategy,
+ const char *version, int stream_size));
+extern int EXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+
+#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+uLongf *get_crc_table OF((void)); /* can be used by asm versions of crc32() */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZLIB_H */
+/* --- zlib.h */
diff --git a/mdk-stage1/ppp/configure b/mdk-stage1/ppp/configure
new file mode 100755
index 000000000..544fb0d71
--- /dev/null
+++ b/mdk-stage1/ppp/configure
@@ -0,0 +1,141 @@
+#!/bin/sh
+# $Id$
+
+# if [ -d /NextApps ]; then
+# system="NeXTStep"
+# else
+ system=`uname -s`
+ release=`uname -r`
+ machine=`uname -p`
+ arch=`uname -m`
+# fi
+state="unknown"
+
+case $system in
+ Linux)
+ makext="linux";
+ ksrc="linux";
+ state="known";;
+ SunOS)
+ case $release in
+# [0-3]*) state="ancient";;
+# 4*) state="known"; ksrc="sunos4"; makext="sunos4";;
+ 5.[1-6]*) state="known"; ksrc="solaris"; makext="sol2";;
+ 5.[7-9]*) state="known"; ksrc="solaris"; makext="sol2";
+ case $arch in
+ sun4u) lp64='y';;
+ *) ;;
+ esac;;
+ esac;;
+ NetBSD|FreeBSD|ULTRIX|OSF1|NeXTStep|SINIX-?|UNIX_SV|UNIX_System_V)
+ state="notincluded";;
+# NetBSD)
+# makext="bsd";
+# case $release in
+# 0.*) state="ancient";;
+# 1.0*) state="ancient";;
+# 1.1*) state="known"; ksrc="netbsd-1.1";;
+# 1.2*) state="known"; ksrc="netbsd-1.2"; makext="netbsd-1.2";;
+# 1.[3-9]*|[2-9]*)
+# state="late"; ksrc="netbsd-1.2";;
+# esac;;
+# ULTRIX)
+# makext="ultrix";
+# case $release in
+# [0-3]*) state="ancient";;
+# 4.[01]*) state="early"; ksrc="ultrix";;
+# 4.[234]) state="known"; ksrc="ultrix";;
+# esac;;
+# OSF1)
+# makext="osf";
+# case $release in
+# V1.*) state="neolithic"; ksrc="osf1";;
+# V[23].*) state="neolithic"; ksrc="osf1";;
+# V4.*) state="known"; ksrc="osf1";;
+# V[5-9]*) state="late"; ksrc="osf1";;
+# esac;;
+# FreeBSD)
+# makext="bsd";
+# case $release in
+# 1.*) state="known"; ksrc="freebsd-old";;
+# 2.[01]*) state="known"; ksrc="freebsd-2.0";;
+# 2.2.[2-7]*) state="late"; ksrc="freebsd-2.0";;
+# 2.2.8*) state="known"; ksrc="freebsd-2.2.8";;
+# 3.[0-1]*) state="known"; ksrc="freebsd-3.0";;
+# esac;;
+# NeXTStep)
+# makext="NeXT";
+# ksrc="NeXT";
+# state="known";;
+# SINIX-?)
+# case $release in
+# 5.4[01]) state=known; ksrc=svr4; makext=svr4;;
+# 5.4[2-9]) state=late; ksrc=svr4; makext=svr4;;
+# esac;;
+# # Intel SVR4 systems come with a bug in the uname program. Unless
+# # your provider fixed the bug, or you get a fix for it, uname -S will
+# # overwrite the system name with the node name!
+# UNIX_SV|UNIX_System_V|`uname -n`)
+# case $release in
+# 4.0) state=known; ksrc=svr4; makext=svr4;;
+# 4.2) state=late; ksrc=svr4; makext=svr4;;
+# esac;;
+esac
+
+if [ -d "$ksrc" ]; then :; else
+ state="notincluded"
+ unset ksrc
+fi
+
+case $state in
+ neolithic)
+ echo "This is a newer release on an outdated OS ($system)."
+ echo " This software may or may not work on this OS."
+ echo " You may want to download an older version of PPP for this OS.";;
+ ancient)
+ echo "This is an old release of a supported OS ($system)."
+ echo "This software cannot be used as-is on this system,"
+ echo "but you may be able to port it. Good luck!"
+ exit;;
+ early)
+ echo "This is an old release of a supported OS ($system)."
+ echo "This software should install and run on this system,"
+ echo "but it hasn't been tested.";;
+ late)
+ echo "This is a newer release of $system than is supported by"
+ echo "this software. It may or may not work.";;
+ unknown)
+ echo "This software has not been ported to this system. Sorry.";;
+ notincluded)
+ echo "Support for this system has not been included"
+ echo "in this distribution. Sorry.";;
+esac
+
+orig_makext=$makext
+
+if [ -d "$ksrc" ]; then
+ echo "Creating links to Makefiles."
+ rm -f Makefile
+ ln -s $ksrc/Makefile.top Makefile
+ echo " Makefile -> $ksrc/Makefile.top"
+ if [ "$ksrc" = solaris ]; then
+ # Point to 64-bit Makefile extension
+ if [ "$lp64" = y ]; then
+ makext=$makext-64
+ fi
+ rm -f $ksrc/Makefile
+ ln -s Makefile.$makext $ksrc/Makefile
+ echo " $ksrc/Makefile -> Makefile.$makext"
+ # Restore extension
+ if [ "$lp64" = y ]; then
+ makext=$orig_makext
+ fi
+ fi
+ for dir in pppd pppstats chat pppdump; do
+ rm -f $dir/Makefile
+ if [ -f $dir/Makefile.$makext ]; then
+ ln -s Makefile.$makext $dir/Makefile
+ echo " $dir/Makefile -> Makefile.$makext"
+ fi
+ done
+fi
diff --git a/mdk-stage1/ppp/contrib/pppgetpass/Makefile.linux b/mdk-stage1/ppp/contrib/pppgetpass/Makefile.linux
new file mode 100644
index 000000000..7eb217dac
--- /dev/null
+++ b/mdk-stage1/ppp/contrib/pppgetpass/Makefile.linux
@@ -0,0 +1,16 @@
+all: pppgetpass.vt pppgetpass.gtk
+
+pppgetpass.vt: pppgetpass.vt.o
+
+pppgetpass.gtk: pppgetpass.gtk.o
+ $(CC) $(LDFLAGS) pppgetpass.gtk.o `gtk-config --libs` -o pppgetpass.gtk
+pppgetpass.gtk.o: pppgetpass.gtk.c
+ $(CC) $(CFLAGS) -c pppgetpass.gtk.c `gtk-config --cflags`
+
+install: all
+ install -m 755 pppgetpass.sh /usr/bin/pppgetpass
+ install -m 4755 -o root -g root pppgetpass.vt /usr/bin/
+ install -m 755 -o root -g root pppgetpass.gtk /usr/X11/bin/
+
+clean:
+ rm -f *.o pppgetpass.gtk pppgetpass.vt core
diff --git a/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.8 b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.8
new file mode 100644
index 000000000..ade576970
--- /dev/null
+++ b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.8
@@ -0,0 +1,18 @@
+.TH PPPGETPASS 8 "26 Sep 1999"
+.SH NAME
+pppgetpass \- prompt for PAP password
+.SH SYNOPSIS
+.B pppgetpass
+.I client server fd
+.SH DESCRIPTION
+.B pppgetpass
+the outer half of a plugin for PAP password prompting in pppd.
+If the peer requires PAP, and the
+.B passprompt.so
+plugin is loaded into pppd, it will run
+.B /usr/sbin/pppgetpass
+(or another program specified by the
+.B promptprog
+option) to prompt the user for the password.
+.SH SEE ALSO
+pppd(8)
diff --git a/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.gtk.c b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.gtk.c
new file mode 100644
index 000000000..48ca04202
--- /dev/null
+++ b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.gtk.c
@@ -0,0 +1,92 @@
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtksignal.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+
+int outfd;
+int err;
+
+static void okpressed(void *widget, void *clientdata)
+{
+ GtkWidget *answer=clientdata;
+ gchar *pass;
+ int passlen;
+ ssize_t wrote;
+ (void)widget;
+
+ pass=gtk_entry_get_text(GTK_ENTRY(answer));
+
+ passlen=strlen(pass);
+ if(!passlen)
+ return;
+
+ if((wrote=write(outfd, pass, passlen))!=passlen) {
+ if(wrote<0)
+ syslog(LOG_ERR, "write error on outpipe: %m");
+ else
+ syslog(LOG_ERR, "short write on outpipe");
+ err=1;
+ }
+ gtk_main_quit();
+}
+
+int main(int argc, char **argv)
+{
+ GtkWidget *mainwindow, *vbox, *question, *answer, *ok;
+ char buf[1024];
+ gtk_init(&argc, &argv);
+
+ openlog(argv[0], LOG_PID, LOG_DAEMON);
+ if(argc!=4) {
+ syslog(LOG_WARNING, "Usage error");
+ return 1;
+ }
+ outfd=atoi(argv[3]);
+ mainwindow=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(mainwindow), "pppgetpass");
+ gtk_signal_connect(GTK_OBJECT(mainwindow), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit), 0);
+
+ vbox=gtk_vbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(mainwindow), vbox);
+ gtk_widget_show(vbox);
+
+ if(argv[1][0] && argv[2][0])
+ snprintf(buf, sizeof buf, "Password for PPP client %s on server %s: ", argv[1], argv[2]);
+ else if(argv[1][0] && !argv[2][0])
+ snprintf(buf, sizeof buf, "Password for PPP client %s: ", argv[1]);
+ else if(!argv[1][0] && argv[2][0])
+ snprintf(buf, sizeof buf, "Password for PPP on server %s: ", argv[2]);
+ else
+ snprintf(buf, sizeof buf, "Enter PPP password: ");
+ question=gtk_label_new(buf);
+ gtk_box_pack_start(GTK_BOX(vbox), question, FALSE, TRUE, 0);
+ gtk_widget_show(question);
+
+ answer=gtk_entry_new();
+ gtk_entry_set_visibility(GTK_ENTRY(answer), 0);
+ gtk_box_pack_start(GTK_BOX(vbox), answer, FALSE, TRUE, 0);
+ gtk_widget_show(answer);
+
+ ok=gtk_button_new_with_label("OK");
+ gtk_box_pack_start(GTK_BOX(vbox), ok, FALSE, TRUE, 0);
+ gtk_signal_connect(GTK_OBJECT(ok), "clicked",
+ GTK_SIGNAL_FUNC(okpressed), answer);
+ gtk_widget_show(ok);
+
+ gtk_widget_show(mainwindow);
+ gtk_main();
+
+ return err;
+}
diff --git a/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.sh b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.sh
new file mode 100644
index 000000000..09c480519
--- /dev/null
+++ b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+if [ -z "$DISPLAY" ]; then
+ exec pppgetpass.vt "$@"
+else
+ exec pppgetpass.gtk "$@"
+fi
diff --git a/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.vt.c b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.vt.c
new file mode 100644
index 000000000..a1520883c
--- /dev/null
+++ b/mdk-stage1/ppp/contrib/pppgetpass/pppgetpass.vt.c
@@ -0,0 +1,218 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <termios.h>
+#include <sys/vt.h>
+
+static int console_owner(uid_t, int);
+
+int main(int argc, char **argv)
+{
+ int console;
+ uid_t uid;
+ struct vt_stat origstate;
+ int openvtnum;
+ char openvtname[256];
+ int openvt;
+ gid_t gid;
+ int chowned;
+ FILE *fp;
+ struct termios t;
+ char pass[256], *nl;
+ int outfd, passlen;
+ ssize_t wrote;
+ console=open("/dev/console", O_RDWR);
+
+ uid=getuid();
+ gid=getgid();
+ seteuid(uid);
+
+ openlog(argv[0], LOG_PID, LOG_DAEMON);
+
+ if(argc!=4) {
+ syslog(LOG_WARNING, "Usage error");
+ return 1;
+ }
+
+ if(console<0) {
+ syslog(LOG_ERR, "open(/dev/console): %m");
+ return 1;
+ }
+
+ if(ioctl(console, VT_GETSTATE, &origstate)<0) {
+ syslog(LOG_ERR, "VT_GETSTATE: %m");
+ return 1;
+ }
+
+ if(uid) {
+ if(!console_owner(uid, origstate.v_active)) {
+ int i;
+ for(i=0;i<64;++i) {
+ if(i!=origstate.v_active && console_owner(uid, i))
+ break;
+ }
+ if(i==64) {
+ syslog(LOG_WARNING, "run by uid %lu not at console", (unsigned long)uid);
+ return 1;
+ }
+ }
+ }
+
+ if(ioctl(console, VT_OPENQRY, &openvtnum)<0) {
+ syslog(LOG_ERR, "VT_OPENQRY: %m");
+ return 1;
+ }
+ if(openvtnum==-1) {
+ syslog(LOG_ERR, "No free VTs");
+ return 1;
+ }
+
+ snprintf(openvtname, sizeof openvtname, "/dev/tty%d", openvtnum);
+ seteuid(0);
+ openvt=open(openvtname, O_RDWR);
+ if(openvt<0) {
+ seteuid(uid);
+ syslog(LOG_ERR, "open(%s): %m", openvtname);
+ return 1;
+ }
+
+ chowned=fchown(openvt, uid, gid);
+ if(chowned<0) {
+ seteuid(uid);
+ syslog(LOG_ERR, "fchown(%s): %m", openvtname);
+ return 1;
+ }
+
+ close(console);
+
+ if(ioctl(openvt, VT_ACTIVATE, openvtnum)<0) {
+ seteuid(uid);
+ syslog(LOG_ERR, "VT_ACTIVATE(%d): %m", openvtnum);
+ return 1;
+ }
+
+ while(ioctl(openvt, VT_WAITACTIVE, openvtnum)<0) {
+ if(errno!=EINTR) {
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ syslog(LOG_ERR, "VT_WAITACTIVE(%d): %m", openvtnum);
+ return 1;
+ }
+ }
+
+ seteuid(uid);
+ fp=fdopen(openvt, "r+");
+ if(!fp) {
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ syslog(LOG_ERR, "fdopen(%s): %m", openvtname);
+ return 1;
+ }
+
+ if(tcgetattr(openvt, &t)<0) {
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ syslog(LOG_ERR, "tcgetattr(%s): %m", openvtname);
+ return 1;
+ }
+ t.c_lflag &= ~ECHO;
+ if(tcsetattr(openvt, TCSANOW, &t)<0) {
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ syslog(LOG_ERR, "tcsetattr(%s): %m", openvtname);
+ return 1;
+ }
+
+ if(fprintf(fp, "\033[2J\033[H")<0) {
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ syslog(LOG_ERR, "write error on %s: %m", openvtname);
+ return 1;
+ }
+ if(argv[1][0] && argv[2][0]) {
+ if(fprintf(fp, "Password for PPP client %s on server %s: ", argv[1], argv[2])<0) {
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ syslog(LOG_ERR, "write error on %s: %m", openvtname);
+ return 1;
+ }
+ } else if(argv[1][0] && !argv[2][0]) {
+ if(fprintf(fp, "Password for PPP client %s: ", argv[1])<0) {
+ syslog(LOG_ERR, "write error on %s: %m", openvtname);
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ return 1;
+ }
+ } else if(!argv[1][0] && argv[2][0]) {
+ if(fprintf(fp, "Password for PPP on server %s: ", argv[2])<0) {
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ syslog(LOG_ERR, "write error on %s: %m", openvtname);
+ return 1;
+ }
+ } else {
+ if(fprintf(fp, "Enter PPP password: ")<0) {
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ syslog(LOG_ERR, "write error on %s: %m", openvtname);
+ return 1;
+ }
+ }
+
+ if(!fgets(pass, sizeof pass, fp)) {
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ if(ferror(fp)) {
+ syslog(LOG_ERR, "read error on %s: %m", openvtname);
+ }
+ return 1;
+ }
+ if((nl=strchr(pass, '\n')))
+ *nl=0;
+ passlen=strlen(pass);
+
+ outfd=atoi(argv[3]);
+ if((wrote=write(outfd, pass, passlen))!=passlen) {
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ if(wrote<0)
+ syslog(LOG_ERR, "write error on outpipe: %m");
+ else
+ syslog(LOG_ERR, "short write on outpipe");
+ return 1;
+ }
+
+ seteuid(0);
+ ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+ seteuid(uid);
+ return 0;
+}
+
+static int console_owner(uid_t uid, int cons)
+{
+ char name[256];
+ struct stat st;
+ snprintf(name, sizeof name, "/dev/tty%d", cons);
+ if(stat(name, &st)<0) {
+ if(errno!=ENOENT)
+ syslog(LOG_ERR, "stat(%s): %m", name);
+ return 0;
+ }
+ return uid==st.st_uid;
+}
diff --git a/mdk-stage1/ppp/etc.ppp/chap-secrets b/mdk-stage1/ppp/etc.ppp/chap-secrets
new file mode 100644
index 000000000..7d1c3cd7c
--- /dev/null
+++ b/mdk-stage1/ppp/etc.ppp/chap-secrets
@@ -0,0 +1,2 @@
+# Secrets for authentication using CHAP
+# client server secret IP addresses
diff --git a/mdk-stage1/ppp/etc.ppp/options b/mdk-stage1/ppp/etc.ppp/options
new file mode 100644
index 000000000..0f77a9aa0
--- /dev/null
+++ b/mdk-stage1/ppp/etc.ppp/options
@@ -0,0 +1,5 @@
+lock
+noauth
+noipdefault
+usepeerdns
+
diff --git a/mdk-stage1/ppp/etc.ppp/options.options b/mdk-stage1/ppp/etc.ppp/options.options
new file mode 100644
index 000000000..4b67b6a0d
--- /dev/null
+++ b/mdk-stage1/ppp/etc.ppp/options.options
@@ -0,0 +1 @@
+lock
diff --git a/mdk-stage1/ppp/etc.ppp/pap-secrets b/mdk-stage1/ppp/etc.ppp/pap-secrets
new file mode 100644
index 000000000..f8b7dce3c
--- /dev/null
+++ b/mdk-stage1/ppp/etc.ppp/pap-secrets
@@ -0,0 +1,2 @@
+# Secrets for authentication using PAP
+# client server secret IP addresses
diff --git a/mdk-stage1/ppp/include/linux/if_ppp.h b/mdk-stage1/ppp/include/linux/if_ppp.h
new file mode 100644
index 000000000..1febf0931
--- /dev/null
+++ b/mdk-stage1/ppp/include/linux/if_ppp.h
@@ -0,0 +1,155 @@
+/* $Id$ */
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/*
+ * ==FILEVERSION 20000724==
+ *
+ * NOTE TO MAINTAINERS:
+ * If you modify this file at all, please set the above date.
+ * if_ppp.h is shipped with a PPP distribution as well as with the kernel;
+ * if everyone increases the FILEVERSION number above, then scripts
+ * can do the right thing when deciding whether to install a new if_ppp.h
+ * file. Don't change the format of that line otherwise, so the
+ * installation script can recognize it.
+ */
+
+#ifndef _IF_PPP_H_
+#define _IF_PPP_H_
+
+/*
+ * Packet sizes
+ */
+
+#define PPP_MTU 1500 /* Default MTU (size of Info field) */
+#define PPP_MAXMRU 65000 /* Largest MRU we allow */
+#define PROTO_IPX 0x002b /* protocol numbers */
+#define PROTO_DNA_RT 0x0027 /* DNA Routing */
+
+
+/*
+ * Bit definitions for flags.
+ */
+
+#define SC_COMP_PROT 0x00000001 /* protocol compression (output) */
+#define SC_COMP_AC 0x00000002 /* header compression (output) */
+#define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */
+#define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */
+#define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */
+#define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */
+#define SC_CCP_OPEN 0x00000040 /* Look at CCP packets */
+#define SC_CCP_UP 0x00000080 /* May send/recv compressed packets */
+#define SC_ENABLE_IP 0x00000100 /* IP packets may be exchanged */
+#define SC_LOOP_TRAFFIC 0x00000200 /* send traffic to pppd */
+#define SC_MULTILINK 0x00000400 /* do multilink encapsulation */
+#define SC_MP_SHORTSEQ 0x00000800 /* use short MP sequence numbers */
+#define SC_COMP_RUN 0x00001000 /* compressor has been inited */
+#define SC_DECOMP_RUN 0x00002000 /* decompressor has been inited */
+#define SC_MP_XSHORTSEQ 0x00004000 /* transmit short MP seq numbers */
+#define SC_DEBUG 0x00010000 /* enable debug messages */
+#define SC_LOG_INPKT 0x00020000 /* log contents of good pkts recvd */
+#define SC_LOG_OUTPKT 0x00040000 /* log contents of pkts sent */
+#define SC_LOG_RAWIN 0x00080000 /* log all chars received */
+#define SC_LOG_FLUSH 0x00100000 /* log all chars flushed */
+#define SC_SYNC 0x00200000 /* synchronous serial mode */
+#define SC_MASK 0x0f200fff /* bits that user can change */
+
+/* state bits */
+#define SC_XMIT_BUSY 0x10000000 /* (used by isdn_ppp?) */
+#define SC_RCV_ODDP 0x08000000 /* have rcvd char with odd parity */
+#define SC_RCV_EVNP 0x04000000 /* have rcvd char with even parity */
+#define SC_RCV_B7_1 0x02000000 /* have rcvd char with bit 7 = 1 */
+#define SC_RCV_B7_0 0x01000000 /* have rcvd char with bit 7 = 0 */
+#define SC_DC_FERROR 0x00800000 /* fatal decomp error detected */
+#define SC_DC_ERROR 0x00400000 /* non-fatal decomp error detected */
+
+/*
+ * Ioctl definitions.
+ */
+
+struct npioctl {
+ int protocol; /* PPP protocol, e.g. PPP_IP */
+ enum NPmode mode;
+};
+
+/* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */
+struct ppp_option_data {
+ __u8 *ptr;
+ __u32 length;
+ int transmit;
+};
+
+struct ifpppstatsreq {
+ struct ifreq b;
+ struct ppp_stats stats; /* statistic information */
+};
+
+struct ifpppcstatsreq {
+ struct ifreq b;
+ struct ppp_comp_stats stats;
+};
+
+#define ifr__name b.ifr_ifrn.ifrn_name
+#define stats_ptr b.ifr_ifru.ifru_data
+
+/*
+ * Ioctl definitions.
+ */
+
+#define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */
+#define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */
+#define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */
+#define PPPIOCSASYNCMAP _IOW('t', 87, int) /* set async map */
+#define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */
+#define PPPIOCGRASYNCMAP _IOR('t', 85, int) /* get receive async map */
+#define PPPIOCSRASYNCMAP _IOW('t', 84, int) /* set receive async map */
+#define PPPIOCGMRU _IOR('t', 83, int) /* get max receive unit */
+#define PPPIOCSMRU _IOW('t', 82, int) /* set max receive unit */
+#define PPPIOCSMAXCID _IOW('t', 81, int) /* set VJ max slot ID */
+#define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */
+#define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */
+#define PPPIOCXFERUNIT _IO('t', 78) /* transfer PPP unit */
+#define PPPIOCSCOMPRESS _IOW('t', 77, struct ppp_option_data)
+#define PPPIOCGNPMODE _IOWR('t', 76, struct npioctl) /* get NP mode */
+#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl) /* set NP mode */
+#define PPPIOCSPASS _IOW('t', 71, struct sock_fprog) /* set pass filter */
+#define PPPIOCSACTIVE _IOW('t', 70, struct sock_fprog) /* set active filt */
+#define PPPIOCGDEBUG _IOR('t', 65, int) /* Read debug level */
+#define PPPIOCSDEBUG _IOW('t', 64, int) /* Set debug level */
+#define PPPIOCGIDLE _IOR('t', 63, struct ppp_idle) /* get idle time */
+#define PPPIOCNEWUNIT _IOWR('t', 62, int) /* create new ppp unit */
+#define PPPIOCATTACH _IOW('t', 61, int) /* attach to ppp unit */
+#define PPPIOCDETACH _IOW('t', 60, int) /* detach from ppp unit/chan */
+#define PPPIOCSMRRU _IOW('t', 59, int) /* set multilink MRU */
+#define PPPIOCCONNECT _IOW('t', 58, int) /* connect channel to unit */
+#define PPPIOCDISCONN _IO('t', 57) /* disconnect channel */
+#define PPPIOCATTCHAN _IOW('t', 56, int) /* attach to ppp channel */
+#define PPPIOCGCHAN _IOR('t', 55, int) /* get ppp channel number */
+
+#define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0)
+#define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */
+#define SIOCGPPPCSTATS (SIOCDEVPRIVATE + 2)
+
+#if !defined(ifr_mtu)
+#define ifr_mtu ifr_ifru.ifru_metric
+#endif
+
+#endif /* _IF_PPP_H_ */
diff --git a/mdk-stage1/ppp/include/linux/if_pppvar.h b/mdk-stage1/ppp/include/linux/if_pppvar.h
new file mode 100644
index 000000000..b2485cd9d
--- /dev/null
+++ b/mdk-stage1/ppp/include/linux/if_pppvar.h
@@ -0,0 +1,138 @@
+/* From: if_pppvar.h,v 1.2 1995/06/12 11:36:51 paulus Exp */
+/*
+ * if_pppvar.h - private structures and declarations for PPP.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * ==FILEVERSION 990911==
+ *
+ * NOTE TO MAINTAINERS:
+ * If you modify this file at all, please set the above date.
+ * if_pppvar.h is shipped with a PPP distribution as well as with the kernel;
+ * if everyone increases the FILEVERSION number above, then scripts
+ * can do the right thing when deciding whether to install a new if_pppvar.h
+ * file. Don't change the format of that line otherwise, so the
+ * installation script can recognize it.
+ */
+
+/*
+ * Supported network protocols. These values are used for
+ * indexing sc_npmode.
+ */
+
+#define NP_IP 0 /* Internet Protocol */
+#define NP_IPX 1 /* IPX protocol */
+#define NP_AT 2 /* Appletalk protocol */
+#define NP_IPV6 3 /* Internet Protocol */
+#define NUM_NP 4 /* Number of NPs. */
+
+#define OBUFSIZE 256 /* # chars of output buffering */
+
+/*
+ * Structure describing each ppp unit.
+ */
+
+struct ppp {
+ int magic; /* magic value for structure */
+ struct ppp *next; /* unit with next index */
+ unsigned long inuse; /* are we allocated? */
+ int line; /* network interface unit # */
+ __u32 flags; /* miscellaneous control flags */
+ int mtu; /* maximum xmit frame size */
+ int mru; /* maximum receive frame size */
+ struct slcompress *slcomp; /* for TCP header compression */
+ struct sk_buff_head xmt_q; /* frames to send from pppd */
+ struct sk_buff_head rcv_q; /* frames for pppd to read */
+ unsigned long xmit_busy; /* bit 0 set when xmitter busy */
+
+ /* Information specific to using ppp on async serial lines. */
+ struct tty_struct *tty; /* ptr to TTY structure */
+ struct tty_struct *backup_tty; /* TTY to use if tty gets closed */
+ __u8 escape; /* 0x20 if prev char was PPP_ESC */
+ __u8 toss; /* toss this frame */
+ volatile __u8 tty_pushing; /* internal state flag */
+ volatile __u8 woke_up; /* internal state flag */
+ __u32 xmit_async_map[8]; /* 1 bit means that given control
+ character is quoted on output*/
+ __u32 recv_async_map; /* 1 bit means that given control
+ character is ignored on input*/
+ __u32 bytes_sent; /* Bytes sent on frame */
+ __u32 bytes_rcvd; /* Bytes recvd on frame */
+
+ /* Async transmission information */
+ struct sk_buff *tpkt; /* frame currently being sent */
+ int tpkt_pos; /* how much of it we've done */
+ __u16 tfcs; /* FCS so far for it */
+ unsigned char *optr; /* where we're up to in sending */
+ unsigned char *olim; /* points past last valid char */
+
+ /* Async reception information */
+ struct sk_buff *rpkt; /* frame currently being rcvd */
+ __u16 rfcs; /* FCS so far of rpkt */
+
+ /* Queues for select() functionality */
+ struct wait_queue *read_wait; /* queue for reading processes */
+
+ /* info for detecting idle channels */
+ unsigned long last_xmit; /* time of last transmission */
+ unsigned long last_recv; /* time last packet received */
+
+ /* Statistic information */
+ struct pppstat stats; /* statistic information */
+
+ /* PPP compression protocol information */
+ struct compressor *sc_xcomp; /* transmit compressor */
+ void *sc_xc_state; /* transmit compressor state */
+ struct compressor *sc_rcomp; /* receive decompressor */
+ void *sc_rc_state; /* receive decompressor state */
+
+ enum NPmode sc_npmode[NUM_NP]; /* what to do with each NP */
+ int sc_xfer; /* PID of reserved PPP table */
+ char name[8]; /* space for unit name */
+ struct device dev; /* net device structure */
+ struct enet_statistics estats; /* more detailed stats */
+
+ /* tty output buffer */
+ unsigned char obuf[OBUFSIZE]; /* buffer for characters to send */
+};
+
+#define PPP_MAGIC 0x5002
+#define PPP_VERSION "2.3.11"
diff --git a/mdk-stage1/ppp/include/linux/ppp-comp.h b/mdk-stage1/ppp/include/linux/ppp-comp.h
new file mode 100644
index 000000000..3184d0731
--- /dev/null
+++ b/mdk-stage1/ppp/include/linux/ppp-comp.h
@@ -0,0 +1,203 @@
+/*
+ * ppp-comp.h - Definitions for doing PPP packet compression.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * ==FILEVERSION 980319==
+ *
+ * NOTE TO MAINTAINERS:
+ * If you modify this file at all, please set the above date.
+ * ppp-comp.h is shipped with a PPP distribution as well as with the kernel;
+ * if everyone increases the FILEVERSION number above, then scripts
+ * can do the right thing when deciding whether to install a new ppp-comp.h
+ * file. Don't change the format of that line otherwise, so the
+ * installation script can recognize it.
+ */
+
+#ifndef _NET_PPP_COMP_H
+#define _NET_PPP_COMP_H
+
+/*
+ * The following symbols control whether we include code for
+ * various compression methods.
+ */
+
+#ifndef DO_BSD_COMPRESS
+#define DO_BSD_COMPRESS 1 /* by default, include BSD-Compress */
+#endif
+#ifndef DO_DEFLATE
+#define DO_DEFLATE 1 /* by default, include Deflate */
+#endif
+#define DO_PREDICTOR_1 0
+#define DO_PREDICTOR_2 0
+
+/*
+ * Structure giving methods for compression/decompression.
+ */
+
+struct compressor {
+ int compress_proto; /* CCP compression protocol number */
+
+ /* Allocate space for a compressor (transmit side) */
+ void *(*comp_alloc) (unsigned char *options, int opt_len);
+
+ /* Free space used by a compressor */
+ void (*comp_free) (void *state);
+
+ /* Initialize a compressor */
+ int (*comp_init) (void *state, unsigned char *options,
+ int opt_len, int unit, int opthdr, int debug);
+
+ /* Reset a compressor */
+ void (*comp_reset) (void *state);
+
+ /* Compress a packet */
+ int (*compress) (void *state, unsigned char *rptr,
+ unsigned char *obuf, int isize, int osize);
+
+ /* Return compression statistics */
+ void (*comp_stat) (void *state, struct compstat *stats);
+
+ /* Allocate space for a decompressor (receive side) */
+ void *(*decomp_alloc) (unsigned char *options, int opt_len);
+
+ /* Free space used by a decompressor */
+ void (*decomp_free) (void *state);
+
+ /* Initialize a decompressor */
+ int (*decomp_init) (void *state, unsigned char *options,
+ int opt_len, int unit, int opthdr, int mru,
+ int debug);
+
+ /* Reset a decompressor */
+ void (*decomp_reset) (void *state);
+
+ /* Decompress a packet. */
+ int (*decompress) (void *state, unsigned char *ibuf, int isize,
+ unsigned char *obuf, int osize);
+
+ /* Update state for an incompressible packet received */
+ void (*incomp) (void *state, unsigned char *ibuf, int icnt);
+
+ /* Return decompression statistics */
+ void (*decomp_stat) (void *state, struct compstat *stats);
+};
+
+/*
+ * The return value from decompress routine is the length of the
+ * decompressed packet if successful, otherwise DECOMP_ERROR
+ * or DECOMP_FATALERROR if an error occurred.
+ *
+ * We need to make this distinction so that we can disable certain
+ * useful functionality, namely sending a CCP reset-request as a result
+ * of an error detected after decompression. This is to avoid infringing
+ * a patent held by Motorola.
+ * Don't you just lurve software patents.
+ */
+
+#define DECOMP_ERROR -1 /* error detected before decomp. */
+#define DECOMP_FATALERROR -2 /* error detected after decomp. */
+
+/*
+ * CCP codes.
+ */
+
+#define CCP_CONFREQ 1
+#define CCP_CONFACK 2
+#define CCP_TERMREQ 5
+#define CCP_TERMACK 6
+#define CCP_RESETREQ 14
+#define CCP_RESETACK 15
+
+/*
+ * Max # bytes for a CCP option
+ */
+
+#define CCP_MAX_OPTION_LENGTH 32
+
+/*
+ * Parts of a CCP packet.
+ */
+
+#define CCP_CODE(dp) ((dp)[0])
+#define CCP_ID(dp) ((dp)[1])
+#define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN 4
+
+#define CCP_OPT_CODE(dp) ((dp)[0])
+#define CCP_OPT_LENGTH(dp) ((dp)[1])
+#define CCP_OPT_MINLEN 2
+
+/*
+ * Definitions for BSD-Compress.
+ */
+
+#define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS 3 /* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */
+#define BSD_VERSION(x) ((x) >> 5) /* version of option format */
+#define BSD_CURRENT_VERSION 1 /* current version number */
+#define BSD_MAKE_OPT(v, n) (((v) << 5) | (n))
+
+#define BSD_MIN_BITS 9 /* smallest code size supported */
+#define BSD_MAX_BITS 15 /* largest code size supported */
+
+/*
+ * Definitions for Deflate.
+ */
+
+#define CI_DEFLATE 26 /* config option for Deflate */
+#define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */
+#define CILEN_DEFLATE 4 /* length of its config option */
+
+#define DEFLATE_MIN_SIZE 8
+#define DEFLATE_MAX_SIZE 15
+#define DEFLATE_METHOD_VAL 8
+#define DEFLATE_SIZE(x) (((x) >> 4) + DEFLATE_MIN_SIZE)
+#define DEFLATE_METHOD(x) ((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w) ((((w) - DEFLATE_MIN_SIZE) << 4) \
+ + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE 0
+
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+
+#define CI_PREDICTOR_1 1 /* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1 2 /* length of its config option */
+#define CI_PREDICTOR_2 2 /* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2 2 /* length of its config option */
+
+#ifdef __KERNEL__
+extern int ppp_register_compressor(struct compressor *);
+extern void ppp_unregister_compressor(struct compressor *);
+#endif /* __KERNEL__ */
+
+#endif /* _NET_PPP_COMP_H */
diff --git a/mdk-stage1/ppp/include/linux/ppp_defs.h b/mdk-stage1/ppp/include/linux/ppp_defs.h
new file mode 100644
index 000000000..46946fbdc
--- /dev/null
+++ b/mdk-stage1/ppp/include/linux/ppp_defs.h
@@ -0,0 +1,185 @@
+/* $Id$ */
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+/*
+ * ==FILEVERSION 20000114==
+ *
+ * NOTE TO MAINTAINERS:
+ * If you modify this file at all, please set the above date.
+ * ppp_defs.h is shipped with a PPP distribution as well as with the kernel;
+ * if everyone increases the FILEVERSION number above, then scripts
+ * can do the right thing when deciding whether to install a new ppp_defs.h
+ * file. Don't change the format of that line otherwise, so the
+ * installation script can recognize it.
+ */
+
+#ifndef _PPP_DEFS_H_
+#define _PPP_DEFS_H_
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN 4 /* octets for standard ppp header */
+#define PPP_FCSLEN 2 /* octets for FCS */
+#define PPP_MRU 1500 /* default MRU = max length of info field */
+
+#define PPP_ADDRESS(p) (((__u8 *)(p))[0])
+#define PPP_CONTROL(p) (((__u8 *)(p))[1])
+#define PPP_PROTOCOL(p) ((((__u8 *)(p))[2] << 8) + ((__u8 *)(p))[3])
+
+/*
+ * Significant octet values.
+ */
+#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */
+#define PPP_UI 0x03 /* Unnumbered Information */
+#define PPP_FLAG 0x7e /* Flag Sequence */
+#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */
+#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */
+
+/*
+ * Protocol field values.
+ */
+#define PPP_IP 0x21 /* Internet Protocol */
+#define PPP_AT 0x29 /* AppleTalk Protocol */
+#define PPP_IPX 0x2b /* IPX protocol */
+#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */
+#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */
+#define PPP_MP 0x3d /* Multilink protocol */
+#define PPP_IPV6 0x57 /* Internet Protocol Version 6 */
+#define PPP_COMPFRAG 0xfb /* fragment compressed below bundle */
+#define PPP_COMP 0xfd /* compressed packet */
+#define PPP_IPCP 0x8021 /* IP Control Protocol */
+#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */
+#define PPP_IPXCP 0x802b /* IPX Control Protocol */
+#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */
+#define PPP_CCPFRAG 0x80fb /* CCP at link level (below MP bundle) */
+#define PPP_CCP 0x80fd /* Compression Control Protocol */
+#define PPP_LCP 0xc021 /* Link Control Protocol */
+#define PPP_PAP 0xc023 /* Password Authentication Protocol */
+#define PPP_LQR 0xc025 /* Link Quality Report protocol */
+#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */
+#define PPP_CBCP 0xc029 /* Callback Control Protocol */
+
+/*
+ * Values for FCS calculations.
+ */
+
+#define PPP_INITFCS 0xffff /* Initial FCS value */
+#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */
+#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+
+typedef __u32 ext_accm[8];
+
+/*
+ * What to do with network protocol (NP) packets.
+ */
+enum NPmode {
+ NPMODE_PASS, /* pass the packet through */
+ NPMODE_DROP, /* silently drop the packet */
+ NPMODE_ERROR, /* return an error */
+ NPMODE_QUEUE /* save it up for later. */
+};
+
+/*
+ * Statistics for LQRP and pppstats
+ */
+struct pppstat {
+ __u32 ppp_discards; /* # frames discarded */
+
+ __u32 ppp_ibytes; /* bytes received */
+ __u32 ppp_ioctects; /* bytes received not in error */
+ __u32 ppp_ipackets; /* packets received */
+ __u32 ppp_ierrors; /* receive errors */
+ __u32 ppp_ilqrs; /* # LQR frames received */
+
+ __u32 ppp_obytes; /* raw bytes sent */
+ __u32 ppp_ooctects; /* frame bytes sent */
+ __u32 ppp_opackets; /* packets sent */
+ __u32 ppp_oerrors; /* transmit errors */
+ __u32 ppp_olqrs; /* # LQR frames sent */
+};
+
+struct vjstat {
+ __u32 vjs_packets; /* outbound packets */
+ __u32 vjs_compressed; /* outbound compressed packets */
+ __u32 vjs_searches; /* searches for connection state */
+ __u32 vjs_misses; /* times couldn't find conn. state */
+ __u32 vjs_uncompressedin; /* inbound uncompressed packets */
+ __u32 vjs_compressedin; /* inbound compressed packets */
+ __u32 vjs_errorin; /* inbound unknown type packets */
+ __u32 vjs_tossed; /* inbound packets tossed because of error */
+};
+
+struct compstat {
+ __u32 unc_bytes; /* total uncompressed bytes */
+ __u32 unc_packets; /* total uncompressed packets */
+ __u32 comp_bytes; /* compressed bytes */
+ __u32 comp_packets; /* compressed packets */
+ __u32 inc_bytes; /* incompressible bytes */
+ __u32 inc_packets; /* incompressible packets */
+
+ /* the compression ratio is defined as in_count / bytes_out */
+ __u32 in_count; /* Bytes received */
+ __u32 bytes_out; /* Bytes transmitted */
+
+ double ratio; /* not computed in kernel. */
+};
+
+struct ppp_stats {
+ struct pppstat p; /* basic PPP statistics */
+ struct vjstat vj; /* VJ header compression statistics */
+};
+
+struct ppp_comp_stats {
+ struct compstat c; /* packet compression statistics */
+ struct compstat d; /* packet decompression statistics */
+};
+
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+ time_t xmit_idle; /* time since last NP packet sent */
+ time_t recv_idle; /* time since last NP packet received */
+};
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+#endif /* _PPP_DEFS_H_ */
diff --git a/mdk-stage1/ppp/include/net/if_ppp.h b/mdk-stage1/ppp/include/net/if_ppp.h
new file mode 100644
index 000000000..1527ecf3f
--- /dev/null
+++ b/mdk-stage1/ppp/include/net/if_ppp.h
@@ -0,0 +1,133 @@
+/* $Id$ */
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _IF_PPP_H_
+#define _IF_PPP_H_
+
+/*
+ * Bit definitions for flags.
+ */
+#define SC_COMP_PROT 0x00000001 /* protocol compression (output) */
+#define SC_COMP_AC 0x00000002 /* header compression (output) */
+#define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */
+#define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */
+#define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */
+#define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */
+#define SC_CCP_OPEN 0x00000040 /* Look at CCP packets */
+#define SC_CCP_UP 0x00000080 /* May send/recv compressed packets */
+#define SC_DEBUG 0x00010000 /* enable debug messages */
+#define SC_LOG_INPKT 0x00020000 /* log contents of good pkts recvd */
+#define SC_LOG_OUTPKT 0x00040000 /* log contents of pkts sent */
+#define SC_LOG_RAWIN 0x00080000 /* log all chars received */
+#define SC_LOG_FLUSH 0x00100000 /* log all chars flushed */
+#define SC_RCV_B7_0 0x01000000 /* have rcvd char with bit 7 = 0 */
+#define SC_RCV_B7_1 0x02000000 /* have rcvd char with bit 7 = 1 */
+#define SC_RCV_EVNP 0x04000000 /* have rcvd char with even parity */
+#define SC_RCV_ODDP 0x08000000 /* have rcvd char with odd parity */
+#define SC_SYNC 0x00200000 /* use synchronous HDLC framing */
+#define SC_MASK 0x0fff00ff /* bits that user can change */
+
+/*
+ * State bits in sc_flags, not changeable by user.
+ */
+#define SC_TIMEOUT 0x00000400 /* timeout is currently pending */
+#define SC_VJ_RESET 0x00000800 /* need to reset VJ decomp */
+#define SC_COMP_RUN 0x00001000 /* compressor has been inited */
+#define SC_DECOMP_RUN 0x00002000 /* decompressor has been inited */
+#define SC_DC_ERROR 0x00004000 /* non-fatal decomp error detected */
+#define SC_DC_FERROR 0x00008000 /* fatal decomp error detected */
+#define SC_TBUSY 0x10000000 /* xmitter doesn't need a packet yet */
+#define SC_PKTLOST 0x20000000 /* have lost or dropped a packet */
+#define SC_FLUSH 0x40000000 /* flush input until next PPP_FLAG */
+#define SC_ESCAPED 0x80000000 /* saw a PPP_ESCAPE */
+
+/*
+ * Ioctl definitions.
+ */
+
+struct npioctl {
+ int protocol; /* PPP procotol, e.g. PPP_IP */
+ enum NPmode mode;
+};
+
+/* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */
+struct ppp_option_data {
+ u_char *ptr;
+ u_int length;
+ int transmit;
+};
+
+struct ifpppstatsreq {
+ char ifr_name[IFNAMSIZ];
+ struct ppp_stats stats;
+};
+
+struct ifpppcstatsreq {
+ char ifr_name[IFNAMSIZ];
+ struct ppp_comp_stats stats;
+};
+
+/*
+ * Ioctl definitions.
+ */
+
+#define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */
+#define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */
+#define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */
+#define PPPIOCSASYNCMAP _IOW('t', 87, int) /* set async map */
+#define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */
+#define PPPIOCGRASYNCMAP _IOR('t', 85, int) /* get receive async map */
+#define PPPIOCSRASYNCMAP _IOW('t', 84, int) /* set receive async map */
+#define PPPIOCGMRU _IOR('t', 83, int) /* get max receive unit */
+#define PPPIOCSMRU _IOW('t', 82, int) /* set max receive unit */
+#define PPPIOCSMAXCID _IOW('t', 81, int) /* set VJ max slot ID */
+#define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */
+#define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */
+#define PPPIOCXFERUNIT _IO('t', 78) /* transfer PPP unit */
+#define PPPIOCSCOMPRESS _IOW('t', 77, struct ppp_option_data)
+#define PPPIOCGNPMODE _IOWR('t', 76, struct npioctl) /* get NP mode */
+#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl) /* set NP mode */
+#define PPPIOCGIDLE _IOR('t', 74, struct ppp_idle) /* get idle time */
+#ifdef PPP_FILTER
+#define PPPIOCSPASS _IOW('t', 71, struct bpf_program) /* set pass filter */
+#define PPPIOCSACTIVE _IOW('t', 70, struct bpf_program) /* set active filt */
+#endif /* PPP_FILTER */
+
+/* PPPIOC[GS]MTU are alternatives to SIOC[GS]IFMTU, used under Ultrix */
+#define PPPIOCGMTU _IOR('t', 73, int) /* get interface MTU */
+#define PPPIOCSMTU _IOW('t', 72, int) /* set interface MTU */
+
+/*
+ * These two are interface ioctls so that pppstats can do them on
+ * a socket without having to open the serial device.
+ */
+#define SIOCGPPPSTATS _IOWR('i', 123, struct ifpppstatsreq)
+#define SIOCGPPPCSTATS _IOWR('i', 122, struct ifpppcstatsreq)
+
+#if !defined(ifr_mtu)
+#define ifr_mtu ifr_ifru.ifru_metric
+#endif
+
+#if (defined(_KERNEL) || defined(KERNEL)) && !defined(NeXT)
+void pppattach __P((void));
+void pppintr __P((void));
+#endif
+#endif /* _IF_PPP_H_ */
diff --git a/mdk-stage1/ppp/include/net/ppp-comp.h b/mdk-stage1/ppp/include/net/ppp-comp.h
new file mode 100644
index 000000000..0e6a9c672
--- /dev/null
+++ b/mdk-stage1/ppp/include/net/ppp-comp.h
@@ -0,0 +1,165 @@
+/*
+ * ppp-comp.h - Definitions for doing PPP packet compression.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+#ifndef _NET_PPP_COMP_H
+#define _NET_PPP_COMP_H
+
+/*
+ * The following symbols control whether we include code for
+ * various compression methods.
+ */
+#ifndef DO_BSD_COMPRESS
+#define DO_BSD_COMPRESS 1 /* by default, include BSD-Compress */
+#endif
+#ifndef DO_DEFLATE
+#define DO_DEFLATE 1 /* by default, include Deflate */
+#endif
+#define DO_PREDICTOR_1 0
+#define DO_PREDICTOR_2 0
+
+/*
+ * Structure giving methods for compression/decompression.
+ */
+#ifdef PACKETPTR
+struct compressor {
+ int compress_proto; /* CCP compression protocol number */
+
+ /* Allocate space for a compressor (transmit side) */
+ void *(*comp_alloc) __P((u_char *options, int opt_len));
+ /* Free space used by a compressor */
+ void (*comp_free) __P((void *state));
+ /* Initialize a compressor */
+ int (*comp_init) __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int debug));
+ /* Reset a compressor */
+ void (*comp_reset) __P((void *state));
+ /* Compress a packet */
+ int (*compress) __P((void *state, PACKETPTR *mret,
+ PACKETPTR mp, int orig_len, int max_len));
+ /* Return compression statistics */
+ void (*comp_stat) __P((void *state, struct compstat *stats));
+
+ /* Allocate space for a decompressor (receive side) */
+ void *(*decomp_alloc) __P((u_char *options, int opt_len));
+ /* Free space used by a decompressor */
+ void (*decomp_free) __P((void *state));
+ /* Initialize a decompressor */
+ int (*decomp_init) __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+ /* Reset a decompressor */
+ void (*decomp_reset) __P((void *state));
+ /* Decompress a packet. */
+ int (*decompress) __P((void *state, PACKETPTR mp,
+ PACKETPTR *dmpp));
+ /* Update state for an incompressible packet received */
+ void (*incomp) __P((void *state, PACKETPTR mp));
+ /* Return decompression statistics */
+ void (*decomp_stat) __P((void *state, struct compstat *stats));
+};
+#endif /* PACKETPTR */
+
+/*
+ * Return values for decompress routine.
+ * We need to make these distinctions so that we can disable certain
+ * useful functionality, namely sending a CCP reset-request as a result
+ * of an error detected after decompression. This is to avoid infringing
+ * a patent held by Motorola.
+ * Don't you just lurve software patents.
+ */
+#define DECOMP_OK 0 /* everything went OK */
+#define DECOMP_ERROR 1 /* error detected before decomp. */
+#define DECOMP_FATALERROR 2 /* error detected after decomp. */
+
+/*
+ * CCP codes.
+ */
+#define CCP_CONFREQ 1
+#define CCP_CONFACK 2
+#define CCP_TERMREQ 5
+#define CCP_TERMACK 6
+#define CCP_RESETREQ 14
+#define CCP_RESETACK 15
+
+/*
+ * Max # bytes for a CCP option
+ */
+#define CCP_MAX_OPTION_LENGTH 32
+
+/*
+ * Parts of a CCP packet.
+ */
+#define CCP_CODE(dp) ((dp)[0])
+#define CCP_ID(dp) ((dp)[1])
+#define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN 4
+
+#define CCP_OPT_CODE(dp) ((dp)[0])
+#define CCP_OPT_LENGTH(dp) ((dp)[1])
+#define CCP_OPT_MINLEN 2
+
+/*
+ * Definitions for BSD-Compress.
+ */
+#define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS 3 /* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */
+#define BSD_VERSION(x) ((x) >> 5) /* version of option format */
+#define BSD_CURRENT_VERSION 1 /* current version number */
+#define BSD_MAKE_OPT(v, n) (((v) << 5) | (n))
+
+#define BSD_MIN_BITS 9 /* smallest code size supported */
+#define BSD_MAX_BITS 15 /* largest code size supported */
+
+/*
+ * Definitions for Deflate.
+ */
+#define CI_DEFLATE 26 /* config option for Deflate */
+#define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */
+#define CILEN_DEFLATE 4 /* length of its config option */
+
+#define DEFLATE_MIN_SIZE 8
+#define DEFLATE_MAX_SIZE 15
+#define DEFLATE_METHOD_VAL 8
+#define DEFLATE_SIZE(x) (((x) >> 4) + DEFLATE_MIN_SIZE)
+#define DEFLATE_METHOD(x) ((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w) ((((w) - DEFLATE_MIN_SIZE) << 4) \
+ + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE 0
+
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+#define CI_PREDICTOR_1 1 /* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1 2 /* length of its config option */
+#define CI_PREDICTOR_2 2 /* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2 2 /* length of its config option */
+
+#endif /* _NET_PPP_COMP_H */
diff --git a/mdk-stage1/ppp/include/net/ppp_defs.h b/mdk-stage1/ppp/include/net/ppp_defs.h
new file mode 100644
index 000000000..c35020eab
--- /dev/null
+++ b/mdk-stage1/ppp/include/net/ppp_defs.h
@@ -0,0 +1,184 @@
+/* $Id$ */
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#ifndef _PPP_DEFS_H_
+#define _PPP_DEFS_H_
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN 4 /* octets for standard ppp header */
+#define PPP_FCSLEN 2 /* octets for FCS */
+
+/*
+ * Packet sizes
+ *
+ * Note - lcp shouldn't be allowed to negotiate stuff outside these
+ * limits. See lcp.h in the pppd directory.
+ * (XXX - these constants should simply be shared by lcp.c instead
+ * of living in lcp.h)
+ */
+#define PPP_MTU 1500 /* Default MTU (size of Info field) */
+#define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN)
+#define PPP_MINMTU 64
+#define PPP_MRU 1500 /* default MRU = max length of info field */
+#define PPP_MAXMRU 65000 /* Largest MRU we allow */
+#define PPP_MINMRU 128
+
+#define PPP_ADDRESS(p) (((u_char *)(p))[0])
+#define PPP_CONTROL(p) (((u_char *)(p))[1])
+#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3])
+
+/*
+ * Significant octet values.
+ */
+#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */
+#define PPP_UI 0x03 /* Unnumbered Information */
+#define PPP_FLAG 0x7e /* Flag Sequence */
+#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */
+#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */
+
+/*
+ * Protocol field values.
+ */
+#define PPP_IP 0x21 /* Internet Protocol */
+#define PPP_AT 0x29 /* AppleTalk Protocol */
+#define PPP_IPX 0x2b /* IPX protocol */
+#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */
+#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */
+#define PPP_IPV6 0x57 /* Internet Protocol Version 6 */
+#define PPP_COMP 0xfd /* compressed packet */
+#define PPP_IPCP 0x8021 /* IP Control Protocol */
+#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */
+#define PPP_IPXCP 0x802b /* IPX Control Protocol */
+#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */
+#define PPP_CCP 0x80fd /* Compression Control Protocol */
+#define PPP_LCP 0xc021 /* Link Control Protocol */
+#define PPP_PAP 0xc023 /* Password Authentication Protocol */
+#define PPP_LQR 0xc025 /* Link Quality Report protocol */
+#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */
+#define PPP_CBCP 0xc029 /* Callback Control Protocol */
+
+/*
+ * Values for FCS calculations.
+ */
+#define PPP_INITFCS 0xffff /* Initial FCS value */
+#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */
+#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+
+/*
+ * A 32-bit unsigned integral type.
+ */
+
+#if !defined(__BIT_TYPES_DEFINED__) && !defined(_BITYPES) \
+ && !defined(__FreeBSD__) && (NS_TARGET < 40)
+#ifdef UINT32_T
+typedef UINT32_T u_int32_t;
+#else
+typedef unsigned int u_int32_t;
+typedef unsigned short u_int16_t;
+#endif
+#endif
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+typedef u_int32_t ext_accm[8];
+
+/*
+ * What to do with network protocol (NP) packets.
+ */
+enum NPmode {
+ NPMODE_PASS, /* pass the packet through */
+ NPMODE_DROP, /* silently drop the packet */
+ NPMODE_ERROR, /* return an error */
+ NPMODE_QUEUE /* save it up for later. */
+};
+
+/*
+ * Statistics.
+ */
+struct pppstat {
+ unsigned int ppp_ibytes; /* bytes received */
+ unsigned int ppp_ipackets; /* packets received */
+ unsigned int ppp_ierrors; /* receive errors */
+ unsigned int ppp_obytes; /* bytes sent */
+ unsigned int ppp_opackets; /* packets sent */
+ unsigned int ppp_oerrors; /* transmit errors */
+};
+
+struct vjstat {
+ unsigned int vjs_packets; /* outbound packets */
+ unsigned int vjs_compressed; /* outbound compressed packets */
+ unsigned int vjs_searches; /* searches for connection state */
+ unsigned int vjs_misses; /* times couldn't find conn. state */
+ unsigned int vjs_uncompressedin; /* inbound uncompressed packets */
+ unsigned int vjs_compressedin; /* inbound compressed packets */
+ unsigned int vjs_errorin; /* inbound unknown type packets */
+ unsigned int vjs_tossed; /* inbound packets tossed because of error */
+};
+
+struct ppp_stats {
+ struct pppstat p; /* basic PPP statistics */
+ struct vjstat vj; /* VJ header compression statistics */
+};
+
+struct compstat {
+ unsigned int unc_bytes; /* total uncompressed bytes */
+ unsigned int unc_packets; /* total uncompressed packets */
+ unsigned int comp_bytes; /* compressed bytes */
+ unsigned int comp_packets; /* compressed packets */
+ unsigned int inc_bytes; /* incompressible bytes */
+ unsigned int inc_packets; /* incompressible packets */
+ unsigned int ratio; /* recent compression ratio << 8 */
+};
+
+struct ppp_comp_stats {
+ struct compstat c; /* packet compression statistics */
+ struct compstat d; /* packet decompression statistics */
+};
+
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+ time_t xmit_idle; /* time since last NP packet sent */
+ time_t recv_idle; /* time since last NP packet received */
+};
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+#endif /* _PPP_DEFS_H_ */
diff --git a/mdk-stage1/ppp/include/net/pppio.h b/mdk-stage1/ppp/include/net/pppio.h
new file mode 100644
index 000000000..9db1ca9ab
--- /dev/null
+++ b/mdk-stage1/ppp/include/net/pppio.h
@@ -0,0 +1,99 @@
+/*
+ * pppio.h - ioctl and other misc. definitions for STREAMS modules.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+#define _PPPIO(n) (('p' << 8) + (n))
+
+#define PPPIO_NEWPPA _PPPIO(130) /* allocate a new PPP unit */
+#define PPPIO_GETSTAT _PPPIO(131) /* get PPP statistics */
+#define PPPIO_GETCSTAT _PPPIO(132) /* get PPP compression stats */
+#define PPPIO_MTU _PPPIO(133) /* set max transmission unit */
+#define PPPIO_MRU _PPPIO(134) /* set max receive unit */
+#define PPPIO_CFLAGS _PPPIO(135) /* set/clear/get compression flags */
+#define PPPIO_XCOMP _PPPIO(136) /* alloc transmit compressor */
+#define PPPIO_RCOMP _PPPIO(137) /* alloc receive decompressor */
+#define PPPIO_XACCM _PPPIO(138) /* set transmit asyncmap */
+#define PPPIO_RACCM _PPPIO(139) /* set receive asyncmap */
+#define PPPIO_VJINIT _PPPIO(140) /* initialize VJ comp/decomp */
+#define PPPIO_ATTACH _PPPIO(141) /* attach to a ppa (without putmsg) */
+#define PPPIO_LASTMOD _PPPIO(142) /* mark last ppp module */
+#define PPPIO_GCLEAN _PPPIO(143) /* get 8-bit-clean flags */
+#define PPPIO_DEBUG _PPPIO(144) /* request debug information */
+#define PPPIO_BIND _PPPIO(145) /* bind to SAP */
+#define PPPIO_NPMODE _PPPIO(146) /* set mode for handling data pkts */
+#define PPPIO_GIDLE _PPPIO(147) /* get time since last data pkt */
+#define PPPIO_PASSFILT _PPPIO(148) /* set filter for packets to pass */
+#define PPPIO_ACTIVEFILT _PPPIO(149) /* set filter for "link active" pkts */
+
+/*
+ * Values for PPPIO_CFLAGS
+ */
+#define COMP_AC 0x1 /* compress address/control */
+#define DECOMP_AC 0x2 /* decompress address/control */
+#define COMP_PROT 0x4 /* compress PPP protocol */
+#define DECOMP_PROT 0x8 /* decompress PPP protocol */
+
+#define COMP_VJC 0x10 /* compress TCP/IP headers */
+#define COMP_VJCCID 0x20 /* compress connection ID as well */
+#define DECOMP_VJC 0x40 /* decompress TCP/IP headers */
+#define DECOMP_VJCCID 0x80 /* accept compressed connection ID */
+
+#define CCP_ISOPEN 0x100 /* look at CCP packets */
+#define CCP_ISUP 0x200 /* do packet comp/decomp */
+#define CCP_ERROR 0x400 /* (status) error in packet decomp */
+#define CCP_FATALERROR 0x800 /* (status) fatal error ditto */
+#define CCP_COMP_RUN 0x1000 /* (status) seen CCP ack sent */
+#define CCP_DECOMP_RUN 0x2000 /* (status) seen CCP ack rcvd */
+
+/*
+ * Values for 8-bit-clean flags.
+ */
+#define RCV_B7_0 1 /* have rcvd char with bit 7 = 0 */
+#define RCV_B7_1 2 /* have rcvd char with bit 7 = 1 */
+#define RCV_EVNP 4 /* have rcvd char with even parity */
+#define RCV_ODDP 8 /* have rcvd char with odd parity */
+
+/*
+ * Values for the first byte of M_CTL messages passed between
+ * PPP modules.
+ */
+#define PPPCTL_OERROR 0xe0 /* output error [up] */
+#define PPPCTL_IERROR 0xe1 /* input error (e.g. FCS) [up] */
+#define PPPCTL_MTU 0xe2 /* set MTU [down] */
+#define PPPCTL_MRU 0xe3 /* set MRU [down] */
+#define PPPCTL_UNIT 0xe4 /* note PPP unit number [down] */
+
+/*
+ * Values for the integer argument to PPPIO_DEBUG.
+ */
+#define PPPDBG_DUMP 0x10000 /* print out debug info now */
+#define PPPDBG_LOG 0x100 /* log various things */
+#define PPPDBG_DRIVER 0 /* identifies ppp driver as target */
+#define PPPDBG_IF 1 /* identifies ppp network i/f target */
+#define PPPDBG_COMP 2 /* identifies ppp compression target */
+#define PPPDBG_AHDLC 3 /* identifies ppp async hdlc target */
diff --git a/mdk-stage1/ppp/include/net/slcompress.h b/mdk-stage1/ppp/include/net/slcompress.h
new file mode 100644
index 000000000..9e19bc0ed
--- /dev/null
+++ b/mdk-stage1/ppp/include/net/slcompress.h
@@ -0,0 +1,148 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id$
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#ifndef _SLCOMPRESS_H_
+#define _SLCOMPRESS_H_
+
+#define MAX_STATES 16 /* must be > 2 and < 256 */
+#define MAX_HDR MLEN /* XXX 4bsd-ism: should really be 128 */
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ struct cstate *cs_next; /* next most recently used cstate (xmit only) */
+ u_short cs_hlen; /* size of hdr (receive only) */
+ u_char cs_id; /* connection # associated with this state */
+ u_char cs_filler;
+ union {
+ char csu_hdr[MAX_HDR];
+ struct ip csu_ip; /* ip/tcp hdr from most recent packet */
+ } slcs_u;
+};
+#define cs_ip slcs_u.csu_ip
+#define cs_hdr slcs_u.csu_hdr
+
+/*
+ * all the state data for one serial line (we need one of these
+ * per line).
+ */
+struct slcompress {
+ struct cstate *last_cs; /* most recently used tstate */
+ u_char last_recv; /* last rcvd conn. id */
+ u_char last_xmit; /* last sent conn. id */
+ u_short flags;
+#ifndef SL_NO_STATS
+ int sls_packets; /* outbound packets */
+ int sls_compressed; /* outbound compressed packets */
+ int sls_searches; /* searches for connection state */
+ int sls_misses; /* times couldn't find conn. state */
+ int sls_uncompressedin; /* inbound uncompressed packets */
+ int sls_compressedin; /* inbound compressed packets */
+ int sls_errorin; /* inbound unknown type packets */
+ int sls_tossed; /* inbound packets tossed because of error */
+#endif
+ struct cstate tstate[MAX_STATES]; /* xmit connection states */
+ struct cstate rstate[MAX_STATES]; /* receive connection states */
+};
+/* flag values */
+#define SLF_TOSS 1 /* tossing rcvd frames because of input err */
+
+void sl_compress_init __P((struct slcompress *));
+void sl_compress_setup __P((struct slcompress *, int));
+u_int sl_compress_tcp __P((struct mbuf *,
+ struct ip *, struct slcompress *, int));
+int sl_uncompress_tcp __P((u_char **, int, u_int, struct slcompress *));
+int sl_uncompress_tcp_core __P((u_char *, int, int, u_int,
+ struct slcompress *, u_char **, u_int *));
+
+#endif /* _SLCOMPRESS_H_ */
diff --git a/mdk-stage1/ppp/include/net/vjcompress.h b/mdk-stage1/ppp/include/net/vjcompress.h
new file mode 100644
index 000000000..c64596926
--- /dev/null
+++ b/mdk-stage1/ppp/include/net/vjcompress.h
@@ -0,0 +1,144 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id$
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#ifndef _VJCOMPRESS_H_
+#define _VJCOMPRESS_H_
+
+#define MAX_STATES 16 /* must be > 2 and < 256 */
+#define MAX_HDR 128
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ struct cstate *cs_next; /* next most recently used state (xmit only) */
+ u_short cs_hlen; /* size of hdr (receive only) */
+ u_char cs_id; /* connection # associated with this state */
+ u_char cs_filler;
+ union {
+ char csu_hdr[MAX_HDR];
+ struct ip csu_ip; /* ip/tcp hdr from most recent packet */
+ } vjcs_u;
+};
+#define cs_ip vjcs_u.csu_ip
+#define cs_hdr vjcs_u.csu_hdr
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct vjcompress {
+ struct cstate *last_cs; /* most recently used tstate */
+ u_char last_recv; /* last rcvd conn. id */
+ u_char last_xmit; /* last sent conn. id */
+ u_short flags;
+#ifndef VJ_NO_STATS
+ struct vjstat stats;
+#endif
+ struct cstate tstate[MAX_STATES]; /* xmit connection states */
+ struct cstate rstate[MAX_STATES]; /* receive connection states */
+};
+
+/* flag values */
+#define VJF_TOSS 1 /* tossing rcvd frames because of input err */
+
+extern void vj_compress_init __P((struct vjcompress *comp, int max_state));
+extern u_int vj_compress_tcp __P((struct ip *ip, u_int mlen,
+ struct vjcompress *comp, int compress_cid_flag,
+ u_char **vjhdrp));
+extern void vj_uncompress_err __P((struct vjcompress *comp));
+extern int vj_uncompress_uncomp __P((u_char *buf, int buflen,
+ struct vjcompress *comp));
+extern int vj_uncompress_tcp __P((u_char *buf, int buflen, int total_len,
+ struct vjcompress *comp, u_char **hdrp,
+ u_int *hlenp));
+
+#endif /* _VJCOMPRESS_H_ */
diff --git a/mdk-stage1/ppp/include/pcap-int.h b/mdk-stage1/ppp/include/pcap-int.h
new file mode 100644
index 000000000..323994929
--- /dev/null
+++ b/mdk-stage1/ppp/include/pcap-int.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) $Header$ (LBL)
+ */
+
+#ifndef pcap_int_h
+#define pcap_int_h
+
+#include <pcap.h>
+
+/*
+ * Savefile
+ */
+struct pcap_sf {
+ FILE *rfile;
+ int swapped;
+ int version_major;
+ int version_minor;
+ u_char *base;
+};
+
+struct pcap_md {
+ struct pcap_stat stat;
+ /*XXX*/
+ int use_bpf;
+ u_long TotPkts; /* can't oflow for 79 hrs on ether */
+ u_long TotAccepted; /* count accepted by filter */
+ u_long TotDrops; /* count of dropped packets */
+ long TotMissed; /* missed by i/f during this run */
+ long OrigMissed; /* missed by i/f before this run */
+#ifdef linux
+ int pad;
+ int skip;
+ char *device;
+#endif
+};
+
+struct pcap {
+ int fd;
+ int snapshot;
+ int linktype;
+ int tzoff; /* timezone offset */
+ int offset; /* offset for proper alignment */
+
+ struct pcap_sf sf;
+ struct pcap_md md;
+
+ /*
+ * Read buffer.
+ */
+ int bufsize;
+ u_char *buffer;
+ u_char *bp;
+ int cc;
+
+ /*
+ * Place holder for pcap_next().
+ */
+ u_char *pkt;
+
+
+ /*
+ * Placeholder for filter code if bpf not in kernel.
+ */
+ struct bpf_program fcode;
+
+ char errbuf[PCAP_ERRBUF_SIZE];
+};
+
+int yylex(void);
+
+#ifndef min
+#define min(a, b) ((a) > (b) ? (b) : (a))
+#endif
+
+/* XXX should these be in pcap.h? */
+int pcap_offline_read(pcap_t *, int, pcap_handler, u_char *);
+int pcap_read(pcap_t *, int cnt, pcap_handler, u_char *);
+
+/* Ultrix pads to make everything line up on a nice boundary */
+#if defined(ultrix) || defined(__alpha)
+#define PCAP_FDDIPAD 3
+#endif
+
+/* XXX */
+extern int pcap_fddipad;
+#endif
diff --git a/mdk-stage1/ppp/linux/Makefile.top b/mdk-stage1/ppp/linux/Makefile.top
new file mode 100644
index 000000000..fa34ce59f
--- /dev/null
+++ b/mdk-stage1/ppp/linux/Makefile.top
@@ -0,0 +1,55 @@
+# PPP top-level Makefile for Linux.
+
+
+BINDIR = $(DESTDIR)/usr/sbin
+MANDIR = $(DESTDIR)/usr/man
+ETCDIR = $(DESTDIR)/etc/ppp
+
+# uid 0 = root
+INSTALL= install
+
+all:
+ cd chat; $(MAKE) $(MFLAGS) all
+ cd pppd; $(MAKE) $(MFLAGS) all
+ cd pppstats; $(MAKE) $(MFLAGS) all
+ cd pppdump; $(MAKE) $(MFLAGS) all
+
+install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp
+
+install-progs:
+ cd chat; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
+ cd pppd; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
+ cd pppstats; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
+ cd pppdump; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
+
+install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
+ $(ETCDIR)/chap-secrets
+
+$(ETCDIR)/options:
+ $(INSTALL) -c -m 644 etc.ppp/options $@
+$(ETCDIR)/pap-secrets:
+ $(INSTALL) -c -m 600 etc.ppp/pap-secrets $@
+$(ETCDIR)/chap-secrets:
+ $(INSTALL) -c -m 600 etc.ppp/chap-secrets $@
+
+$(BINDIR):
+ $(INSTALL) -d -m 755 $@
+$(MANDIR)/man8:
+ $(INSTALL) -d -m 755 $@
+$(ETCDIR):
+ $(INSTALL) -d -m 755 $@
+
+clean:
+ rm -f `find . -name '*.[oas]' -print`
+ rm -f `find . -name 'core' -print`
+ rm -f `find . -name '*~' -print`
+ cd chat; $(MAKE) clean
+ cd pppd; $(MAKE) clean
+ cd pppstats; $(MAKE) clean
+ cd pppdump; $(MAKE) clean
+
+dist-clean: clean
+ rm -f Makefile `find . -name Makefile -print`
+
+#kernel:
+# cd linux; ./kinstall.sh
diff --git a/mdk-stage1/ppp/modules/bsd-comp.c b/mdk-stage1/ppp/modules/bsd-comp.c
new file mode 100644
index 000000000..b1b9325c7
--- /dev/null
+++ b/mdk-stage1/ppp/modules/bsd-comp.c
@@ -0,0 +1,1116 @@
+/* Because this code is derived from the 4.3BSD compress source:
+ *
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This version is for use with STREAMS under SunOS 4.x,
+ * Digital UNIX, AIX 4.x, and SVR4 systems including Solaris 2.
+ *
+ * $Id$
+ */
+
+#ifdef AIX4
+#include <net/net_globals.h>
+#endif
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <net/ppp_defs.h>
+#include "ppp_mod.h"
+
+#ifdef SVR4
+#include <sys/byteorder.h>
+#ifndef _BIG_ENDIAN
+#define BSD_LITTLE_ENDIAN
+#endif
+#endif
+
+#ifdef __osf__
+#undef FIRST
+#undef LAST
+#define BSD_LITTLE_ENDIAN
+#endif
+
+#define PACKETPTR mblk_t *
+#include <net/ppp-comp.h>
+
+#if DO_BSD_COMPRESS
+
+/*
+ * PPP "BSD compress" compression
+ * The differences between this compression and the classic BSD LZW
+ * source are obvious from the requirement that the classic code worked
+ * with files while this handles arbitrarily long streams that
+ * are broken into packets. They are:
+ *
+ * When the code size expands, a block of junk is not emitted by
+ * the compressor and not expected by the decompressor.
+ *
+ * New codes are not necessarily assigned every time an old
+ * code is output by the compressor. This is because a packet
+ * end forces a code to be emitted, but does not imply that a
+ * new sequence has been seen.
+ *
+ * The compression ratio is checked at the first end of a packet
+ * after the appropriate gap. Besides simplifying and speeding
+ * things up, this makes it more likely that the transmitter
+ * and receiver will agree when the dictionary is cleared when
+ * compression is not going well.
+ */
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+struct bsd_db {
+ int totlen; /* length of this structure */
+ u_int hsize; /* size of the hash table */
+ u_char hshift; /* used in hash function */
+ u_char n_bits; /* current bits/code */
+ u_char maxbits;
+ u_char debug;
+ u_char unit;
+ u_short seqno; /* sequence number of next packet */
+ u_int hdrlen; /* header length to preallocate */
+ u_int mru;
+ u_int maxmaxcode; /* largest valid code */
+ u_int max_ent; /* largest code in use */
+ u_int in_count; /* uncompressed bytes, aged */
+ u_int bytes_out; /* compressed bytes, aged */
+ u_int ratio; /* recent compression ratio */
+ u_int checkpoint; /* when to next check the ratio */
+ u_int clear_count; /* times dictionary cleared */
+ u_int incomp_count; /* incompressible packets */
+ u_int incomp_bytes; /* incompressible bytes */
+ u_int uncomp_count; /* uncompressed packets */
+ u_int uncomp_bytes; /* uncompressed bytes */
+ u_int comp_count; /* compressed packets */
+ u_int comp_bytes; /* compressed bytes */
+ u_short *lens; /* array of lengths of codes */
+ struct bsd_dict {
+ union { /* hash value */
+ u_int32_t fcode;
+ struct {
+#ifdef BSD_LITTLE_ENDIAN
+ u_short prefix; /* preceding code */
+ u_char suffix; /* last character of new code */
+ u_char pad;
+#else
+ u_char pad;
+ u_char suffix; /* last character of new code */
+ u_short prefix; /* preceding code */
+#endif
+ } hs;
+ } f;
+ u_short codem1; /* output of hash table -1 */
+ u_short cptr; /* map code to hash table entry */
+ } dict[1];
+};
+
+#define BSD_OVHD 2 /* BSD compress overhead/packet */
+#define BSD_INIT_BITS BSD_MIN_BITS
+
+static void *bsd_comp_alloc __P((u_char *options, int opt_len));
+static void *bsd_decomp_alloc __P((u_char *options, int opt_len));
+static void bsd_free __P((void *state));
+static int bsd_comp_init __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int debug));
+static int bsd_decomp_init __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+static int bsd_compress __P((void *state, mblk_t **mret,
+ mblk_t *mp, int slen, int maxolen));
+static void bsd_incomp __P((void *state, mblk_t *dmsg));
+static int bsd_decompress __P((void *state, mblk_t *cmp, mblk_t **dmpp));
+static void bsd_reset __P((void *state));
+static void bsd_comp_stats __P((void *state, struct compstat *stats));
+
+/*
+ * Procedures exported to ppp_comp.c.
+ */
+struct compressor ppp_bsd_compress = {
+ CI_BSD_COMPRESS, /* compress_proto */
+ bsd_comp_alloc, /* comp_alloc */
+ bsd_free, /* comp_free */
+ bsd_comp_init, /* comp_init */
+ bsd_reset, /* comp_reset */
+ bsd_compress, /* compress */
+ bsd_comp_stats, /* comp_stat */
+ bsd_decomp_alloc, /* decomp_alloc */
+ bsd_free, /* decomp_free */
+ bsd_decomp_init, /* decomp_init */
+ bsd_reset, /* decomp_reset */
+ bsd_decompress, /* decompress */
+ bsd_incomp, /* incomp */
+ bsd_comp_stats, /* decomp_stat */
+};
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR 256 /* table clear output code */
+#define FIRST 257 /* first free entry */
+#define LAST 255
+
+#define MAXCODE(b) ((1 << (b)) - 1)
+#define BADCODEM1 MAXCODE(BSD_MAX_BITS)
+
+#define BSD_HASH(prefix,suffix,hshift) ((((u_int32_t)(suffix)) << (hshift)) \
+ ^ (u_int32_t)(prefix))
+#define BSD_KEY(prefix,suffix) ((((u_int32_t)(suffix)) << 16) \
+ + (u_int32_t)(prefix))
+
+#define CHECK_GAP 10000 /* Ratio check interval */
+
+#define RATIO_SCALE_LOG 8
+#define RATIO_SCALE (1<<RATIO_SCALE_LOG)
+#define RATIO_MAX (0x7fffffff>>RATIO_SCALE_LOG)
+
+#define DECOMP_CHUNK 256
+
+/*
+ * clear the dictionary
+ */
+static void
+bsd_clear(db)
+ struct bsd_db *db;
+{
+ db->clear_count++;
+ db->max_ent = FIRST-1;
+ db->n_bits = BSD_INIT_BITS;
+ db->ratio = 0;
+ db->bytes_out = 0;
+ db->in_count = 0;
+ db->checkpoint = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+static int /* 1=output CLEAR */
+bsd_check(db)
+ struct bsd_db *db;
+{
+ u_int new_ratio;
+
+ if (db->in_count >= db->checkpoint) {
+ /* age the ratio by limiting the size of the counts */
+ if (db->in_count >= RATIO_MAX
+ || db->bytes_out >= RATIO_MAX) {
+ db->in_count -= db->in_count/4;
+ db->bytes_out -= db->bytes_out/4;
+ }
+
+ db->checkpoint = db->in_count + CHECK_GAP;
+
+ if (db->max_ent >= db->maxmaxcode) {
+ /* Reset the dictionary only if the ratio is worse,
+ * or if it looks as if it has been poisoned
+ * by incompressible data.
+ *
+ * This does not overflow, because
+ * db->in_count <= RATIO_MAX.
+ */
+ new_ratio = db->in_count << RATIO_SCALE_LOG;
+ if (db->bytes_out != 0)
+ new_ratio /= db->bytes_out;
+
+ if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE) {
+ bsd_clear(db);
+ return 1;
+ }
+ db->ratio = new_ratio;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return statistics.
+ */
+static void
+bsd_comp_stats(state, stats)
+ void *state;
+ struct compstat *stats;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ u_int out;
+
+ stats->unc_bytes = db->uncomp_bytes;
+ stats->unc_packets = db->uncomp_count;
+ stats->comp_bytes = db->comp_bytes;
+ stats->comp_packets = db->comp_count;
+ stats->inc_bytes = db->incomp_bytes;
+ stats->inc_packets = db->incomp_count;
+ stats->ratio = db->in_count;
+ out = db->bytes_out;
+ if (stats->ratio <= 0x7fffff)
+ stats->ratio <<= 8;
+ else
+ out >>= 8;
+ if (out != 0)
+ stats->ratio /= out;
+}
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+static void
+bsd_reset(state)
+ void *state;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ db->seqno = 0;
+ bsd_clear(db);
+ db->clear_count = 0;
+}
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+static void *
+bsd_alloc(options, opt_len, decomp)
+ u_char *options;
+ int opt_len, decomp;
+{
+ int bits;
+ u_int newlen, hsize, hshift, maxmaxcode;
+ struct bsd_db *db;
+
+ if (opt_len != 3 || options[0] != CI_BSD_COMPRESS || options[1] != 3
+ || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION)
+ return NULL;
+
+ bits = BSD_NBITS(options[2]);
+ switch (bits) {
+ case 9: /* needs 82152 for both directions */
+ case 10: /* needs 84144 */
+ case 11: /* needs 88240 */
+ case 12: /* needs 96432 */
+ hsize = 5003;
+ hshift = 4;
+ break;
+ case 13: /* needs 176784 */
+ hsize = 9001;
+ hshift = 5;
+ break;
+ case 14: /* needs 353744 */
+ hsize = 18013;
+ hshift = 6;
+ break;
+ case 15: /* needs 691440 */
+ hsize = 35023;
+ hshift = 7;
+ break;
+ case 16: /* needs 1366160--far too much, */
+ /* hsize = 69001; */ /* and 69001 is too big for cptr */
+ /* hshift = 8; */ /* in struct bsd_db */
+ /* break; */
+ default:
+ return NULL;
+ }
+
+ maxmaxcode = MAXCODE(bits);
+ newlen = sizeof(*db) + (hsize-1) * (sizeof(db->dict[0]));
+#ifdef __osf__
+ db = (struct bsd_db *) ALLOC_SLEEP(newlen);
+#else
+ db = (struct bsd_db *) ALLOC_NOSLEEP(newlen);
+#endif
+ if (!db)
+ return NULL;
+ bzero(db, sizeof(*db) - sizeof(db->dict));
+
+ if (!decomp) {
+ db->lens = NULL;
+ } else {
+#ifdef __osf__
+ db->lens = (u_short *) ALLOC_SLEEP((maxmaxcode+1) * sizeof(db->lens[0]));
+#else
+ db->lens = (u_short *) ALLOC_NOSLEEP((maxmaxcode+1) * sizeof(db->lens[0]));
+#endif
+ if (!db->lens) {
+ FREE(db, newlen);
+ return NULL;
+ }
+ }
+
+ db->totlen = newlen;
+ db->hsize = hsize;
+ db->hshift = hshift;
+ db->maxmaxcode = maxmaxcode;
+ db->maxbits = bits;
+
+ return (void *) db;
+}
+
+static void
+bsd_free(state)
+ void *state;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ if (db->lens)
+ FREE(db->lens, (db->maxmaxcode+1) * sizeof(db->lens[0]));
+ FREE(db, db->totlen);
+}
+
+static void *
+bsd_comp_alloc(options, opt_len)
+ u_char *options;
+ int opt_len;
+{
+ return bsd_alloc(options, opt_len, 0);
+}
+
+static void *
+bsd_decomp_alloc(options, opt_len)
+ u_char *options;
+ int opt_len;
+{
+ return bsd_alloc(options, opt_len, 1);
+}
+
+/*
+ * Initialize the database.
+ */
+static int
+bsd_init(db, options, opt_len, unit, hdrlen, mru, debug, decomp)
+ struct bsd_db *db;
+ u_char *options;
+ int opt_len, unit, hdrlen, mru, debug, decomp;
+{
+ int i;
+
+ if (opt_len < CILEN_BSD_COMPRESS
+ || options[0] != CI_BSD_COMPRESS || options[1] != CILEN_BSD_COMPRESS
+ || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION
+ || BSD_NBITS(options[2]) != db->maxbits
+ || decomp && db->lens == NULL)
+ return 0;
+
+ if (decomp) {
+ i = LAST+1;
+ while (i != 0)
+ db->lens[--i] = 1;
+ }
+ i = db->hsize;
+ while (i != 0) {
+ db->dict[--i].codem1 = BADCODEM1;
+ db->dict[i].cptr = 0;
+ }
+
+ db->unit = unit;
+ db->hdrlen = hdrlen;
+ db->mru = mru;
+ if (debug)
+ db->debug = 1;
+
+ bsd_reset(db);
+
+ return 1;
+}
+
+static int
+bsd_comp_init(state, options, opt_len, unit, hdrlen, debug)
+ void *state;
+ u_char *options;
+ int opt_len, unit, hdrlen, debug;
+{
+ return bsd_init((struct bsd_db *) state, options, opt_len,
+ unit, hdrlen, 0, debug, 0);
+}
+
+static int
+bsd_decomp_init(state, options, opt_len, unit, hdrlen, mru, debug)
+ void *state;
+ u_char *options;
+ int opt_len, unit, hdrlen, mru, debug;
+{
+ return bsd_init((struct bsd_db *) state, options, opt_len,
+ unit, hdrlen, mru, debug, 1);
+}
+
+
+/*
+ * compress a packet
+ * One change from the BSD compress command is that when the
+ * code size expands, we do not output a bunch of padding.
+ *
+ * N.B. at present, we ignore the hdrlen specified in the comp_init call.
+ */
+static int /* new slen */
+bsd_compress(state, mretp, mp, slen, maxolen)
+ void *state;
+ mblk_t **mretp; /* return compressed mbuf chain here */
+ mblk_t *mp; /* from here */
+ int slen; /* uncompressed length */
+ int maxolen; /* max compressed length */
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ int hshift = db->hshift;
+ u_int max_ent = db->max_ent;
+ u_int n_bits = db->n_bits;
+ u_int bitno = 32;
+ u_int32_t accm = 0, fcode;
+ struct bsd_dict *dictp;
+ u_char c;
+ int hval, disp, ent, ilen;
+ mblk_t *np, *mret;
+ u_char *rptr, *wptr;
+ u_char *cp_end;
+ int olen;
+ mblk_t *m, **mnp;
+
+#define PUTBYTE(v) { \
+ if (wptr) { \
+ *wptr++ = (v); \
+ if (wptr >= cp_end) { \
+ m->b_wptr = wptr; \
+ m = m->b_cont; \
+ if (m) { \
+ wptr = m->b_wptr; \
+ cp_end = m->b_datap->db_lim; \
+ } else \
+ wptr = NULL; \
+ } \
+ } \
+ ++olen; \
+}
+
+#define OUTPUT(ent) { \
+ bitno -= n_bits; \
+ accm |= ((ent) << bitno); \
+ do { \
+ PUTBYTE(accm >> 24); \
+ accm <<= 8; \
+ bitno += 8; \
+ } while (bitno <= 24); \
+}
+
+ /*
+ * First get the protocol and check that we're
+ * interested in this packet.
+ */
+ *mretp = NULL;
+ rptr = mp->b_rptr;
+ if (rptr + PPP_HDRLEN > mp->b_wptr) {
+ if (!pullupmsg(mp, PPP_HDRLEN))
+ return 0;
+ rptr = mp->b_rptr;
+ }
+ ent = PPP_PROTOCOL(rptr); /* get the protocol */
+ if (ent < 0x21 || ent > 0xf9)
+ return 0;
+
+ /* Don't generate compressed packets which are larger than
+ the uncompressed packet. */
+ if (maxolen > slen)
+ maxolen = slen;
+
+ /* Allocate enough message blocks to give maxolen total space. */
+ mnp = &mret;
+ for (olen = maxolen; olen > 0; ) {
+ m = allocb((olen < 4096? olen: 4096), BPRI_MED);
+ *mnp = m;
+ if (m == NULL) {
+ if (mret != NULL) {
+ freemsg(mret);
+ mnp = &mret;
+ }
+ break;
+ }
+ mnp = &m->b_cont;
+ olen -= m->b_datap->db_lim - m->b_wptr;
+ }
+ *mnp = NULL;
+
+ if ((m = mret) != NULL) {
+ wptr = m->b_wptr;
+ cp_end = m->b_datap->db_lim;
+ } else
+ wptr = cp_end = NULL;
+ olen = 0;
+
+ /*
+ * Copy the PPP header over, changing the protocol,
+ * and install the 2-byte sequence number.
+ */
+ if (wptr) {
+ wptr[0] = PPP_ADDRESS(rptr);
+ wptr[1] = PPP_CONTROL(rptr);
+ wptr[2] = 0; /* change the protocol */
+ wptr[3] = PPP_COMP;
+ wptr[4] = db->seqno >> 8;
+ wptr[5] = db->seqno;
+ wptr += PPP_HDRLEN + BSD_OVHD;
+ }
+ ++db->seqno;
+ rptr += PPP_HDRLEN;
+
+ slen = mp->b_wptr - rptr;
+ ilen = slen + 1;
+ np = mp->b_cont;
+ for (;;) {
+ if (slen <= 0) {
+ if (!np)
+ break;
+ rptr = np->b_rptr;
+ slen = np->b_wptr - rptr;
+ np = np->b_cont;
+ if (!slen)
+ continue; /* handle 0-length buffers */
+ ilen += slen;
+ }
+
+ slen--;
+ c = *rptr++;
+ fcode = BSD_KEY(ent, c);
+ hval = BSD_HASH(ent, c, hshift);
+ dictp = &db->dict[hval];
+
+ /* Validate and then check the entry. */
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ if (dictp->f.fcode == fcode) {
+ ent = dictp->codem1+1;
+ continue; /* found (prefix,suffix) */
+ }
+
+ /* continue probing until a match or invalid entry */
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = &db->dict[hval];
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ } while (dictp->f.fcode != fcode);
+ ent = dictp->codem1 + 1; /* finally found (prefix,suffix) */
+ continue;
+
+ nomatch:
+ OUTPUT(ent); /* output the prefix */
+
+ /* code -> hashtable */
+ if (max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ /* expand code size if needed */
+ if (max_ent >= MAXCODE(n_bits))
+ db->n_bits = ++n_bits;
+
+ /* Invalidate old hash table entry using
+ * this code, and then take it over.
+ */
+ dictp2 = &db->dict[max_ent+1];
+ if (db->dict[dictp2->cptr].codem1 == max_ent)
+ db->dict[dictp2->cptr].codem1 = BADCODEM1;
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->f.fcode = fcode;
+
+ db->max_ent = ++max_ent;
+ }
+ ent = c;
+ }
+
+ OUTPUT(ent); /* output the last code */
+ db->bytes_out += olen;
+ db->in_count += ilen;
+ if (bitno < 32)
+ ++db->bytes_out; /* count complete bytes */
+
+ if (bsd_check(db))
+ OUTPUT(CLEAR); /* do not count the CLEAR */
+
+ /*
+ * Pad dribble bits of last code with ones.
+ * Do not emit a completely useless byte of ones.
+ */
+ if (bitno != 32)
+ PUTBYTE((accm | (0xff << (bitno-8))) >> 24);
+
+ /*
+ * Increase code size if we would have without the packet
+ * boundary and as the decompressor will.
+ */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode)
+ db->n_bits++;
+
+ db->uncomp_bytes += ilen;
+ ++db->uncomp_count;
+ if (olen + PPP_HDRLEN + BSD_OVHD > maxolen && mret != NULL) {
+ /* throw away the compressed stuff if it is longer than uncompressed */
+ freemsg(mret);
+ mret = NULL;
+ ++db->incomp_count;
+ db->incomp_bytes += ilen;
+ } else if (wptr != NULL) {
+ m->b_wptr = wptr;
+ if (m->b_cont) {
+ freemsg(m->b_cont);
+ m->b_cont = NULL;
+ }
+ ++db->comp_count;
+ db->comp_bytes += olen + BSD_OVHD;
+ }
+
+ *mretp = mret;
+ return olen + PPP_HDRLEN + BSD_OVHD;
+#undef OUTPUT
+#undef PUTBYTE
+}
+
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+static void
+bsd_incomp(state, dmsg)
+ void *state;
+ mblk_t *dmsg;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ u_int hshift = db->hshift;
+ u_int max_ent = db->max_ent;
+ u_int n_bits = db->n_bits;
+ struct bsd_dict *dictp;
+ u_int32_t fcode;
+ u_char c;
+ long hval, disp;
+ int slen, ilen;
+ u_int bitno = 7;
+ u_char *rptr;
+ u_int ent;
+
+ rptr = dmsg->b_rptr;
+ if (rptr + PPP_HDRLEN > dmsg->b_wptr) {
+ if (!pullupmsg(dmsg, PPP_HDRLEN))
+ return;
+ rptr = dmsg->b_rptr;
+ }
+ ent = PPP_PROTOCOL(rptr); /* get the protocol */
+ if (ent < 0x21 || ent > 0xf9)
+ return;
+
+ db->seqno++;
+ ilen = 1; /* count the protocol as 1 byte */
+ rptr += PPP_HDRLEN;
+ for (;;) {
+ slen = dmsg->b_wptr - rptr;
+ if (slen <= 0) {
+ dmsg = dmsg->b_cont;
+ if (!dmsg)
+ break;
+ rptr = dmsg->b_rptr;
+ continue; /* skip zero-length buffers */
+ }
+ ilen += slen;
+
+ do {
+ c = *rptr++;
+ fcode = BSD_KEY(ent, c);
+ hval = BSD_HASH(ent, c, hshift);
+ dictp = &db->dict[hval];
+
+ /* validate and then check the entry */
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ if (dictp->f.fcode == fcode) {
+ ent = dictp->codem1+1;
+ continue; /* found (prefix,suffix) */
+ }
+
+ /* continue probing until a match or invalid entry */
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = &db->dict[hval];
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ } while (dictp->f.fcode != fcode);
+ ent = dictp->codem1+1;
+ continue; /* finally found (prefix,suffix) */
+
+ nomatch: /* output (count) the prefix */
+ bitno += n_bits;
+
+ /* code -> hashtable */
+ if (max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ /* expand code size if needed */
+ if (max_ent >= MAXCODE(n_bits))
+ db->n_bits = ++n_bits;
+
+ /* Invalidate previous hash table entry
+ * assigned this code, and then take it over.
+ */
+ dictp2 = &db->dict[max_ent+1];
+ if (db->dict[dictp2->cptr].codem1 == max_ent)
+ db->dict[dictp2->cptr].codem1 = BADCODEM1;
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->f.fcode = fcode;
+
+ db->max_ent = ++max_ent;
+ db->lens[max_ent] = db->lens[ent]+1;
+ }
+ ent = c;
+ } while (--slen != 0);
+ }
+ bitno += n_bits; /* output (count) the last code */
+ db->bytes_out += bitno/8;
+ db->in_count += ilen;
+ (void)bsd_check(db);
+
+ ++db->incomp_count;
+ db->incomp_bytes += ilen;
+ ++db->uncomp_count;
+ db->uncomp_bytes += ilen;
+
+ /* Increase code size if we would have without the packet
+ * boundary and as the decompressor will.
+ */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode)
+ db->n_bits++;
+}
+
+
+/*
+ * Decompress "BSD Compress"
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression. For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+static int
+bsd_decompress(state, cmsg, dmpp)
+ void *state;
+ mblk_t *cmsg, **dmpp;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ u_int max_ent = db->max_ent;
+ u_int32_t accm = 0;
+ u_int bitno = 32; /* 1st valid bit in accm */
+ u_int n_bits = db->n_bits;
+ u_int tgtbitno = 32-n_bits; /* bitno when we have a code */
+ struct bsd_dict *dictp;
+ int explen, i, seq, len;
+ u_int incode, oldcode, finchar;
+ u_char *p, *rptr, *wptr;
+ mblk_t *dmsg, *mret;
+ int adrs, ctrl, ilen;
+ int dlen, space, codelen, extra;
+
+ /*
+ * Get at least the BSD Compress header in the first buffer
+ */
+ rptr = cmsg->b_rptr;
+ if (rptr + PPP_HDRLEN + BSD_OVHD >= cmsg->b_wptr) {
+ if (!pullupmsg(cmsg, PPP_HDRLEN + BSD_OVHD + 1)) {
+ if (db->debug)
+ printf("bsd_decomp%d: failed to pullup\n", db->unit);
+ return DECOMP_ERROR;
+ }
+ rptr = cmsg->b_rptr;
+ }
+
+ /*
+ * Save the address/control from the PPP header
+ * and then get the sequence number.
+ */
+ adrs = PPP_ADDRESS(rptr);
+ ctrl = PPP_CONTROL(rptr);
+ rptr += PPP_HDRLEN;
+ seq = (rptr[0] << 8) + rptr[1];
+ rptr += BSD_OVHD;
+ ilen = len = cmsg->b_wptr - rptr;
+
+ /*
+ * Check the sequence number and give up if it is not what we expect.
+ */
+ if (seq != db->seqno++) {
+ if (db->debug)
+ printf("bsd_decomp%d: bad sequence # %d, expected %d\n",
+ db->unit, seq, db->seqno - 1);
+ return DECOMP_ERROR;
+ }
+
+ /*
+ * Allocate one message block to start with.
+ */
+ if ((dmsg = allocb(DECOMP_CHUNK + db->hdrlen, BPRI_MED)) == NULL)
+ return DECOMP_ERROR;
+ mret = dmsg;
+ dmsg->b_wptr += db->hdrlen;
+ dmsg->b_rptr = wptr = dmsg->b_wptr;
+
+ /* Fill in the ppp header, but not the last byte of the protocol
+ (that comes from the decompressed data). */
+ wptr[0] = adrs;
+ wptr[1] = ctrl;
+ wptr[2] = 0;
+ wptr += PPP_HDRLEN - 1;
+ space = dmsg->b_datap->db_lim - wptr;
+
+ oldcode = CLEAR;
+ explen = 0;
+ for (;;) {
+ if (len == 0) {
+ cmsg = cmsg->b_cont;
+ if (!cmsg) /* quit at end of message */
+ break;
+ rptr = cmsg->b_rptr;
+ len = cmsg->b_wptr - rptr;
+ ilen += len;
+ continue; /* handle 0-length buffers */
+ }
+
+ /*
+ * Accumulate bytes until we have a complete code.
+ * Then get the next code, relying on the 32-bit,
+ * unsigned accm to mask the result.
+ */
+ bitno -= 8;
+ accm |= *rptr++ << bitno;
+ --len;
+ if (tgtbitno < bitno)
+ continue;
+ incode = accm >> tgtbitno;
+ accm <<= n_bits;
+ bitno += n_bits;
+
+ if (incode == CLEAR) {
+ /*
+ * The dictionary must only be cleared at
+ * the end of a packet. But there could be an
+ * empty message block at the end.
+ */
+ if (len > 0 || cmsg->b_cont != 0) {
+ if (cmsg->b_cont)
+ len += msgdsize(cmsg->b_cont);
+ if (len > 0) {
+ freemsg(dmsg);
+ if (db->debug)
+ printf("bsd_decomp%d: bad CLEAR\n", db->unit);
+ return DECOMP_FATALERROR;
+ }
+ }
+ bsd_clear(db);
+ explen = ilen = 0;
+ break;
+ }
+
+ if (incode > max_ent + 2 || incode > db->maxmaxcode
+ || incode > max_ent && oldcode == CLEAR) {
+ freemsg(dmsg);
+ if (db->debug) {
+ printf("bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+ db->unit, incode, oldcode);
+ printf("max_ent=0x%x dlen=%d seqno=%d\n",
+ max_ent, dlen, db->seqno);
+ }
+ return DECOMP_FATALERROR; /* probably a bug */
+ }
+
+ /* Special case for KwKwK string. */
+ if (incode > max_ent) {
+ finchar = oldcode;
+ extra = 1;
+ } else {
+ finchar = incode;
+ extra = 0;
+ }
+
+ codelen = db->lens[finchar];
+ explen += codelen + extra;
+ if (explen > db->mru + 1) {
+ freemsg(dmsg);
+ if (db->debug)
+ printf("bsd_decomp%d: ran out of mru\n", db->unit);
+ return DECOMP_FATALERROR;
+ }
+
+ /*
+ * Decode this code and install it in the decompressed buffer.
+ */
+ space -= codelen + extra;
+ if (space < 0) {
+ /* Allocate another message block. */
+ dmsg->b_wptr = wptr;
+ dlen = codelen + extra;
+ if (dlen < DECOMP_CHUNK)
+ dlen = DECOMP_CHUNK;
+ if ((dmsg->b_cont = allocb(dlen, BPRI_MED)) == NULL) {
+ freemsg(dmsg);
+ return DECOMP_ERROR;
+ }
+ dmsg = dmsg->b_cont;
+ wptr = dmsg->b_wptr;
+ space = dmsg->b_datap->db_lim - wptr - codelen - extra;
+ }
+ p = (wptr += codelen);
+ while (finchar > LAST) {
+ dictp = &db->dict[db->dict[finchar].cptr];
+#ifdef DEBUG
+ --codelen;
+ if (codelen <= 0) {
+ freemsg(dmsg);
+ printf("bsd_decomp%d: fell off end of chain ", db->unit);
+ printf("0x%x at 0x%x by 0x%x, max_ent=0x%x\n",
+ incode, finchar, db->dict[finchar].cptr, max_ent);
+ return DECOMP_FATALERROR;
+ }
+ if (dictp->codem1 != finchar-1) {
+ freemsg(dmsg);
+ printf("bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",
+ db->unit, incode, finchar);
+ printf("oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode,
+ db->dict[finchar].cptr, dictp->codem1);
+ return DECOMP_FATALERROR;
+ }
+#endif
+ *--p = dictp->f.hs.suffix;
+ finchar = dictp->f.hs.prefix;
+ }
+ *--p = finchar;
+
+#ifdef DEBUG
+ if (--codelen != 0)
+ printf("bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n",
+ db->unit, codelen, incode, max_ent);
+#endif
+
+ if (extra) /* the KwKwK case again */
+ *wptr++ = finchar;
+
+ /*
+ * If not first code in a packet, and
+ * if not out of code space, then allocate a new code.
+ *
+ * Keep the hash table correct so it can be used
+ * with uncompressed packets.
+ */
+ if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ u_int32_t fcode;
+ int hval, disp;
+
+ fcode = BSD_KEY(oldcode,finchar);
+ hval = BSD_HASH(oldcode,finchar,db->hshift);
+ dictp = &db->dict[hval];
+
+ /* look for a free hash table entry */
+ if (dictp->codem1 < max_ent) {
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = &db->dict[hval];
+ } while (dictp->codem1 < max_ent);
+ }
+
+ /*
+ * Invalidate previous hash table entry
+ * assigned this code, and then take it over
+ */
+ dictp2 = &db->dict[max_ent+1];
+ if (db->dict[dictp2->cptr].codem1 == max_ent) {
+ db->dict[dictp2->cptr].codem1 = BADCODEM1;
+ }
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->f.fcode = fcode;
+
+ db->max_ent = ++max_ent;
+ db->lens[max_ent] = db->lens[oldcode]+1;
+
+ /* Expand code size if needed. */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
+ db->n_bits = ++n_bits;
+ tgtbitno = 32-n_bits;
+ }
+ }
+ oldcode = incode;
+ }
+ dmsg->b_wptr = wptr;
+
+ /*
+ * Keep the checkpoint right so that incompressible packets
+ * clear the dictionary at the right times.
+ */
+ db->bytes_out += ilen;
+ db->in_count += explen;
+ if (bsd_check(db) && db->debug) {
+ printf("bsd_decomp%d: peer should have cleared dictionary\n",
+ db->unit);
+ }
+
+ ++db->comp_count;
+ db->comp_bytes += ilen + BSD_OVHD;
+ ++db->uncomp_count;
+ db->uncomp_bytes += explen;
+
+ *dmpp = mret;
+ return DECOMP_OK;
+}
+#endif /* DO_BSD_COMPRESS */
diff --git a/mdk-stage1/ppp/modules/deflate.c b/mdk-stage1/ppp/modules/deflate.c
new file mode 100644
index 000000000..1b9054412
--- /dev/null
+++ b/mdk-stage1/ppp/modules/deflate.c
@@ -0,0 +1,760 @@
+/*
+ * ppp_deflate.c - interface the zlib procedures for Deflate compression
+ * and decompression (as used by gzip) to the PPP code.
+ * This version is for use with STREAMS under SunOS 4.x, Solaris 2,
+ * SVR4, OSF/1 and AIX 4.x.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+#ifdef AIX4
+#include <net/net_globals.h>
+#endif
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <net/ppp_defs.h>
+#include "ppp_mod.h"
+
+#define PACKETPTR mblk_t *
+#include <net/ppp-comp.h>
+
+#ifdef __osf__
+#include "zlib.h"
+#else
+#include "../common/zlib.h"
+#endif
+
+#if DO_DEFLATE
+
+#define DEFLATE_DEBUG 1
+
+/*
+ * State for a Deflate (de)compressor.
+ */
+struct deflate_state {
+ int seqno;
+ int w_size;
+ int unit;
+ int hdrlen;
+ int mru;
+ int debug;
+ z_stream strm;
+ struct compstat stats;
+};
+
+#define DEFLATE_OVHD 2 /* Deflate overhead/packet */
+
+static void *z_alloc __P((void *, u_int items, u_int size));
+static void *z_alloc_init __P((void *, u_int items, u_int size));
+static void z_free __P((void *, void *ptr));
+static void *z_comp_alloc __P((u_char *options, int opt_len));
+static void *z_decomp_alloc __P((u_char *options, int opt_len));
+static void z_comp_free __P((void *state));
+static void z_decomp_free __P((void *state));
+static int z_comp_init __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int debug));
+static int z_decomp_init __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+static int z_compress __P((void *state, mblk_t **mret,
+ mblk_t *mp, int slen, int maxolen));
+static void z_incomp __P((void *state, mblk_t *dmsg));
+static int z_decompress __P((void *state, mblk_t *cmp,
+ mblk_t **dmpp));
+static void z_comp_reset __P((void *state));
+static void z_decomp_reset __P((void *state));
+static void z_comp_stats __P((void *state, struct compstat *stats));
+
+/*
+ * Procedures exported to ppp_comp.c.
+ */
+struct compressor ppp_deflate = {
+ CI_DEFLATE, /* compress_proto */
+ z_comp_alloc, /* comp_alloc */
+ z_comp_free, /* comp_free */
+ z_comp_init, /* comp_init */
+ z_comp_reset, /* comp_reset */
+ z_compress, /* compress */
+ z_comp_stats, /* comp_stat */
+ z_decomp_alloc, /* decomp_alloc */
+ z_decomp_free, /* decomp_free */
+ z_decomp_init, /* decomp_init */
+ z_decomp_reset, /* decomp_reset */
+ z_decompress, /* decompress */
+ z_incomp, /* incomp */
+ z_comp_stats, /* decomp_stat */
+};
+
+struct compressor ppp_deflate_draft = {
+ CI_DEFLATE_DRAFT, /* compress_proto */
+ z_comp_alloc, /* comp_alloc */
+ z_comp_free, /* comp_free */
+ z_comp_init, /* comp_init */
+ z_comp_reset, /* comp_reset */
+ z_compress, /* compress */
+ z_comp_stats, /* comp_stat */
+ z_decomp_alloc, /* decomp_alloc */
+ z_decomp_free, /* decomp_free */
+ z_decomp_init, /* decomp_init */
+ z_decomp_reset, /* decomp_reset */
+ z_decompress, /* decompress */
+ z_incomp, /* incomp */
+ z_comp_stats, /* decomp_stat */
+};
+
+#define DECOMP_CHUNK 512
+
+/*
+ * Space allocation and freeing routines for use by zlib routines.
+ */
+struct zchunk {
+ u_int size;
+ u_int guard;
+};
+
+#define GUARD_MAGIC 0x77a6011a
+
+static void *
+z_alloc_init(notused, items, size)
+ void *notused;
+ u_int items, size;
+{
+ struct zchunk *z;
+
+ size = items * size + sizeof(struct zchunk);
+#ifdef __osf__
+ z = (struct zchunk *) ALLOC_SLEEP(size);
+#else
+ z = (struct zchunk *) ALLOC_NOSLEEP(size);
+#endif
+ z->size = size;
+ z->guard = GUARD_MAGIC;
+ return (void *) (z + 1);
+}
+
+static void *
+z_alloc(notused, items, size)
+ void *notused;
+ u_int items, size;
+{
+ struct zchunk *z;
+
+ size = items * size + sizeof(struct zchunk);
+ z = (struct zchunk *) ALLOC_NOSLEEP(size);
+ z->size = size;
+ z->guard = GUARD_MAGIC;
+ return (void *) (z + 1);
+}
+
+static void
+z_free(notused, ptr)
+ void *notused;
+ void *ptr;
+{
+ struct zchunk *z = ((struct zchunk *) ptr) - 1;
+
+ if (z->guard != GUARD_MAGIC) {
+ printf("ppp: z_free of corrupted chunk at %x (%x, %x)\n",
+ z, z->size, z->guard);
+ return;
+ }
+ FREE(z, z->size);
+}
+
+/*
+ * Allocate space for a compressor.
+ */
+static void *
+z_comp_alloc(options, opt_len)
+ u_char *options;
+ int opt_len;
+{
+ struct deflate_state *state;
+ int w_size;
+
+ if (opt_len != CILEN_DEFLATE
+ || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return NULL;
+ w_size = DEFLATE_SIZE(options[2]);
+ /*
+ * N.B. the 9 below should be DEFLATE_MIN_SIZE (8), but using
+ * 8 will cause kernel crashes because of a bug in zlib.
+ */
+ if (w_size < 9 || w_size > DEFLATE_MAX_SIZE)
+ return NULL;
+
+
+#ifdef __osf__
+ state = (struct deflate_state *) ALLOC_SLEEP(sizeof(*state));
+#else
+ state = (struct deflate_state *) ALLOC_NOSLEEP(sizeof(*state));
+#endif
+
+ if (state == NULL)
+ return NULL;
+
+ state->strm.next_in = NULL;
+ state->strm.zalloc = (alloc_func) z_alloc_init;
+ state->strm.zfree = (free_func) z_free;
+ if (deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION, DEFLATE_METHOD_VAL,
+ -w_size, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
+ FREE(state, sizeof(*state));
+ return NULL;
+ }
+
+ state->strm.zalloc = (alloc_func) z_alloc;
+ state->w_size = w_size;
+ bzero(&state->stats, sizeof(state->stats));
+ return (void *) state;
+}
+
+static void
+z_comp_free(arg)
+ void *arg;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ deflateEnd(&state->strm);
+ FREE(state, sizeof(*state));
+}
+
+static int
+z_comp_init(arg, options, opt_len, unit, hdrlen, debug)
+ void *arg;
+ u_char *options;
+ int opt_len, unit, hdrlen, debug;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ if (opt_len < CILEN_DEFLATE
+ || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(options[2]) != state->w_size
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+
+ state->seqno = 0;
+ state->unit = unit;
+ state->hdrlen = hdrlen;
+ state->debug = debug;
+
+ deflateReset(&state->strm);
+
+ return 1;
+}
+
+static void
+z_comp_reset(arg)
+ void *arg;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ state->seqno = 0;
+ deflateReset(&state->strm);
+}
+
+static int
+z_compress(arg, mret, mp, orig_len, maxolen)
+ void *arg;
+ mblk_t **mret; /* compressed packet (out) */
+ mblk_t *mp; /* uncompressed packet (in) */
+ int orig_len, maxolen;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ u_char *rptr, *wptr;
+ int proto, olen, wspace, r, flush;
+ mblk_t *m;
+
+ /*
+ * Check that the protocol is in the range we handle.
+ */
+ *mret = NULL;
+ rptr = mp->b_rptr;
+ if (rptr + PPP_HDRLEN > mp->b_wptr) {
+ if (!pullupmsg(mp, PPP_HDRLEN))
+ return 0;
+ rptr = mp->b_rptr;
+ }
+ proto = PPP_PROTOCOL(rptr);
+ if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+ return orig_len;
+
+ /* Allocate one mblk initially. */
+ if (maxolen > orig_len)
+ maxolen = orig_len;
+ if (maxolen <= PPP_HDRLEN + 2) {
+ wspace = 0;
+ m = NULL;
+ } else {
+ wspace = maxolen + state->hdrlen;
+ if (wspace > 4096)
+ wspace = 4096;
+ m = allocb(wspace, BPRI_MED);
+ }
+ if (m != NULL) {
+ *mret = m;
+ if (state->hdrlen + PPP_HDRLEN + 2 < wspace) {
+ m->b_rptr += state->hdrlen;
+ m->b_wptr = m->b_rptr;
+ wspace -= state->hdrlen;
+ }
+ wptr = m->b_wptr;
+
+ /*
+ * Copy over the PPP header and store the 2-byte sequence number.
+ */
+ wptr[0] = PPP_ADDRESS(rptr);
+ wptr[1] = PPP_CONTROL(rptr);
+ wptr[2] = PPP_COMP >> 8;
+ wptr[3] = PPP_COMP;
+ wptr += PPP_HDRLEN;
+ wptr[0] = state->seqno >> 8;
+ wptr[1] = state->seqno;
+ wptr += 2;
+ state->strm.next_out = wptr;
+ state->strm.avail_out = wspace - (PPP_HDRLEN + 2);
+ } else {
+ state->strm.next_out = NULL;
+ state->strm.avail_out = 1000000;
+ }
+ ++state->seqno;
+
+ rptr += (proto > 0xff)? 2: 3; /* skip 1st proto byte if 0 */
+ state->strm.next_in = rptr;
+ state->strm.avail_in = mp->b_wptr - rptr;
+ mp = mp->b_cont;
+ flush = (mp == NULL)? Z_PACKET_FLUSH: Z_NO_FLUSH;
+ olen = 0;
+ for (;;) {
+ r = deflate(&state->strm, flush);
+ if (r != Z_OK) {
+ printf("z_compress: deflate returned %d (%s)\n",
+ r, (state->strm.msg? state->strm.msg: ""));
+ break;
+ }
+ if (flush != Z_NO_FLUSH && state->strm.avail_out != 0)
+ break; /* all done */
+ if (state->strm.avail_in == 0 && mp != NULL) {
+ state->strm.next_in = mp->b_rptr;
+ state->strm.avail_in = mp->b_wptr - mp->b_rptr;
+ mp = mp->b_cont;
+ if (mp == NULL)
+ flush = Z_PACKET_FLUSH;
+ }
+ if (state->strm.avail_out == 0) {
+ if (m != NULL) {
+ m->b_wptr += wspace;
+ olen += wspace;
+ wspace = maxolen - olen;
+ if (wspace <= 0) {
+ wspace = 0;
+ m->b_cont = NULL;
+ } else {
+ if (wspace < 32)
+ wspace = 32;
+ else if (wspace > 4096)
+ wspace = 4096;
+ m->b_cont = allocb(wspace, BPRI_MED);
+ }
+ m = m->b_cont;
+ if (m != NULL) {
+ state->strm.next_out = m->b_wptr;
+ state->strm.avail_out = wspace;
+ }
+ }
+ if (m == NULL) {
+ state->strm.next_out = NULL;
+ state->strm.avail_out = 1000000;
+ }
+ }
+ }
+ if (m != NULL) {
+ m->b_wptr += wspace - state->strm.avail_out;
+ olen += wspace - state->strm.avail_out;
+ }
+
+ /*
+ * See if we managed to reduce the size of the packet.
+ */
+ if (olen < orig_len && m != NULL) {
+ state->stats.comp_bytes += olen;
+ state->stats.comp_packets++;
+ } else {
+ if (*mret != NULL) {
+ freemsg(*mret);
+ *mret = NULL;
+ }
+ state->stats.inc_bytes += orig_len;
+ state->stats.inc_packets++;
+ olen = orig_len;
+ }
+ state->stats.unc_bytes += orig_len;
+ state->stats.unc_packets++;
+
+ return olen;
+}
+
+static void
+z_comp_stats(arg, stats)
+ void *arg;
+ struct compstat *stats;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ u_int out;
+
+ *stats = state->stats;
+ stats->ratio = stats->unc_bytes;
+ out = stats->comp_bytes + stats->unc_bytes;
+ if (stats->ratio <= 0x7ffffff)
+ stats->ratio <<= 8;
+ else
+ out >>= 8;
+ if (out != 0)
+ stats->ratio /= out;
+}
+
+/*
+ * Allocate space for a decompressor.
+ */
+static void *
+z_decomp_alloc(options, opt_len)
+ u_char *options;
+ int opt_len;
+{
+ struct deflate_state *state;
+ int w_size;
+
+ if (opt_len != CILEN_DEFLATE
+ || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return NULL;
+ w_size = DEFLATE_SIZE(options[2]);
+ /*
+ * N.B. the 9 below should be DEFLATE_MIN_SIZE (8), but using
+ * 8 will cause kernel crashes because of a bug in zlib.
+ */
+ if (w_size < 9 || w_size > DEFLATE_MAX_SIZE)
+ return NULL;
+
+#ifdef __osf__
+ state = (struct deflate_state *) ALLOC_SLEEP(sizeof(*state));
+#else
+ state = (struct deflate_state *) ALLOC_NOSLEEP(sizeof(*state));
+#endif
+ if (state == NULL)
+ return NULL;
+
+ state->strm.next_out = NULL;
+ state->strm.zalloc = (alloc_func) z_alloc_init;
+ state->strm.zfree = (free_func) z_free;
+ if (inflateInit2(&state->strm, -w_size) != Z_OK) {
+ FREE(state, sizeof(*state));
+ return NULL;
+ }
+
+ state->strm.zalloc = (alloc_func) z_alloc;
+ state->w_size = w_size;
+ bzero(&state->stats, sizeof(state->stats));
+ return (void *) state;
+}
+
+static void
+z_decomp_free(arg)
+ void *arg;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ inflateEnd(&state->strm);
+ FREE(state, sizeof(*state));
+}
+
+static int
+z_decomp_init(arg, options, opt_len, unit, hdrlen, mru, debug)
+ void *arg;
+ u_char *options;
+ int opt_len, unit, hdrlen, mru, debug;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ if (opt_len < CILEN_DEFLATE
+ || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(options[2]) != state->w_size
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+
+ state->seqno = 0;
+ state->unit = unit;
+ state->hdrlen = hdrlen;
+ state->debug = debug;
+ state->mru = mru;
+
+ inflateReset(&state->strm);
+
+ return 1;
+}
+
+static void
+z_decomp_reset(arg)
+ void *arg;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ state->seqno = 0;
+ inflateReset(&state->strm);
+}
+
+/*
+ * Decompress a Deflate-compressed packet.
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression. For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+static int
+z_decompress(arg, mi, mop)
+ void *arg;
+ mblk_t *mi, **mop;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ mblk_t *mo, *mo_head;
+ u_char *rptr, *wptr;
+ int rlen, olen, ospace;
+ int seq, i, flush, r, decode_proto;
+ u_char hdr[PPP_HDRLEN + DEFLATE_OVHD];
+
+ *mop = NULL;
+ rptr = mi->b_rptr;
+ for (i = 0; i < PPP_HDRLEN + DEFLATE_OVHD; ++i) {
+ while (rptr >= mi->b_wptr) {
+ mi = mi->b_cont;
+ if (mi == NULL)
+ return DECOMP_ERROR;
+ rptr = mi->b_rptr;
+ }
+ hdr[i] = *rptr++;
+ }
+
+ /* Check the sequence number. */
+ seq = (hdr[PPP_HDRLEN] << 8) + hdr[PPP_HDRLEN+1];
+ if (seq != state->seqno) {
+#if !DEFLATE_DEBUG
+ if (state->debug)
+#endif
+ printf("z_decompress%d: bad seq # %d, expected %d\n",
+ state->unit, seq, state->seqno);
+ return DECOMP_ERROR;
+ }
+ ++state->seqno;
+
+ /* Allocate an output message block. */
+ mo = allocb(DECOMP_CHUNK + state->hdrlen, BPRI_MED);
+ if (mo == NULL)
+ return DECOMP_ERROR;
+ mo_head = mo;
+ mo->b_cont = NULL;
+ mo->b_rptr += state->hdrlen;
+ mo->b_wptr = wptr = mo->b_rptr;
+ ospace = DECOMP_CHUNK;
+ olen = 0;
+
+ /*
+ * Fill in the first part of the PPP header. The protocol field
+ * comes from the decompressed data.
+ */
+ wptr[0] = PPP_ADDRESS(hdr);
+ wptr[1] = PPP_CONTROL(hdr);
+ wptr[2] = 0;
+
+ /*
+ * Set up to call inflate. We set avail_out to 1 initially so we can
+ * look at the first byte of the output and decide whether we have
+ * a 1-byte or 2-byte protocol field.
+ */
+ state->strm.next_in = rptr;
+ state->strm.avail_in = mi->b_wptr - rptr;
+ mi = mi->b_cont;
+ flush = (mi == NULL)? Z_PACKET_FLUSH: Z_NO_FLUSH;
+ rlen = state->strm.avail_in + PPP_HDRLEN + DEFLATE_OVHD;
+ state->strm.next_out = wptr + 3;
+ state->strm.avail_out = 1;
+ decode_proto = 1;
+
+ /*
+ * Call inflate, supplying more input or output as needed.
+ */
+ for (;;) {
+ r = inflate(&state->strm, flush);
+ if (r != Z_OK) {
+#if !DEFLATE_DEBUG
+ if (state->debug)
+#endif
+ printf("z_decompress%d: inflate returned %d (%s)\n",
+ state->unit, r, (state->strm.msg? state->strm.msg: ""));
+ freemsg(mo_head);
+ return DECOMP_FATALERROR;
+ }
+ if (flush != Z_NO_FLUSH && state->strm.avail_out != 0)
+ break; /* all done */
+ if (state->strm.avail_in == 0 && mi != NULL) {
+ state->strm.next_in = mi->b_rptr;
+ state->strm.avail_in = mi->b_wptr - mi->b_rptr;
+ rlen += state->strm.avail_in;
+ mi = mi->b_cont;
+ if (mi == NULL)
+ flush = Z_PACKET_FLUSH;
+ }
+ if (state->strm.avail_out == 0) {
+ if (decode_proto) {
+ state->strm.avail_out = ospace - PPP_HDRLEN;
+ if ((wptr[3] & 1) == 0) {
+ /* 2-byte protocol field */
+ wptr[2] = wptr[3];
+ --state->strm.next_out;
+ ++state->strm.avail_out;
+ }
+ decode_proto = 0;
+ } else {
+ mo->b_wptr += ospace;
+ olen += ospace;
+ mo->b_cont = allocb(DECOMP_CHUNK, BPRI_MED);
+ mo = mo->b_cont;
+ if (mo == NULL) {
+ freemsg(mo_head);
+ return DECOMP_ERROR;
+ }
+ state->strm.next_out = mo->b_rptr;
+ state->strm.avail_out = ospace = DECOMP_CHUNK;
+ }
+ }
+ }
+ if (decode_proto) {
+ freemsg(mo_head);
+ return DECOMP_ERROR;
+ }
+ mo->b_wptr += ospace - state->strm.avail_out;
+ olen += ospace - state->strm.avail_out;
+
+#if DEFLATE_DEBUG
+ if (olen > state->mru + PPP_HDRLEN)
+ printf("ppp_deflate%d: exceeded mru (%d > %d)\n",
+ state->unit, olen, state->mru + PPP_HDRLEN);
+#endif
+
+ state->stats.unc_bytes += olen;
+ state->stats.unc_packets++;
+ state->stats.comp_bytes += rlen;
+ state->stats.comp_packets++;
+
+ *mop = mo_head;
+ return DECOMP_OK;
+}
+
+/*
+ * Incompressible data has arrived - add it to the history.
+ */
+static void
+z_incomp(arg, mi)
+ void *arg;
+ mblk_t *mi;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ u_char *rptr;
+ int rlen, proto, r;
+
+ /*
+ * Check that the protocol is one we handle.
+ */
+ rptr = mi->b_rptr;
+ if (rptr + PPP_HDRLEN > mi->b_wptr) {
+ if (!pullupmsg(mi, PPP_HDRLEN))
+ return;
+ rptr = mi->b_rptr;
+ }
+ proto = PPP_PROTOCOL(rptr);
+ if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+ return;
+
+ ++state->seqno;
+
+ /*
+ * Iterate through the message blocks, adding the characters in them
+ * to the decompressor's history. For the first block, we start
+ * at the either the 1st or 2nd byte of the protocol field,
+ * depending on whether the protocol value is compressible.
+ */
+ rlen = mi->b_wptr - mi->b_rptr;
+ state->strm.next_in = rptr + 3;
+ state->strm.avail_in = rlen - 3;
+ if (proto > 0xff) {
+ --state->strm.next_in;
+ ++state->strm.avail_in;
+ }
+ for (;;) {
+ r = inflateIncomp(&state->strm);
+ if (r != Z_OK) {
+ /* gak! */
+#if !DEFLATE_DEBUG
+ if (state->debug)
+#endif
+ printf("z_incomp%d: inflateIncomp returned %d (%s)\n",
+ state->unit, r, (state->strm.msg? state->strm.msg: ""));
+ return;
+ }
+ mi = mi->b_cont;
+ if (mi == NULL)
+ break;
+ state->strm.next_in = mi->b_rptr;
+ state->strm.avail_in = mi->b_wptr - mi->b_rptr;
+ rlen += state->strm.avail_in;
+ }
+
+ /*
+ * Update stats.
+ */
+ state->stats.inc_bytes += rlen;
+ state->stats.inc_packets++;
+ state->stats.unc_bytes += rlen;
+ state->stats.unc_packets++;
+}
+
+#endif /* DO_DEFLATE */
diff --git a/mdk-stage1/ppp/modules/if_ppp.c b/mdk-stage1/ppp/modules/if_ppp.c
new file mode 100644
index 000000000..14e89eb4a
--- /dev/null
+++ b/mdk-stage1/ppp/modules/if_ppp.c
@@ -0,0 +1,865 @@
+/*
+ * if_ppp.c - a network interface connected to a STREAMS module.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under SunOS 4 and Digital UNIX.
+ *
+ * This file provides the glue between PPP and IP.
+ */
+
+#define INET 1
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#ifdef __osf__
+#include <sys/ioctl.h>
+#include <net/if_types.h>
+#else
+#include <sys/sockio.h>
+#endif
+#include "ppp_mod.h"
+
+#include <sys/stream.h>
+
+#ifdef SNIT_SUPPORT
+#include <sys/time.h>
+#include <net/nit_if.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef __osf__
+#define SIOCSIFMTU SIOCSIPMTU
+#define SIOCGIFMTU SIOCRIPMTU
+#define IFA_ADDR(ifa) (*(ifa)->ifa_addr)
+#else
+#define IFA_ADDR(ifa) ((ifa)->ifa_addr)
+#endif
+
+#define ifr_mtu ifr_metric
+
+static int if_ppp_open __P((queue_t *, int, int, int));
+static int if_ppp_close __P((queue_t *, int));
+static int if_ppp_wput __P((queue_t *, mblk_t *));
+static int if_ppp_rput __P((queue_t *, mblk_t *));
+
+#define PPP_IF_ID 0x8021
+static struct module_info minfo = {
+ PPP_IF_ID, "if_ppp", 0, INFPSZ, 4096, 128
+};
+
+static struct qinit rinit = {
+ if_ppp_rput, NULL, if_ppp_open, if_ppp_close, NULL, &minfo, NULL
+};
+
+static struct qinit winit = {
+ if_ppp_wput, NULL, NULL, NULL, NULL, &minfo, NULL
+};
+
+struct streamtab if_pppinfo = {
+ &rinit, &winit, NULL, NULL
+};
+
+typedef struct if_ppp_state {
+ int unit;
+ queue_t *q;
+ int flags;
+} if_ppp_t;
+
+/* Values for flags */
+#define DBGLOG 1
+
+static int if_ppp_count; /* Number of currently-active streams */
+
+static int ppp_nalloc; /* Number of elements of ifs and states */
+static struct ifnet **ifs; /* Array of pointers to interface structs */
+static if_ppp_t **states; /* Array of pointers to state structs */
+
+static int if_ppp_output __P((struct ifnet *, struct mbuf *,
+ struct sockaddr *));
+static int if_ppp_ioctl __P((struct ifnet *, u_int, caddr_t));
+static struct mbuf *make_mbufs __P((mblk_t *, int));
+static mblk_t *make_message __P((struct mbuf *, int));
+
+#ifdef SNIT_SUPPORT
+/* Fake ether header for SNIT */
+static struct ether_header snit_ehdr = {{0}, {0}, ETHERTYPE_IP};
+#endif
+
+#ifndef __osf__
+static void ppp_if_detach __P((struct ifnet *));
+
+/*
+ * Detach all the interfaces before unloading.
+ * Not sure this works.
+ */
+int
+if_ppp_unload()
+{
+ int i;
+
+ if (if_ppp_count > 0)
+ return EBUSY;
+ for (i = 0; i < ppp_nalloc; ++i)
+ if (ifs[i] != 0)
+ ppp_if_detach(ifs[i]);
+ if (ifs) {
+ FREE(ifs, ppp_nalloc * sizeof (struct ifnet *));
+ FREE(states, ppp_nalloc * sizeof (struct if_ppp_t *));
+ }
+ ppp_nalloc = 0;
+ return 0;
+}
+#endif /* __osf__ */
+
+/*
+ * STREAMS module entry points.
+ */
+static int
+if_ppp_open(q, dev, flag, sflag)
+ queue_t *q;
+ int dev;
+ int flag, sflag;
+{
+ if_ppp_t *sp;
+
+ if (q->q_ptr == 0) {
+ sp = (if_ppp_t *) ALLOC_SLEEP(sizeof (if_ppp_t));
+ if (sp == 0)
+ return OPENFAIL;
+ bzero(sp, sizeof (if_ppp_t));
+ q->q_ptr = (caddr_t) sp;
+ WR(q)->q_ptr = (caddr_t) sp;
+ sp->unit = -1; /* no interface unit attached at present */
+ sp->q = WR(q);
+ sp->flags = 0;
+ ++if_ppp_count;
+ }
+ return 0;
+}
+
+static int
+if_ppp_close(q, flag)
+ queue_t *q;
+ int flag;
+{
+ if_ppp_t *sp;
+ struct ifnet *ifp;
+
+ sp = (if_ppp_t *) q->q_ptr;
+ if (sp != 0) {
+ if (sp->flags & DBGLOG)
+ printf("if_ppp closed, q=%x sp=%x\n", q, sp);
+ if (sp->unit >= 0) {
+ if (sp->unit < ppp_nalloc) {
+ states[sp->unit] = 0;
+ ifp = ifs[sp->unit];
+ if (ifp != 0)
+ ifp->if_flags &= ~(IFF_UP | IFF_RUNNING);
+#ifdef DEBUG
+ } else {
+ printf("if_ppp: unit %d nonexistent!\n", sp->unit);
+#endif
+ }
+ }
+ FREE(sp, sizeof (if_ppp_t));
+ --if_ppp_count;
+ }
+ return 0;
+}
+
+static int
+if_ppp_wput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ if_ppp_t *sp;
+ struct iocblk *iop;
+ int error, unit;
+ struct ifnet *ifp;
+
+ sp = (if_ppp_t *) q->q_ptr;
+ switch (mp->b_datap->db_type) {
+ case M_DATA:
+ /*
+ * Now why would we be getting data coming in here??
+ */
+ if (sp->flags & DBGLOG)
+ printf("if_ppp: got M_DATA len=%d\n", msgdsize(mp));
+ freemsg(mp);
+ break;
+
+ case M_IOCTL:
+ iop = (struct iocblk *) mp->b_rptr;
+ error = EINVAL;
+
+ if (sp->flags & DBGLOG)
+ printf("if_ppp: got ioctl cmd=%x count=%d\n",
+ iop->ioc_cmd, iop->ioc_count);
+
+ switch (iop->ioc_cmd) {
+ case PPPIO_NEWPPA: /* well almost */
+ if (iop->ioc_count != sizeof(int) || sp->unit >= 0)
+ break;
+ if ((error = NOTSUSER()) != 0)
+ break;
+ unit = *(int *)mp->b_cont->b_rptr;
+
+ /* Check that this unit isn't already in use */
+ if (unit < ppp_nalloc && states[unit] != 0) {
+ error = EADDRINUSE;
+ break;
+ }
+
+ /* Extend ifs and states arrays if necessary. */
+ error = ENOSR;
+ if (unit >= ppp_nalloc) {
+ int newn;
+ struct ifnet **newifs;
+ if_ppp_t **newstates;
+
+ newn = unit + 4;
+ if (sp->flags & DBGLOG)
+ printf("if_ppp: extending ifs to %d\n", newn);
+ newifs = (struct ifnet **)
+ ALLOC_NOSLEEP(newn * sizeof (struct ifnet *));
+ if (newifs == 0)
+ break;
+ bzero(newifs, newn * sizeof (struct ifnet *));
+ newstates = (if_ppp_t **)
+ ALLOC_NOSLEEP(newn * sizeof (struct if_ppp_t *));
+ if (newstates == 0) {
+ FREE(newifs, newn * sizeof (struct ifnet *));
+ break;
+ }
+ bzero(newstates, newn * sizeof (struct if_ppp_t *));
+ bcopy(ifs, newifs, ppp_nalloc * sizeof(struct ifnet *));
+ bcopy(states, newstates, ppp_nalloc * sizeof(if_ppp_t *));
+ if (ifs) {
+ FREE(ifs, ppp_nalloc * sizeof(struct ifnet *));
+ FREE(states, ppp_nalloc * sizeof(if_ppp_t *));
+ }
+ ifs = newifs;
+ states = newstates;
+ ppp_nalloc = newn;
+ }
+
+ /* Allocate a new ifnet struct if necessary. */
+ ifp = ifs[unit];
+ if (ifp == 0) {
+ ifp = (struct ifnet *) ALLOC_NOSLEEP(sizeof (struct ifnet));
+ if (ifp == 0)
+ break;
+ bzero(ifp, sizeof (struct ifnet));
+ ifs[unit] = ifp;
+ ifp->if_name = "ppp";
+ ifp->if_unit = unit;
+ ifp->if_mtu = PPP_MTU;
+ ifp->if_flags = IFF_POINTOPOINT | IFF_RUNNING;
+#ifndef __osf__
+#ifdef IFF_MULTICAST
+ ifp->if_flags |= IFF_MULTICAST;
+#endif
+#endif /* __osf__ */
+ ifp->if_output = if_ppp_output;
+#ifdef __osf__
+ ifp->if_version = "Point-to-Point Protocol, version 2.3.11";
+ ifp->if_mediamtu = PPP_MTU;
+ ifp->if_type = IFT_PPP;
+ ifp->if_hdrlen = PPP_HDRLEN;
+ ifp->if_addrlen = 0;
+ ifp->if_flags |= IFF_NOARP | IFF_SIMPLEX | IFF_NOTRAILERS;
+#ifdef IFF_VAR_MTU
+ ifp->if_flags |= IFF_VAR_MTU;
+#endif
+#ifdef NETMASTERCPU
+ ifp->if_affinity = NETMASTERCPU;
+#endif
+#endif
+ ifp->if_ioctl = if_ppp_ioctl;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+ if_attach(ifp);
+ if (sp->flags & DBGLOG)
+ printf("if_ppp: created unit %d\n", unit);
+ } else {
+ ifp->if_mtu = PPP_MTU;
+ ifp->if_flags |= IFF_RUNNING;
+ }
+
+ states[unit] = sp;
+ sp->unit = unit;
+
+ error = 0;
+ iop->ioc_count = 0;
+ if (sp->flags & DBGLOG)
+ printf("if_ppp: attached unit %d, sp=%x q=%x\n", unit,
+ sp, sp->q);
+ break;
+
+ case PPPIO_DEBUG:
+ error = -1;
+ if (iop->ioc_count == sizeof(int)) {
+ if (*(int *)mp->b_cont->b_rptr == PPPDBG_LOG + PPPDBG_IF) {
+ printf("if_ppp: debug log enabled, q=%x sp=%x\n", q, sp);
+ sp->flags |= DBGLOG;
+ error = 0;
+ iop->ioc_count = 0;
+ }
+ }
+ break;
+
+ default:
+ error = -1;
+ break;
+ }
+
+ if (sp->flags & DBGLOG)
+ printf("if_ppp: ioctl result %d\n", error);
+ if (error < 0)
+ putnext(q, mp);
+ else if (error == 0) {
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+ } else {
+ mp->b_datap->db_type = M_IOCNAK;
+ iop->ioc_count = 0;
+ iop->ioc_error = error;
+ qreply(q, mp);
+ }
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+ return 0;
+}
+
+static int
+if_ppp_rput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ if_ppp_t *sp;
+ int proto, s;
+ struct mbuf *mb;
+ struct ifqueue *inq;
+ struct ifnet *ifp;
+ int len;
+
+ sp = (if_ppp_t *) q->q_ptr;
+ switch (mp->b_datap->db_type) {
+ case M_DATA:
+ /*
+ * Convert the message into an mbuf chain
+ * and inject it into the network code.
+ */
+ if (sp->flags & DBGLOG)
+ printf("if_ppp: rput pkt len %d data %x %x %x %x %x %x %x %x\n",
+ msgdsize(mp), mp->b_rptr[0], mp->b_rptr[1], mp->b_rptr[2],
+ mp->b_rptr[3], mp->b_rptr[4], mp->b_rptr[5], mp->b_rptr[6],
+ mp->b_rptr[7]);
+
+ if (sp->unit < 0) {
+ freemsg(mp);
+ break;
+ }
+ if (sp->unit >= ppp_nalloc || (ifp = ifs[sp->unit]) == 0) {
+#ifdef DEBUG
+ printf("if_ppp: no unit %d!\n", sp->unit);
+#endif
+ freemsg(mp);
+ break;
+ }
+
+ if ((ifp->if_flags & IFF_UP) == 0) {
+ freemsg(mp);
+ break;
+ }
+ ++ifp->if_ipackets;
+
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ adjmsg(mp, PPP_HDRLEN);
+ len = msgdsize(mp);
+ mb = make_mbufs(mp, sizeof(struct ifnet *));
+ freemsg(mp);
+ if (mb == NULL) {
+ if (sp->flags & DBGLOG)
+ printf("if_ppp%d: make_mbufs failed\n", ifp->if_unit);
+ ++ifp->if_ierrors;
+ break;
+ }
+
+#ifdef SNIT_SUPPORT
+ if (proto == PPP_IP && (ifp->if_flags & IFF_PROMISC)) {
+ struct nit_if nif;
+
+ nif.nif_header = (caddr_t) &snit_ehdr;
+ nif.nif_hdrlen = sizeof(snit_ehdr);
+ nif.nif_bodylen = len;
+ nif.nif_promisc = 0;
+ snit_intr(ifp, mb, &nif);
+ }
+#endif
+
+/*
+ * For Digital UNIX, there's space set aside in the header mbuf
+ * for the interface info.
+ *
+ * For Sun it's smuggled around via a pointer at the front of the mbuf.
+ */
+#ifdef __osf__
+ mb->m_pkthdr.rcvif = ifp;
+ mb->m_pkthdr.len = len;
+#else
+ mb->m_off -= sizeof(struct ifnet *);
+ mb->m_len += sizeof(struct ifnet *);
+ *mtod(mb, struct ifnet **) = ifp;
+#endif
+
+ inq = 0;
+ switch (proto) {
+ case PPP_IP:
+ inq = &ipintrq;
+ schednetisr(NETISR_IP);
+ }
+
+ if (inq != 0) {
+ s = splhigh();
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ ++ifp->if_ierrors;
+ if (sp->flags & DBGLOG)
+ printf("if_ppp: inq full, proto=%x\n", proto);
+ m_freem(mb);
+ } else {
+ IF_ENQUEUE(inq, mb);
+ }
+ splx(s);
+ } else {
+ if (sp->flags & DBGLOG)
+ printf("if_ppp%d: proto=%x?\n", ifp->if_unit, proto);
+ ++ifp->if_ierrors;
+ m_freem(mb);
+ }
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+ return 0;
+}
+
+/*
+ * Network code wants to output a packet.
+ * Turn it into a STREAMS message and send it down.
+ */
+static int
+if_ppp_output(ifp, m0, dst)
+ struct ifnet *ifp;
+ struct mbuf *m0;
+ struct sockaddr *dst;
+{
+ mblk_t *mp;
+ int proto, s;
+ if_ppp_t *sp;
+ u_char *p;
+
+ if ((ifp->if_flags & IFF_UP) == 0) {
+ m_freem(m0);
+ return ENETDOWN;
+ }
+
+ if ((unsigned)ifp->if_unit >= ppp_nalloc) {
+#ifdef DEBUG
+ printf("if_ppp_output: unit %d?\n", ifp->if_unit);
+#endif
+ m_freem(m0);
+ return EINVAL;
+ }
+ sp = states[ifp->if_unit];
+ if (sp == 0) {
+#ifdef DEBUG
+ printf("if_ppp_output: no queue?\n");
+#endif
+ m_freem(m0);
+ return ENETDOWN;
+ }
+
+ if (sp->flags & DBGLOG) {
+ p = mtod(m0, u_char *);
+ printf("if_ppp_output%d: af=%d data=%x %x %x %x %x %x %x %x q=%x\n",
+ ifp->if_unit, dst->sa_family, p[0], p[1], p[2], p[3], p[4],
+ p[5], p[6], p[7], sp->q);
+ }
+
+ switch (dst->sa_family) {
+ case AF_INET:
+ proto = PPP_IP;
+#ifdef SNIT_SUPPORT
+ if (ifp->if_flags & IFF_PROMISC) {
+ struct nit_if nif;
+ struct mbuf *m;
+ int len;
+
+ for (len = 0, m = m0; m != NULL; m = m->m_next)
+ len += m->m_len;
+ nif.nif_header = (caddr_t) &snit_ehdr;
+ nif.nif_hdrlen = sizeof(snit_ehdr);
+ nif.nif_bodylen = len;
+ nif.nif_promisc = 0;
+ snit_intr(ifp, m0, &nif);
+ }
+#endif
+ break;
+
+ default:
+ m_freem(m0);
+ return EAFNOSUPPORT;
+ }
+
+ ++ifp->if_opackets;
+ mp = make_message(m0, PPP_HDRLEN);
+ m_freem(m0);
+ if (mp == 0) {
+ ++ifp->if_oerrors;
+ return ENOBUFS;
+ }
+ mp->b_rptr -= PPP_HDRLEN;
+ mp->b_rptr[0] = PPP_ALLSTATIONS;
+ mp->b_rptr[1] = PPP_UI;
+ mp->b_rptr[2] = proto >> 8;
+ mp->b_rptr[3] = proto;
+
+ s = splstr();
+ if (sp->flags & DBGLOG)
+ printf("if_ppp: putnext(%x, %x), r=%x w=%x p=%x\n",
+ sp->q, mp, mp->b_rptr, mp->b_wptr, proto);
+ putnext(sp->q, mp);
+ splx(s);
+
+ return 0;
+}
+
+/*
+ * Socket ioctl routine for ppp interfaces.
+ */
+static int
+if_ppp_ioctl(ifp, cmd, data)
+ struct ifnet *ifp;
+ u_int cmd;
+ caddr_t data;
+{
+ int s, error;
+ struct ifreq *ifr = (struct ifreq *) data;
+ struct ifaddr *ifa = (struct ifaddr *) data;
+ u_short mtu;
+
+ error = 0;
+ s = splimp();
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_RUNNING) == 0)
+ ifp->if_flags &= ~IFF_UP;
+ break;
+
+ case SIOCSIFADDR:
+ if (IFA_ADDR(ifa).sa_family != AF_INET)
+ error = EAFNOSUPPORT;
+ break;
+
+ case SIOCSIFDSTADDR:
+ if (IFA_ADDR(ifa).sa_family != AF_INET)
+ error = EAFNOSUPPORT;
+ break;
+
+ case SIOCSIFMTU:
+ if ((error = NOTSUSER()) != 0)
+ break;
+#ifdef __osf__
+ /* this hack is necessary because ifioctl checks ifr_data
+ * in 4.0 and 5.0, but ifr_data and ifr_metric overlay each
+ * other in the definition of struct ifreq so pppd can't set both.
+ */
+ bcopy(ifr->ifr_data, &mtu, sizeof (u_short));
+ ifr->ifr_mtu = mtu;
+#endif
+
+ if (ifr->ifr_mtu < PPP_MINMTU || ifr->ifr_mtu > PPP_MAXMTU) {
+ error = EINVAL;
+ break;
+ }
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = ifp->if_mtu;
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ switch(ifr->ifr_addr.sa_family) {
+ case AF_INET:
+ break;
+ default:
+ error = EAFNOSUPPORT;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+
+/*
+ * Turn a STREAMS message into an mbuf chain.
+ */
+static struct mbuf *
+make_mbufs(mp, off)
+ mblk_t *mp;
+ int off;
+{
+ struct mbuf *head, **prevp, *m;
+ int len, space, n;
+ unsigned char *cp, *dp;
+
+ len = msgdsize(mp);
+ if (len == 0)
+ return 0;
+ prevp = &head;
+ space = 0;
+ cp = mp->b_rptr;
+#ifdef __osf__
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ m->m_len = 0;
+ space = MHLEN;
+ *prevp = m;
+ prevp = &m->m_next;
+ dp = mtod(m, unsigned char *);
+ len -= space;
+ off = 0;
+#endif
+ for (;;) {
+ while (cp >= mp->b_wptr) {
+ mp = mp->b_cont;
+ if (mp == 0) {
+ *prevp = 0;
+ return head;
+ }
+ cp = mp->b_rptr;
+ }
+ n = mp->b_wptr - cp;
+ if (space == 0) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ *prevp = m;
+ if (m == 0) {
+ if (head != 0)
+ m_freem(head);
+ return 0;
+ }
+ if (len + off > 2 * MLEN) {
+#ifdef __osf__
+ MCLGET(m, M_DONTWAIT);
+#else
+ MCLGET(m);
+#endif
+ }
+#ifdef __osf__
+ space = ((m->m_flags & M_EXT) ? MCLBYTES : MLEN);
+#else
+ space = (m->m_off > MMAXOFF? MCLBYTES: MLEN) - off;
+ m->m_off += off;
+#endif
+ m->m_len = 0;
+ len -= space;
+ dp = mtod(m, unsigned char *);
+ off = 0;
+ prevp = &m->m_next;
+ }
+ if (n > space)
+ n = space;
+ bcopy(cp, dp, n);
+ cp += n;
+ dp += n;
+ space -= n;
+ m->m_len += n;
+ }
+}
+
+/*
+ * Turn an mbuf chain into a STREAMS message.
+ */
+#define ALLOCB_MAX 4096
+
+static mblk_t *
+make_message(m, off)
+ struct mbuf *m;
+ int off;
+{
+ mblk_t *head, **prevp, *mp;
+ int len, space, n, nb;
+ unsigned char *cp, *dp;
+ struct mbuf *nm;
+
+ len = 0;
+ for (nm = m; nm != 0; nm = nm->m_next)
+ len += nm->m_len;
+ prevp = &head;
+ space = 0;
+ cp = mtod(m, unsigned char *);
+ nb = m->m_len;
+ for (;;) {
+ while (nb <= 0) {
+ m = m->m_next;
+ if (m == 0) {
+ *prevp = 0;
+ return head;
+ }
+ cp = mtod(m, unsigned char *);
+ nb = m->m_len;
+ }
+ if (space == 0) {
+ space = len + off;
+ if (space > ALLOCB_MAX)
+ space = ALLOCB_MAX;
+ mp = allocb(space, BPRI_LO);
+ *prevp = mp;
+ if (mp == 0) {
+ if (head != 0)
+ freemsg(head);
+ return 0;
+ }
+ dp = mp->b_rptr += off;
+ space -= off;
+ len -= space;
+ off = 0;
+ prevp = &mp->b_cont;
+ }
+ n = nb < space? nb: space;
+ bcopy(cp, dp, n);
+ cp += n;
+ dp += n;
+ nb -= n;
+ space -= n;
+ mp->b_wptr = dp;
+ }
+}
+
+/*
+ * Digital UNIX doesn't allow for removing ifnet structures
+ * from the list. But then we're not using this as a loadable
+ * module anyway, so that's OK.
+ *
+ * Under SunOS, this should allow the module to be unloaded.
+ * Unfortunately, it doesn't seem to detach all the references,
+ * so your system may well crash after you unload this module :-(
+ */
+#ifndef __osf__
+
+/*
+ * Remove an interface from the system.
+ * This routine contains magic.
+ */
+#include <net/route.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+static void
+ppp_if_detach(ifp)
+ struct ifnet *ifp;
+{
+ int s;
+ struct inpcb *pcb;
+ struct ifaddr *ifa;
+ struct in_ifaddr **inap;
+ struct ifnet **ifpp;
+
+ s = splhigh();
+
+ /*
+ * Clear the interface from any routes currently cached in
+ * TCP or UDP protocol control blocks.
+ */
+ for (pcb = tcb.inp_next; pcb != &tcb; pcb = pcb->inp_next)
+ if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
+ in_losing(pcb);
+ for (pcb = udb.inp_next; pcb != &udb; pcb = pcb->inp_next)
+ if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
+ in_losing(pcb);
+
+ /*
+ * Delete routes through all addresses of the interface.
+ */
+ for (ifa = ifp->if_addrlist; ifa != 0; ifa = ifa->ifa_next) {
+ rtinit(ifa, ifa, SIOCDELRT, RTF_HOST);
+ rtinit(ifa, ifa, SIOCDELRT, 0);
+ }
+
+ /*
+ * Unlink the interface's address(es) from the in_ifaddr list.
+ */
+ for (inap = &in_ifaddr; *inap != 0; ) {
+ if ((*inap)->ia_ifa.ifa_ifp == ifp)
+ *inap = (*inap)->ia_next;
+ else
+ inap = &(*inap)->ia_next;
+ }
+
+ /*
+ * Delete the interface from the ifnet list.
+ */
+ for (ifpp = &ifnet; (*ifpp) != 0; ) {
+ if (*ifpp == ifp)
+ break;
+ ifpp = &(*ifpp)->if_next;
+ }
+ if (*ifpp == 0)
+ printf("couldn't find interface ppp%d in ifnet list\n", ifp->if_unit);
+ else
+ *ifpp = ifp->if_next;
+
+ splx(s);
+}
+
+#endif /* __osf__ */
diff --git a/mdk-stage1/ppp/modules/ppp.c b/mdk-stage1/ppp/modules/ppp.c
new file mode 100644
index 000000000..44bf08dff
--- /dev/null
+++ b/mdk-stage1/ppp/modules/ppp.c
@@ -0,0 +1,2486 @@
+/*
+ * ppp.c - STREAMS multiplexing pseudo-device driver for PPP.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/errno.h>
+#ifdef __osf__
+#include <sys/ioctl.h>
+#include <sys/cmn_err.h>
+#define queclass(mp) ((mp)->b_band & QPCTL)
+#else
+#include <sys/ioccom.h>
+#endif
+#include <sys/time.h>
+#ifdef SVR4
+#include <sys/cmn_err.h>
+#include <sys/conf.h>
+#include <sys/dlpi.h>
+#include <sys/ddi.h>
+#ifdef SOL2
+#include <sys/ksynch.h>
+#include <sys/kstat.h>
+#include <sys/sunddi.h>
+#include <sys/ethernet.h>
+#else
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#endif /* SOL2 */
+#else /* not SVR4 */
+#include <sys/user.h>
+#endif /* SVR4 */
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include "ppp_mod.h"
+
+/*
+ * Modifications marked with #ifdef PRIOQ are for priority queueing of
+ * interactive traffic, and are due to Marko Zec <zec@japa.tel.fer.hr>.
+ */
+#ifdef PRIOQ
+#endif /* PRIOQ */
+
+#include <netinet/in.h> /* leave this outside of PRIOQ for htons */
+
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+
+/*
+ * The IP module may use this SAP value for IP packets.
+ */
+#ifndef ETHERTYPE_IP
+#define ETHERTYPE_IP 0x800
+#endif
+
+#if !defined(ETHERTYPE_IPV6)
+#define ETHERTYPE_IPV6 0x86dd
+#endif /* !defined(ETHERTYPE_IPV6) */
+
+#if !defined(ETHERTYPE_ALLSAP) && defined(SOL2)
+#define ETHERTYPE_ALLSAP 0
+#endif /* !defined(ETHERTYPE_ALLSAP) && defined(SOL2) */
+
+#if !defined(PPP_ALLSAP) && defined(SOL2)
+#define PPP_ALLSAP PPP_ALLSTATIONS
+#endif /* !defined(PPP_ALLSAP) && defined(SOL2) */
+
+extern time_t time;
+
+#ifdef SOL2
+/*
+ * We use this reader-writer lock to ensure that the lower streams
+ * stay connected to the upper streams while the lower-side put and
+ * service procedures are running. Essentially it is an existence
+ * lock for the upper stream associated with each lower stream.
+ */
+krwlock_t ppp_lower_lock;
+#define LOCK_LOWER_W rw_enter(&ppp_lower_lock, RW_WRITER)
+#define LOCK_LOWER_R rw_enter(&ppp_lower_lock, RW_READER)
+#define TRYLOCK_LOWER_R rw_tryenter(&ppp_lower_lock, RW_READER)
+#define UNLOCK_LOWER rw_exit(&ppp_lower_lock)
+
+#define MT_ENTER(x) mutex_enter(x)
+#define MT_EXIT(x) mutex_exit(x)
+
+/*
+ * Notes on multithreaded implementation for Solaris 2:
+ *
+ * We use an inner perimeter around each queue pair and an outer
+ * perimeter around the whole driver. The inner perimeter is
+ * entered exclusively for all entry points (open, close, put,
+ * service). The outer perimeter is entered exclusively for open
+ * and close and shared for put and service. This is all done for
+ * us by the streams framework.
+ *
+ * I used to think that the perimeters were entered for the lower
+ * streams' put and service routines as well as for the upper streams'.
+ * Because of problems experienced by people, and after reading the
+ * documentation more closely, I now don't think that is true. So we
+ * now use ppp_lower_lock to give us an existence guarantee on the
+ * upper stream controlling each lower stream.
+ *
+ * Shared entry to the outer perimeter protects the existence of all
+ * the upper streams and their upperstr_t structures, and guarantees
+ * that the following fields of any upperstr_t won't change:
+ * nextmn, next, nextppa. It guarantees that the lowerq field of an
+ * upperstr_t won't go from non-zero to zero, that the global `ppas'
+ * won't change and that the no lower stream will get unlinked.
+ *
+ * Shared (reader) access to ppa_lower_lock guarantees that no lower
+ * stream will be unlinked and that the lowerq field of all upperstr_t
+ * structures won't change.
+ */
+
+#else /* SOL2 */
+#define LOCK_LOWER_W 0
+#define LOCK_LOWER_R 0
+#define TRYLOCK_LOWER_R 1
+#define UNLOCK_LOWER 0
+#define MT_ENTER(x) 0
+#define MT_EXIT(x) 0
+
+#endif /* SOL2 */
+
+/*
+ * Private information; one per upper stream.
+ */
+typedef struct upperstr {
+ minor_t mn; /* minor device number */
+ struct upperstr *nextmn; /* next minor device */
+ queue_t *q; /* read q associated with this upper stream */
+ int flags; /* flag bits, see below */
+ int state; /* current DLPI state */
+ int sap; /* service access point */
+ int req_sap; /* which SAP the DLPI client requested */
+ struct upperstr *ppa; /* control stream for our ppa */
+ struct upperstr *next; /* next stream for this ppa */
+ uint ioc_id; /* last ioctl ID for this stream */
+ enum NPmode npmode; /* what to do with packets on this SAP */
+ unsigned char rblocked; /* flow control has blocked upper read strm */
+ /* N.B. rblocked is only changed by control stream's put/srv procs */
+ /*
+ * There is exactly one control stream for each PPA.
+ * The following fields are only used for control streams.
+ */
+ int ppa_id;
+ queue_t *lowerq; /* write queue attached below this PPA */
+ struct upperstr *nextppa; /* next control stream */
+ int mru;
+ int mtu;
+ struct pppstat stats; /* statistics */
+ time_t last_sent; /* time last NP packet sent */
+ time_t last_recv; /* time last NP packet rcvd */
+#ifdef SOL2
+ kmutex_t stats_lock; /* lock for stats updates */
+ kstat_t *kstats; /* stats for netstat */
+#endif /* SOL2 */
+#ifdef LACHTCP
+ int ifflags;
+ char ifname[IFNAMSIZ];
+ struct ifstats ifstats;
+#endif /* LACHTCP */
+} upperstr_t;
+
+/* Values for flags */
+#define US_PRIV 1 /* stream was opened by superuser */
+#define US_CONTROL 2 /* stream is a control stream */
+#define US_BLOCKED 4 /* flow ctrl has blocked lower write stream */
+#define US_LASTMOD 8 /* no PPP modules below us */
+#define US_DBGLOG 0x10 /* log various occurrences */
+#define US_RBLOCKED 0x20 /* flow ctrl has blocked upper read stream */
+
+#if defined(SOL2)
+#if DL_CURRENT_VERSION >= 2
+#define US_PROMISC 0x40 /* stream is promiscuous */
+#endif /* DL_CURRENT_VERSION >= 2 */
+#define US_RAWDATA 0x80 /* raw M_DATA, no DLPI header */
+#endif /* defined(SOL2) */
+
+#ifdef PRIOQ
+static u_char max_band=0;
+static u_char def_band=0;
+
+#define IPPORT_DEFAULT 65535
+
+/*
+ * Port priority table
+ * Highest priority ports are listed first, lowest are listed last.
+ * ICMP & packets using unlisted ports will be treated as "default".
+ * If IPPORT_DEFAULT is not listed here, "default" packets will be
+ * assigned lowest priority.
+ * Each line should be terminated with "0".
+ * Line containing only "0" marks the end of the list.
+ */
+
+static u_short prioq_table[]= {
+ 113, 53, 0,
+ 22, 23, 513, 517, 518, 0,
+ 514, 21, 79, 111, 0,
+ 25, 109, 110, 0,
+ IPPORT_DEFAULT, 0,
+ 20, 70, 80, 8001, 8008, 8080, 0, /* 8001,8008,8080 - common proxy ports */
+0 };
+
+#endif /* PRIOQ */
+
+
+static upperstr_t *minor_devs = NULL;
+static upperstr_t *ppas = NULL;
+
+#ifdef SVR4
+static int pppopen __P((queue_t *, dev_t *, int, int, cred_t *));
+static int pppclose __P((queue_t *, int, cred_t *));
+#else
+static int pppopen __P((queue_t *, int, int, int));
+static int pppclose __P((queue_t *, int));
+#endif /* SVR4 */
+static int pppurput __P((queue_t *, mblk_t *));
+static int pppuwput __P((queue_t *, mblk_t *));
+static int pppursrv __P((queue_t *));
+static int pppuwsrv __P((queue_t *));
+static int ppplrput __P((queue_t *, mblk_t *));
+static int ppplwput __P((queue_t *, mblk_t *));
+static int ppplrsrv __P((queue_t *));
+static int ppplwsrv __P((queue_t *));
+#ifndef NO_DLPI
+static void dlpi_request __P((queue_t *, mblk_t *, upperstr_t *));
+static void dlpi_error __P((queue_t *, upperstr_t *, int, int, int));
+static void dlpi_ok __P((queue_t *, int));
+#endif
+static int send_data __P((mblk_t *, upperstr_t *));
+static void new_ppa __P((queue_t *, mblk_t *));
+static void attach_ppa __P((queue_t *, mblk_t *));
+static void detach_ppa __P((queue_t *, mblk_t *));
+static void detach_lower __P((queue_t *, mblk_t *));
+static void debug_dump __P((queue_t *, mblk_t *));
+static upperstr_t *find_dest __P((upperstr_t *, int));
+#if defined(SOL2)
+static upperstr_t *find_promisc __P((upperstr_t *, int));
+static mblk_t *prepend_ether __P((upperstr_t *, mblk_t *, int));
+static mblk_t *prepend_udind __P((upperstr_t *, mblk_t *, int));
+static void promisc_sendup __P((upperstr_t *, mblk_t *, int, int));
+#endif /* defined(SOL2) */
+static int putctl2 __P((queue_t *, int, int, int));
+static int putctl4 __P((queue_t *, int, int, int));
+static int pass_packet __P((upperstr_t *ppa, mblk_t *mp, int outbound));
+#ifdef FILTER_PACKETS
+static int ip_hard_filter __P((upperstr_t *ppa, mblk_t *mp, int outbound));
+#endif /* FILTER_PACKETS */
+
+#define PPP_ID 0xb1a6
+static struct module_info ppp_info = {
+#ifdef PRIOQ
+ PPP_ID, "ppp", 0, 512, 512, 384
+#else
+ PPP_ID, "ppp", 0, 512, 512, 128
+#endif /* PRIOQ */
+};
+
+static struct qinit pppurint = {
+ pppurput, pppursrv, pppopen, pppclose, NULL, &ppp_info, NULL
+};
+
+static struct qinit pppuwint = {
+ pppuwput, pppuwsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+static struct qinit ppplrint = {
+ ppplrput, ppplrsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+static struct qinit ppplwint = {
+ ppplwput, ppplwsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+#ifdef LACHTCP
+extern struct ifstats *ifstats;
+int pppdevflag = 0;
+#endif
+
+struct streamtab pppinfo = {
+ &pppurint, &pppuwint,
+ &ppplrint, &ppplwint
+};
+
+int ppp_count;
+
+/*
+ * How we maintain statistics.
+ */
+#ifdef SOL2
+#define INCR_IPACKETS(ppa) \
+ if (ppa->kstats != 0) { \
+ KSTAT_NAMED_PTR(ppa->kstats)[0].value.ul++; \
+ }
+#define INCR_IERRORS(ppa) \
+ if (ppa->kstats != 0) { \
+ KSTAT_NAMED_PTR(ppa->kstats)[1].value.ul++; \
+ }
+#define INCR_OPACKETS(ppa) \
+ if (ppa->kstats != 0) { \
+ KSTAT_NAMED_PTR(ppa->kstats)[2].value.ul++; \
+ }
+#define INCR_OERRORS(ppa) \
+ if (ppa->kstats != 0) { \
+ KSTAT_NAMED_PTR(ppa->kstats)[3].value.ul++; \
+ }
+#endif
+
+#ifdef LACHTCP
+#define INCR_IPACKETS(ppa) ppa->ifstats.ifs_ipackets++;
+#define INCR_IERRORS(ppa) ppa->ifstats.ifs_ierrors++;
+#define INCR_OPACKETS(ppa) ppa->ifstats.ifs_opackets++;
+#define INCR_OERRORS(ppa) ppa->ifstats.ifs_oerrors++;
+#endif
+
+/*
+ * STREAMS driver entry points.
+ */
+static int
+#ifdef SVR4
+pppopen(q, devp, oflag, sflag, credp)
+ queue_t *q;
+ dev_t *devp;
+ int oflag, sflag;
+ cred_t *credp;
+#else
+pppopen(q, dev, oflag, sflag)
+ queue_t *q;
+ int dev; /* really dev_t */
+ int oflag, sflag;
+#endif
+{
+ upperstr_t *up;
+ upperstr_t **prevp;
+ minor_t mn;
+#ifdef PRIOQ
+ u_short *ptr;
+ u_char new_band;
+#endif /* PRIOQ */
+
+ if (q->q_ptr)
+ DRV_OPEN_OK(dev); /* device is already open */
+
+#ifdef PRIOQ
+ /* Calculate max_bband & def_band from definitions in prioq.h
+ This colud be done at some more approtiate time (less often)
+ but this way it works well so I'll just leave it here */
+
+ max_band = 1;
+ def_band = 0;
+ ptr = prioq_table;
+ while (*ptr) {
+ new_band = 1;
+ while (*ptr)
+ if (*ptr++ == IPPORT_DEFAULT) {
+ new_band = 0;
+ def_band = max_band;
+ }
+ max_band += new_band;
+ ptr++;
+ }
+ if (def_band)
+ def_band = max_band - def_band;
+ --max_band;
+#endif /* PRIOQ */
+
+ if (sflag == CLONEOPEN) {
+ mn = 0;
+ for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) {
+ if (up->mn != mn)
+ break;
+ ++mn;
+ }
+ } else {
+#ifdef SVR4
+ mn = getminor(*devp);
+#else
+ mn = minor(dev);
+#endif
+ for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) {
+ if (up->mn >= mn)
+ break;
+ }
+ if (up->mn == mn) {
+ /* this can't happen */
+ q->q_ptr = WR(q)->q_ptr = (caddr_t) up;
+ DRV_OPEN_OK(dev);
+ }
+ }
+
+ /*
+ * Construct a new minor node.
+ */
+ up = (upperstr_t *) ALLOC_SLEEP(sizeof(upperstr_t));
+ bzero((caddr_t) up, sizeof(upperstr_t));
+ if (up == 0) {
+ DPRINT("pppopen: out of kernel memory\n");
+ OPEN_ERROR(ENXIO);
+ }
+ up->nextmn = *prevp;
+ *prevp = up;
+ up->mn = mn;
+#ifdef SVR4
+ *devp = makedevice(getmajor(*devp), mn);
+#endif
+ up->q = q;
+ if (NOTSUSER() == 0)
+ up->flags |= US_PRIV;
+#ifndef NO_DLPI
+ up->state = DL_UNATTACHED;
+#endif
+#ifdef LACHTCP
+ up->ifflags = IFF_UP | IFF_POINTOPOINT;
+#endif
+ up->sap = -1;
+ up->last_sent = up->last_recv = time;
+ up->npmode = NPMODE_DROP;
+ q->q_ptr = (caddr_t) up;
+ WR(q)->q_ptr = (caddr_t) up;
+ noenable(WR(q));
+#ifdef SOL2
+ mutex_init(&up->stats_lock, NULL, MUTEX_DRIVER, NULL);
+#endif
+ ++ppp_count;
+
+ qprocson(q);
+ DRV_OPEN_OK(makedev(major(dev), mn));
+}
+
+static int
+#ifdef SVR4
+pppclose(q, flag, credp)
+ queue_t *q;
+ int flag;
+ cred_t *credp;
+#else
+pppclose(q, flag)
+ queue_t *q;
+ int flag;
+#endif
+{
+ upperstr_t *up, **upp;
+ upperstr_t *as, *asnext;
+ upperstr_t **prevp;
+
+ qprocsoff(q);
+
+ up = (upperstr_t *) q->q_ptr;
+ if (up == 0) {
+ DPRINT("pppclose: q_ptr = 0\n");
+ return 0;
+ }
+ if (up->flags & US_DBGLOG)
+ DPRINT2("ppp/%d: close, flags=%x\n", up->mn, up->flags);
+ if (up->flags & US_CONTROL) {
+#ifdef LACHTCP
+ struct ifstats *ifp, *pifp;
+#endif
+ if (up->lowerq != 0) {
+ /* Gack! the lower stream should have be unlinked earlier! */
+ DPRINT1("ppp%d: lower stream still connected on close?\n",
+ up->mn);
+ LOCK_LOWER_W;
+ up->lowerq->q_ptr = 0;
+ RD(up->lowerq)->q_ptr = 0;
+ up->lowerq = 0;
+ UNLOCK_LOWER;
+ }
+
+ /*
+ * This stream represents a PPA:
+ * For all streams attached to the PPA, clear their
+ * references to this PPA.
+ * Then remove this PPA from the list of PPAs.
+ */
+ for (as = up->next; as != 0; as = asnext) {
+ asnext = as->next;
+ as->next = 0;
+ as->ppa = 0;
+ if (as->flags & US_BLOCKED) {
+ as->flags &= ~US_BLOCKED;
+ flushq(WR(as->q), FLUSHDATA);
+ }
+ }
+ for (upp = &ppas; *upp != 0; upp = &(*upp)->nextppa)
+ if (*upp == up) {
+ *upp = up->nextppa;
+ break;
+ }
+#ifdef LACHTCP
+ /* Remove the statistics from the active list. */
+ for (ifp = ifstats, pifp = 0; ifp; ifp = ifp->ifs_next) {
+ if (ifp == &up->ifstats) {
+ if (pifp)
+ pifp->ifs_next = ifp->ifs_next;
+ else
+ ifstats = ifp->ifs_next;
+ break;
+ }
+ pifp = ifp;
+ }
+#endif
+ } else {
+ /*
+ * If this stream is attached to a PPA,
+ * remove it from the PPA's list.
+ */
+ if ((as = up->ppa) != 0) {
+ for (; as->next != 0; as = as->next)
+ if (as->next == up) {
+ as->next = up->next;
+ break;
+ }
+ }
+ }
+
+#ifdef SOL2
+ if (up->kstats)
+ kstat_delete(up->kstats);
+ mutex_destroy(&up->stats_lock);
+#endif
+
+ q->q_ptr = NULL;
+ WR(q)->q_ptr = NULL;
+
+ for (prevp = &minor_devs; *prevp != 0; prevp = &(*prevp)->nextmn) {
+ if (*prevp == up) {
+ *prevp = up->nextmn;
+ break;
+ }
+ }
+ FREE(up, sizeof(upperstr_t));
+ --ppp_count;
+
+ return 0;
+}
+
+/*
+ * A message from on high. We do one of three things:
+ * - qreply()
+ * - put the message on the lower write stream
+ * - queue it for our service routine
+ */
+static int
+pppuwput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us, *ppa, *nps;
+ struct iocblk *iop;
+ struct linkblk *lb;
+#ifdef LACHTCP
+ struct ifreq *ifr;
+ int i;
+#endif
+ queue_t *lq;
+ int error, n, sap;
+ mblk_t *mq;
+ struct ppp_idle *pip;
+#ifdef PRIOQ
+ queue_t *tlq;
+#endif /* PRIOQ */
+#ifdef NO_DLPI
+ upperstr_t *os;
+#endif
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("pppuwput: q_ptr = 0!\n");
+ return 0;
+ }
+ if (mp == 0) {
+ DPRINT1("pppuwput/%d: mp = 0!\n", us->mn);
+ return 0;
+ }
+ if (mp->b_datap == 0) {
+ DPRINT1("pppuwput/%d: mp->b_datap = 0!\n", us->mn);
+ return 0;
+ }
+ switch (mp->b_datap->db_type) {
+#ifndef NO_DLPI
+ case M_PCPROTO:
+ case M_PROTO:
+ dlpi_request(q, mp, us);
+ break;
+#endif /* NO_DLPI */
+
+ case M_DATA:
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: uwput M_DATA len=%d flags=%x\n",
+ us->mn, msgdsize(mp), us->flags);
+ if (us->ppa == 0 || msgdsize(mp) > us->ppa->mtu + PPP_HDRLEN
+#ifndef NO_DLPI
+ || (us->flags & US_CONTROL) == 0
+#endif /* NO_DLPI */
+ ) {
+ DPRINT1("pppuwput: junk data len=%d\n", msgdsize(mp));
+ freemsg(mp);
+ break;
+ }
+#ifdef NO_DLPI
+ if ((us->flags & US_CONTROL) == 0 && !pass_packet(us, mp, 1))
+ break;
+#endif
+ if (!send_data(mp, us))
+ putq(q, mp);
+ break;
+
+ case M_IOCTL:
+ iop = (struct iocblk *) mp->b_rptr;
+ error = EINVAL;
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: ioctl %x count=%d\n",
+ us->mn, iop->ioc_cmd, iop->ioc_count);
+ switch (iop->ioc_cmd) {
+#if defined(SOL2)
+ case DLIOCRAW: /* raw M_DATA mode */
+ us->flags |= US_RAWDATA;
+ error = 0;
+ break;
+#endif /* defined(SOL2) */
+ case I_LINK:
+ if ((us->flags & US_CONTROL) == 0 || us->lowerq != 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl I_LINK b_cont = 0!\n", us->mn);
+ break;
+ }
+ lb = (struct linkblk *) mp->b_cont->b_rptr;
+ lq = lb->l_qbot;
+ if (lq == 0) {
+ DPRINT1("pppuwput/%d: ioctl I_LINK l_qbot = 0!\n", us->mn);
+ break;
+ }
+ LOCK_LOWER_W;
+ us->lowerq = lq;
+ lq->q_ptr = (caddr_t) q;
+ RD(lq)->q_ptr = (caddr_t) us->q;
+ UNLOCK_LOWER;
+ iop->ioc_count = 0;
+ error = 0;
+ us->flags &= ~US_LASTMOD;
+ /* Unblock upper streams which now feed this lower stream. */
+ qenable(q);
+ /* Send useful information down to the modules which
+ are now linked below us. */
+ putctl2(lq, M_CTL, PPPCTL_UNIT, us->ppa_id);
+ putctl4(lq, M_CTL, PPPCTL_MRU, us->mru);
+ putctl4(lq, M_CTL, PPPCTL_MTU, us->mtu);
+#ifdef PRIOQ
+ /* Lower tty driver's queue hiwat/lowat from default 4096/128
+ to 256/128 since we don't want queueing of data on
+ output to physical device */
+
+ freezestr(lq);
+ for (tlq = lq; tlq->q_next != NULL; tlq = tlq->q_next)
+ ;
+ strqset(tlq, QHIWAT, 0, 256);
+ strqset(tlq, QLOWAT, 0, 128);
+ unfreezestr(lq);
+#endif /* PRIOQ */
+ break;
+
+ case I_UNLINK:
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl I_UNLINK b_cont = 0!\n", us->mn);
+ break;
+ }
+ lb = (struct linkblk *) mp->b_cont->b_rptr;
+#if DEBUG
+ if (us->lowerq != lb->l_qbot) {
+ DPRINT2("ppp unlink: lowerq=%x qbot=%x\n",
+ us->lowerq, lb->l_qbot);
+ break;
+ }
+#endif
+ iop->ioc_count = 0;
+ qwriter(q, mp, detach_lower, PERIM_OUTER);
+ error = -1;
+ break;
+
+ case PPPIO_NEWPPA:
+ if (us->flags & US_CONTROL)
+ break;
+ if ((us->flags & US_PRIV) == 0) {
+ error = EPERM;
+ break;
+ }
+ /* Arrange to return an int */
+ if ((mq = mp->b_cont) == 0
+ || mq->b_datap->db_lim - mq->b_rptr < sizeof(int)) {
+ mq = allocb(sizeof(int), BPRI_HI);
+ if (mq == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = mq;
+ mq->b_cont = 0;
+ }
+ iop->ioc_count = sizeof(int);
+ mq->b_wptr = mq->b_rptr + sizeof(int);
+ qwriter(q, mp, new_ppa, PERIM_OUTER);
+ error = -1;
+ break;
+
+ case PPPIO_ATTACH:
+ /* like dlpi_attach, for programs which can't write to
+ the stream (like pppstats) */
+ if (iop->ioc_count != sizeof(int) || us->ppa != 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_ATTACH b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
+ if (ppa->ppa_id == n)
+ break;
+ if (ppa == 0)
+ break;
+ us->ppa = ppa;
+ iop->ioc_count = 0;
+ qwriter(q, mp, attach_ppa, PERIM_OUTER);
+ error = -1;
+ break;
+
+#ifdef NO_DLPI
+ case PPPIO_BIND:
+ /* Attach to a given SAP. */
+ if (iop->ioc_count != sizeof(int) || us->ppa == 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_BIND b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ /* n must be a valid PPP network protocol number. */
+ if (n < 0x21 || n > 0x3fff || (n & 0x101) != 1)
+ break;
+ /* check that no other stream is bound to this sap already. */
+ for (os = us->ppa; os != 0; os = os->next)
+ if (os->sap == n)
+ break;
+ if (os != 0)
+ break;
+ us->sap = n;
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+#endif /* NO_DLPI */
+
+ case PPPIO_MRU:
+ if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_MRU b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ if (n <= 0 || n > PPP_MAXMRU)
+ break;
+ if (n < PPP_MRU)
+ n = PPP_MRU;
+ us->mru = n;
+ if (us->lowerq)
+ putctl4(us->lowerq, M_CTL, PPPCTL_MRU, n);
+ error = 0;
+ iop->ioc_count = 0;
+ break;
+
+ case PPPIO_MTU:
+ if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_MTU b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ if (n <= 0 || n > PPP_MAXMTU)
+ break;
+ us->mtu = n;
+#ifdef LACHTCP
+ /* The MTU reported in netstat, not used as IP max packet size! */
+ us->ifstats.ifs_mtu = n;
+#endif
+ if (us->lowerq)
+ putctl4(us->lowerq, M_CTL, PPPCTL_MTU, n);
+ error = 0;
+ iop->ioc_count = 0;
+ break;
+
+ case PPPIO_LASTMOD:
+ us->flags |= US_LASTMOD;
+ error = 0;
+ break;
+
+ case PPPIO_DEBUG:
+ if (iop->ioc_count != sizeof(int))
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_DEBUG b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ if (n == PPPDBG_DUMP + PPPDBG_DRIVER) {
+ qwriter(q, NULL, debug_dump, PERIM_OUTER);
+ iop->ioc_count = 0;
+ error = -1;
+ } else if (n == PPPDBG_LOG + PPPDBG_DRIVER) {
+ DPRINT1("ppp/%d: debug log enabled\n", us->mn);
+ us->flags |= US_DBGLOG;
+ iop->ioc_count = 0;
+ error = 0;
+ } else {
+ if (us->ppa == 0 || us->ppa->lowerq == 0)
+ break;
+ putnext(us->ppa->lowerq, mp);
+ error = -1;
+ }
+ break;
+
+ case PPPIO_NPMODE:
+ if (iop->ioc_count != 2 * sizeof(int))
+ break;
+ if ((us->flags & US_CONTROL) == 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_NPMODE b_cont = 0!\n", us->mn);
+ break;
+ }
+ sap = ((int *)mp->b_cont->b_rptr)[0];
+ for (nps = us->next; nps != 0; nps = nps->next) {
+ if (us->flags & US_DBGLOG)
+ DPRINT2("us = 0x%x, us->next->sap = 0x%x\n", nps, nps->sap);
+ if (nps->sap == sap)
+ break;
+ }
+ if (nps == 0) {
+ if (us->flags & US_DBGLOG)
+ DPRINT2("ppp/%d: no stream for sap %x\n", us->mn, sap);
+ break;
+ }
+ /* XXX possibly should use qwriter here */
+ nps->npmode = (enum NPmode) ((int *)mp->b_cont->b_rptr)[1];
+ if (nps->npmode != NPMODE_QUEUE && (nps->flags & US_BLOCKED) != 0)
+ qenable(WR(nps->q));
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+
+ case PPPIO_GIDLE:
+ if ((ppa = us->ppa) == 0)
+ break;
+ mq = allocb(sizeof(struct ppp_idle), BPRI_HI);
+ if (mq == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = mq;
+ mq->b_cont = 0;
+ pip = (struct ppp_idle *) mq->b_wptr;
+ pip->xmit_idle = time - ppa->last_sent;
+ pip->recv_idle = time - ppa->last_recv;
+ mq->b_wptr += sizeof(struct ppp_idle);
+ iop->ioc_count = sizeof(struct ppp_idle);
+ error = 0;
+ break;
+
+#ifdef LACHTCP
+ case SIOCSIFNAME:
+ /* Sent from IP down to us. Attach the ifstats structure. */
+ if (iop->ioc_count != sizeof(struct ifreq) || us->ppa == 0)
+ break;
+ ifr = (struct ifreq *)mp->b_cont->b_rptr;
+ /* Find the unit number in the interface name. */
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (ifr->ifr_name[i] == 0 ||
+ (ifr->ifr_name[i] >= '0' &&
+ ifr->ifr_name[i] <= '9'))
+ break;
+ else
+ us->ifname[i] = ifr->ifr_name[i];
+ }
+ us->ifname[i] = 0;
+
+ /* Convert the unit number to binary. */
+ for (n = 0; i < IFNAMSIZ; i++) {
+ if (ifr->ifr_name[i] == 0) {
+ break;
+ }
+ else {
+ n = n * 10 + ifr->ifr_name[i] - '0';
+ }
+ }
+
+ /* Verify the ppa. */
+ if (us->ppa->ppa_id != n)
+ break;
+ ppa = us->ppa;
+
+ /* Set up the netstat block. */
+ strncpy (ppa->ifname, us->ifname, IFNAMSIZ);
+
+ ppa->ifstats.ifs_name = ppa->ifname;
+ ppa->ifstats.ifs_unit = n;
+ ppa->ifstats.ifs_active = us->state != DL_UNBOUND;
+ ppa->ifstats.ifs_mtu = ppa->mtu;
+
+ /* Link in statistics used by netstat. */
+ ppa->ifstats.ifs_next = ifstats;
+ ifstats = &ppa->ifstats;
+
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+
+ case SIOCGIFFLAGS:
+ if (!(us->flags & US_CONTROL)) {
+ if (us->ppa)
+ us = us->ppa;
+ else
+ break;
+ }
+ ((struct iocblk_in *)iop)->ioc_ifflags = us->ifflags;
+ error = 0;
+ break;
+
+ case SIOCSIFFLAGS:
+ if (!(us->flags & US_CONTROL)) {
+ if (us->ppa)
+ us = us->ppa;
+ else
+ break;
+ }
+ us->ifflags = ((struct iocblk_in *)iop)->ioc_ifflags;
+ error = 0;
+ break;
+
+ case SIOCSIFADDR:
+ if (!(us->flags & US_CONTROL)) {
+ if (us->ppa)
+ us = us->ppa;
+ else
+ break;
+ }
+ us->ifflags |= IFF_RUNNING;
+ ((struct iocblk_in *)iop)->ioc_ifflags |= IFF_RUNNING;
+ error = 0;
+ break;
+
+ case SIOCSIFMTU:
+ /*
+ * Vanilla SVR4 systems don't handle SIOCSIFMTU, rather
+ * they take the MTU from the DL_INFO_ACK we sent in response
+ * to their DL_INFO_REQ. Fortunately, they will update the
+ * MTU if we send an unsolicited DL_INFO_ACK up.
+ */
+ if ((mq = allocb(sizeof(dl_info_req_t), BPRI_HI)) == 0)
+ break; /* should do bufcall */
+ ((union DL_primitives *)mq->b_rptr)->dl_primitive = DL_INFO_REQ;
+ mq->b_wptr = mq->b_rptr + sizeof(dl_info_req_t);
+ dlpi_request(q, mq, us);
+ error = 0;
+ break;
+
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFMETRIC:
+ error = 0;
+ break;
+#endif /* LACHTCP */
+
+ default:
+ if (us->ppa == 0 || us->ppa->lowerq == 0)
+ break;
+ us->ioc_id = iop->ioc_id;
+ error = -1;
+ switch (iop->ioc_cmd) {
+ case PPPIO_GETSTAT:
+ case PPPIO_GETCSTAT:
+ if (us->flags & US_LASTMOD) {
+ error = EINVAL;
+ break;
+ }
+ putnext(us->ppa->lowerq, mp);
+ break;
+ default:
+ if (us->flags & US_PRIV)
+ putnext(us->ppa->lowerq, mp);
+ else {
+ DPRINT1("ppp ioctl %x rejected\n", iop->ioc_cmd);
+ error = EPERM;
+ }
+ break;
+ }
+ break;
+ }
+
+ if (error > 0) {
+ iop->ioc_error = error;
+ mp->b_datap->db_type = M_IOCNAK;
+ qreply(q, mp);
+ } else if (error == 0) {
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+ }
+ break;
+
+ case M_FLUSH:
+ if (us->flags & US_DBGLOG)
+ DPRINT2("ppp/%d: flush %x\n", us->mn, *mp->b_rptr);
+ if (*mp->b_rptr & FLUSHW)
+ flushq(q, FLUSHDATA);
+ if (*mp->b_rptr & FLUSHR) {
+ *mp->b_rptr &= ~FLUSHW;
+ qreply(q, mp);
+ } else
+ freemsg(mp);
+ break;
+
+ default:
+ freemsg(mp);
+ break;
+ }
+ return 0;
+}
+
+#ifndef NO_DLPI
+static void
+dlpi_request(q, mp, us)
+ queue_t *q;
+ mblk_t *mp;
+ upperstr_t *us;
+{
+ union DL_primitives *d = (union DL_primitives *) mp->b_rptr;
+ int size = mp->b_wptr - mp->b_rptr;
+ mblk_t *reply, *np;
+ upperstr_t *ppa, *os;
+ int sap, len;
+ dl_info_ack_t *info;
+ dl_bind_ack_t *ackp;
+#if DL_CURRENT_VERSION >= 2
+ dl_phys_addr_ack_t *paddrack;
+ static struct ether_addr eaddr = {0};
+#endif
+
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: dlpi prim %x len=%d\n", us->mn,
+ d->dl_primitive, size);
+ switch (d->dl_primitive) {
+ case DL_INFO_REQ:
+ if (size < sizeof(dl_info_req_t))
+ goto badprim;
+ if ((reply = allocb(sizeof(dl_info_ack_t), BPRI_HI)) == 0)
+ break; /* should do bufcall */
+ reply->b_datap->db_type = M_PCPROTO;
+ info = (dl_info_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_info_ack_t);
+ bzero((caddr_t) info, sizeof(dl_info_ack_t));
+ info->dl_primitive = DL_INFO_ACK;
+ info->dl_max_sdu = us->ppa? us->ppa->mtu: PPP_MAXMTU;
+ info->dl_min_sdu = 1;
+ info->dl_addr_length = sizeof(uint);
+ info->dl_mac_type = DL_ETHER; /* a bigger lie */
+ info->dl_current_state = us->state;
+ info->dl_service_mode = DL_CLDLS;
+ info->dl_provider_style = DL_STYLE2;
+#if DL_CURRENT_VERSION >= 2
+ info->dl_sap_length = sizeof(uint);
+ info->dl_version = DL_CURRENT_VERSION;
+#endif
+ qreply(q, reply);
+ break;
+
+ case DL_ATTACH_REQ:
+ if (size < sizeof(dl_attach_req_t))
+ goto badprim;
+ if (us->state != DL_UNATTACHED || us->ppa != 0) {
+ dlpi_error(q, us, DL_ATTACH_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+ for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
+ if (ppa->ppa_id == d->attach_req.dl_ppa)
+ break;
+ if (ppa == 0) {
+ dlpi_error(q, us, DL_ATTACH_REQ, DL_BADPPA, 0);
+ break;
+ }
+ us->ppa = ppa;
+ qwriter(q, mp, attach_ppa, PERIM_OUTER);
+ return;
+
+ case DL_DETACH_REQ:
+ if (size < sizeof(dl_detach_req_t))
+ goto badprim;
+ if (us->state != DL_UNBOUND || us->ppa == 0) {
+ dlpi_error(q, us, DL_DETACH_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+ qwriter(q, mp, detach_ppa, PERIM_OUTER);
+ return;
+
+ case DL_BIND_REQ:
+ if (size < sizeof(dl_bind_req_t))
+ goto badprim;
+ if (us->state != DL_UNBOUND || us->ppa == 0) {
+ dlpi_error(q, us, DL_BIND_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+#if 0
+ /* apparently this test fails (unnecessarily?) on some systems */
+ if (d->bind_req.dl_service_mode != DL_CLDLS) {
+ dlpi_error(q, us, DL_BIND_REQ, DL_UNSUPPORTED, 0);
+ break;
+ }
+#endif
+
+ /* saps must be valid PPP network protocol numbers,
+ except that we accept ETHERTYPE_IP in place of PPP_IP. */
+ sap = d->bind_req.dl_sap;
+ us->req_sap = sap;
+
+#if defined(SOL2)
+ if (us->flags & US_DBGLOG)
+ DPRINT2("DL_BIND_REQ: ip gives sap = 0x%x, us = 0x%x", sap, us);
+
+ if (sap == ETHERTYPE_IP) /* normal IFF_IPV4 */
+ sap = PPP_IP;
+ else if (sap == ETHERTYPE_IPV6) /* when IFF_IPV6 is set */
+ sap = PPP_IPV6;
+ else if (sap == ETHERTYPE_ALLSAP) /* snoop gives sap of 0 */
+ sap = PPP_ALLSAP;
+ else {
+ DPRINT2("DL_BIND_REQ: unrecognized sap = 0x%x, us = 0x%x", sap, us);
+ dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0);
+ break;
+ }
+#else
+ if (sap == ETHERTYPE_IP)
+ sap = PPP_IP;
+ if (sap < 0x21 || sap > 0x3fff || (sap & 0x101) != 1) {
+ dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0);
+ break;
+ }
+#endif /* defined(SOL2) */
+
+ /* check that no other stream is bound to this sap already. */
+ for (os = us->ppa; os != 0; os = os->next)
+ if (os->sap == sap)
+ break;
+ if (os != 0) {
+ dlpi_error(q, us, DL_BIND_REQ, DL_NOADDR, 0);
+ break;
+ }
+
+ us->sap = sap;
+ us->state = DL_IDLE;
+
+ if ((reply = allocb(sizeof(dl_bind_ack_t) + sizeof(uint),
+ BPRI_HI)) == 0)
+ break; /* should do bufcall */
+ ackp = (dl_bind_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_bind_ack_t) + sizeof(uint);
+ reply->b_datap->db_type = M_PCPROTO;
+ bzero((caddr_t) ackp, sizeof(dl_bind_ack_t));
+ ackp->dl_primitive = DL_BIND_ACK;
+ ackp->dl_sap = sap;
+ ackp->dl_addr_length = sizeof(uint);
+ ackp->dl_addr_offset = sizeof(dl_bind_ack_t);
+ *(uint *)(ackp+1) = sap;
+ qreply(q, reply);
+ break;
+
+ case DL_UNBIND_REQ:
+ if (size < sizeof(dl_unbind_req_t))
+ goto badprim;
+ if (us->state != DL_IDLE) {
+ dlpi_error(q, us, DL_UNBIND_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+ us->sap = -1;
+ us->state = DL_UNBOUND;
+#ifdef LACHTCP
+ us->ppa->ifstats.ifs_active = 0;
+#endif
+ dlpi_ok(q, DL_UNBIND_REQ);
+ break;
+
+ case DL_UNITDATA_REQ:
+ if (size < sizeof(dl_unitdata_req_t))
+ goto badprim;
+ if (us->state != DL_IDLE) {
+ dlpi_error(q, us, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+ if ((ppa = us->ppa) == 0) {
+ cmn_err(CE_CONT, "ppp: in state dl_idle but ppa == 0?\n");
+ break;
+ }
+ len = mp->b_cont == 0? 0: msgdsize(mp->b_cont);
+ if (len > ppa->mtu) {
+ DPRINT2("dlpi data too large (%d > %d)\n", len, ppa->mtu);
+ break;
+ }
+
+#if defined(SOL2)
+ /*
+ * Should there be any promiscuous stream(s), send the data
+ * up for each promiscuous stream that we recognize.
+ */
+ if (mp->b_cont)
+ promisc_sendup(ppa, mp->b_cont, us->sap, 0);
+#endif /* defined(SOL2) */
+
+ mp->b_band = 0;
+#ifdef PRIOQ
+ /* Extract s_port & d_port from IP-packet, the code is a bit
+ dirty here, but so am I, too... */
+ if (mp->b_datap->db_type == M_PROTO && us->sap == PPP_IP
+ && mp->b_cont != 0) {
+ u_char *bb, *tlh;
+ int iphlen, len;
+ u_short *ptr;
+ u_char band_unset, cur_band, syn;
+ u_short s_port, d_port;
+
+ bb = mp->b_cont->b_rptr; /* bb points to IP-header*/
+ len = mp->b_cont->b_wptr - mp->b_cont->b_rptr;
+ syn = 0;
+ s_port = IPPORT_DEFAULT;
+ d_port = IPPORT_DEFAULT;
+ if (len >= 20) { /* 20 = minimum length of IP header */
+ iphlen = (bb[0] & 0x0f) * 4;
+ tlh = bb + iphlen;
+ len -= iphlen;
+ switch (bb[9]) {
+ case IPPROTO_TCP:
+ if (len >= 20) { /* min length of TCP header */
+ s_port = (tlh[0] << 8) + tlh[1];
+ d_port = (tlh[2] << 8) + tlh[3];
+ syn = tlh[13] & 0x02;
+ }
+ break;
+ case IPPROTO_UDP:
+ if (len >= 8) { /* min length of UDP header */
+ s_port = (tlh[0] << 8) + tlh[1];
+ d_port = (tlh[2] << 8) + tlh[3];
+ }
+ break;
+ }
+ }
+
+ /*
+ * Now calculate b_band for this packet from the
+ * port-priority table.
+ */
+ ptr = prioq_table;
+ cur_band = max_band;
+ band_unset = 1;
+ while (*ptr) {
+ while (*ptr && band_unset)
+ if (s_port == *ptr || d_port == *ptr++) {
+ mp->b_band = cur_band;
+ band_unset = 0;
+ break;
+ }
+ ptr++;
+ cur_band--;
+ }
+ if (band_unset)
+ mp->b_band = def_band;
+ /* It may be usable to urge SYN packets a bit */
+ if (syn)
+ mp->b_band++;
+ }
+#endif /* PRIOQ */
+ /* this assumes PPP_HDRLEN <= sizeof(dl_unitdata_req_t) */
+ if (mp->b_datap->db_ref > 1) {
+ np = allocb(PPP_HDRLEN, BPRI_HI);
+ if (np == 0)
+ break; /* gak! */
+ np->b_cont = mp->b_cont;
+ mp->b_cont = 0;
+ freeb(mp);
+ mp = np;
+ } else
+ mp->b_datap->db_type = M_DATA;
+ /* XXX should use dl_dest_addr_offset/length here,
+ but we would have to translate ETHERTYPE_IP -> PPP_IP */
+ mp->b_wptr = mp->b_rptr + PPP_HDRLEN;
+ mp->b_rptr[0] = PPP_ALLSTATIONS;
+ mp->b_rptr[1] = PPP_UI;
+ mp->b_rptr[2] = us->sap >> 8;
+ mp->b_rptr[3] = us->sap;
+ if (pass_packet(us, mp, 1)) {
+ if (!send_data(mp, us))
+ putq(q, mp);
+ }
+ return;
+
+#if DL_CURRENT_VERSION >= 2
+ case DL_PHYS_ADDR_REQ:
+ if (size < sizeof(dl_phys_addr_req_t))
+ goto badprim;
+
+ /*
+ * Don't check state because ifconfig sends this one down too
+ */
+
+ if ((reply = allocb(sizeof(dl_phys_addr_ack_t)+ETHERADDRL,
+ BPRI_HI)) == 0)
+ break; /* should do bufcall */
+ reply->b_datap->db_type = M_PCPROTO;
+ paddrack = (dl_phys_addr_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_phys_addr_ack_t);
+ bzero((caddr_t) paddrack, sizeof(dl_phys_addr_ack_t)+ETHERADDRL);
+ paddrack->dl_primitive = DL_PHYS_ADDR_ACK;
+ paddrack->dl_addr_length = ETHERADDRL;
+ paddrack->dl_addr_offset = sizeof(dl_phys_addr_ack_t);
+ bcopy(&eaddr, reply->b_wptr, ETHERADDRL);
+ reply->b_wptr += ETHERADDRL;
+ qreply(q, reply);
+ break;
+
+#if defined(SOL2)
+ case DL_PROMISCON_REQ:
+ if (size < sizeof(dl_promiscon_req_t))
+ goto badprim;
+ us->flags |= US_PROMISC;
+ dlpi_ok(q, DL_PROMISCON_REQ);
+ break;
+
+ case DL_PROMISCOFF_REQ:
+ if (size < sizeof(dl_promiscoff_req_t))
+ goto badprim;
+ us->flags &= ~US_PROMISC;
+ dlpi_ok(q, DL_PROMISCOFF_REQ);
+ break;
+#else
+ case DL_PROMISCON_REQ: /* fall thru */
+ case DL_PROMISCOFF_REQ: /* fall thru */
+#endif /* defined(SOL2) */
+#endif /* DL_CURRENT_VERSION >= 2 */
+
+#if DL_CURRENT_VERSION >= 2
+ case DL_SET_PHYS_ADDR_REQ:
+ case DL_SUBS_BIND_REQ:
+ case DL_SUBS_UNBIND_REQ:
+ case DL_ENABMULTI_REQ:
+ case DL_DISABMULTI_REQ:
+ case DL_XID_REQ:
+ case DL_TEST_REQ:
+ case DL_REPLY_UPDATE_REQ:
+ case DL_REPLY_REQ:
+ case DL_DATA_ACK_REQ:
+#endif
+ case DL_CONNECT_REQ:
+ case DL_TOKEN_REQ:
+ dlpi_error(q, us, d->dl_primitive, DL_NOTSUPPORTED, 0);
+ break;
+
+ case DL_CONNECT_RES:
+ case DL_DISCONNECT_REQ:
+ case DL_RESET_REQ:
+ case DL_RESET_RES:
+ dlpi_error(q, us, d->dl_primitive, DL_OUTSTATE, 0);
+ break;
+
+ case DL_UDQOS_REQ:
+ dlpi_error(q, us, d->dl_primitive, DL_BADQOSTYPE, 0);
+ break;
+
+#if DL_CURRENT_VERSION >= 2
+ case DL_TEST_RES:
+ case DL_XID_RES:
+ break;
+#endif
+
+ default:
+ cmn_err(CE_CONT, "ppp: unknown dlpi prim 0x%x\n", d->dl_primitive);
+ /* fall through */
+ badprim:
+ dlpi_error(q, us, d->dl_primitive, DL_BADPRIM, 0);
+ break;
+ }
+ freemsg(mp);
+}
+
+static void
+dlpi_error(q, us, prim, err, uerr)
+ queue_t *q;
+ upperstr_t *us;
+ int prim, err, uerr;
+{
+ mblk_t *reply;
+ dl_error_ack_t *errp;
+
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: dlpi error, prim=%x, err=%x\n", us->mn, prim, err);
+ reply = allocb(sizeof(dl_error_ack_t), BPRI_HI);
+ if (reply == 0)
+ return; /* XXX should do bufcall */
+ reply->b_datap->db_type = M_PCPROTO;
+ errp = (dl_error_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_error_ack_t);
+ errp->dl_primitive = DL_ERROR_ACK;
+ errp->dl_error_primitive = prim;
+ errp->dl_errno = err;
+ errp->dl_unix_errno = uerr;
+ qreply(q, reply);
+}
+
+static void
+dlpi_ok(q, prim)
+ queue_t *q;
+ int prim;
+{
+ mblk_t *reply;
+ dl_ok_ack_t *okp;
+
+ reply = allocb(sizeof(dl_ok_ack_t), BPRI_HI);
+ if (reply == 0)
+ return; /* XXX should do bufcall */
+ reply->b_datap->db_type = M_PCPROTO;
+ okp = (dl_ok_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_ok_ack_t);
+ okp->dl_primitive = DL_OK_ACK;
+ okp->dl_correct_primitive = prim;
+ qreply(q, reply);
+}
+#endif /* NO_DLPI */
+
+static int
+pass_packet(us, mp, outbound)
+ upperstr_t *us;
+ mblk_t *mp;
+ int outbound;
+{
+ int pass;
+ upperstr_t *ppa;
+
+ if ((ppa = us->ppa) == 0) {
+ freemsg(mp);
+ return 0;
+ }
+
+#ifdef FILTER_PACKETS
+ pass = ip_hard_filter(us, mp, outbound);
+#else
+ /*
+ * Here is where we might, in future, decide whether to pass
+ * or drop the packet, and whether it counts as link activity.
+ */
+ pass = 1;
+#endif /* FILTER_PACKETS */
+
+ if (pass < 0) {
+ /* pass only if link already up, and don't update time */
+ if (ppa->lowerq == 0) {
+ freemsg(mp);
+ return 0;
+ }
+ pass = 1;
+ } else if (pass) {
+ if (outbound)
+ ppa->last_sent = time;
+ else
+ ppa->last_recv = time;
+ }
+
+ return pass;
+}
+
+/*
+ * We have some data to send down to the lower stream (or up the
+ * control stream, if we don't have a lower stream attached).
+ * Returns 1 if the message was dealt with, 0 if it wasn't able
+ * to be sent on and should therefore be queued up.
+ */
+static int
+send_data(mp, us)
+ mblk_t *mp;
+ upperstr_t *us;
+{
+ upperstr_t *ppa;
+
+ if ((us->flags & US_BLOCKED) || us->npmode == NPMODE_QUEUE)
+ return 0;
+ ppa = us->ppa;
+ if (ppa == 0 || us->npmode == NPMODE_DROP || us->npmode == NPMODE_ERROR) {
+ if (us->flags & US_DBGLOG)
+ DPRINT2("ppp/%d: dropping pkt (npmode=%d)\n", us->mn, us->npmode);
+ freemsg(mp);
+ return 1;
+ }
+ if (ppa->lowerq == 0) {
+ /* try to send it up the control stream */
+ if (bcanputnext(ppa->q, mp->b_band)) {
+ /*
+ * The message seems to get corrupted for some reason if
+ * we just send the message up as it is, so we send a copy.
+ */
+ mblk_t *np = copymsg(mp);
+ freemsg(mp);
+ if (np != 0)
+ putnext(ppa->q, np);
+ return 1;
+ }
+ } else {
+ if (bcanputnext(ppa->lowerq, mp->b_band)) {
+ MT_ENTER(&ppa->stats_lock);
+ ppa->stats.ppp_opackets++;
+ ppa->stats.ppp_obytes += msgdsize(mp);
+#ifdef INCR_OPACKETS
+ INCR_OPACKETS(ppa);
+#endif
+ MT_EXIT(&ppa->stats_lock);
+ /*
+ * The lower queue is only ever detached while holding an
+ * exclusive lock on the whole driver. So we can be confident
+ * that the lower queue is still there.
+ */
+ putnext(ppa->lowerq, mp);
+ return 1;
+ }
+ }
+ us->flags |= US_BLOCKED;
+ return 0;
+}
+
+/*
+ * Allocate a new PPA id and link this stream into the list of PPAs.
+ * This procedure is called with an exclusive lock on all queues in
+ * this driver.
+ */
+static void
+new_ppa(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us, *up, **usp;
+ int ppa_id;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("new_ppa: q_ptr = 0!\n");
+ return;
+ }
+
+ usp = &ppas;
+ ppa_id = 0;
+ while ((up = *usp) != 0 && ppa_id == up->ppa_id) {
+ ++ppa_id;
+ usp = &up->nextppa;
+ }
+ us->ppa_id = ppa_id;
+ us->ppa = us;
+ us->next = 0;
+ us->nextppa = *usp;
+ *usp = us;
+ us->flags |= US_CONTROL;
+ us->npmode = NPMODE_PASS;
+
+ us->mtu = PPP_MTU;
+ us->mru = PPP_MRU;
+
+#ifdef SOL2
+ /*
+ * Create a kstats record for our statistics, so netstat -i works.
+ */
+ if (us->kstats == 0) {
+ char unit[32];
+
+ sprintf(unit, "ppp%d", us->ppa->ppa_id);
+ us->kstats = kstat_create("ppp", us->ppa->ppa_id, unit,
+ "net", KSTAT_TYPE_NAMED, 4, 0);
+ if (us->kstats != 0) {
+ kstat_named_t *kn = KSTAT_NAMED_PTR(us->kstats);
+
+ strcpy(kn[0].name, "ipackets");
+ kn[0].data_type = KSTAT_DATA_ULONG;
+ strcpy(kn[1].name, "ierrors");
+ kn[1].data_type = KSTAT_DATA_ULONG;
+ strcpy(kn[2].name, "opackets");
+ kn[2].data_type = KSTAT_DATA_ULONG;
+ strcpy(kn[3].name, "oerrors");
+ kn[3].data_type = KSTAT_DATA_ULONG;
+ kstat_install(us->kstats);
+ }
+ }
+#endif /* SOL2 */
+
+ *(int *)mp->b_cont->b_rptr = ppa_id;
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+}
+
+static void
+attach_ppa(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us, *t;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("attach_ppa: q_ptr = 0!\n");
+ return;
+ }
+
+#ifndef NO_DLPI
+ us->state = DL_UNBOUND;
+#endif
+ for (t = us->ppa; t->next != 0; t = t->next)
+ ;
+ t->next = us;
+ us->next = 0;
+ if (mp->b_datap->db_type == M_IOCTL) {
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+ } else {
+#ifndef NO_DLPI
+ dlpi_ok(q, DL_ATTACH_REQ);
+#endif
+ }
+}
+
+static void
+detach_ppa(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us, *t;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("detach_ppa: q_ptr = 0!\n");
+ return;
+ }
+
+ for (t = us->ppa; t->next != 0; t = t->next)
+ if (t->next == us) {
+ t->next = us->next;
+ break;
+ }
+ us->next = 0;
+ us->ppa = 0;
+#ifndef NO_DLPI
+ us->state = DL_UNATTACHED;
+ dlpi_ok(q, DL_DETACH_REQ);
+#endif
+}
+
+/*
+ * We call this with qwriter in order to give the upper queue procedures
+ * the guarantee that the lower queue is not going to go away while
+ * they are executing.
+ */
+static void
+detach_lower(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("detach_lower: q_ptr = 0!\n");
+ return;
+ }
+
+ LOCK_LOWER_W;
+ us->lowerq->q_ptr = 0;
+ RD(us->lowerq)->q_ptr = 0;
+ us->lowerq = 0;
+ UNLOCK_LOWER;
+
+ /* Unblock streams which now feed back up the control stream. */
+ qenable(us->q);
+
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+}
+
+static int
+pppuwsrv(q)
+ queue_t *q;
+{
+ upperstr_t *us, *as;
+ mblk_t *mp;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("pppuwsrv: q_ptr = 0!\n");
+ return 0;
+ }
+
+ /*
+ * If this is a control stream, then this service procedure
+ * probably got enabled because of flow control in the lower
+ * stream being enabled (or because of the lower stream going
+ * away). Therefore we enable the service procedure of all
+ * attached upper streams.
+ */
+ if (us->flags & US_CONTROL) {
+ for (as = us->next; as != 0; as = as->next)
+ qenable(WR(as->q));
+ }
+
+ /* Try to send on any data queued here. */
+ us->flags &= ~US_BLOCKED;
+ while ((mp = getq(q)) != 0) {
+ if (!send_data(mp, us)) {
+ putbq(q, mp);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* should never get called... */
+static int
+ppplwput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ putnext(q, mp);
+ return 0;
+}
+
+static int
+ppplwsrv(q)
+ queue_t *q;
+{
+ queue_t *uq;
+
+ /*
+ * Flow control has back-enabled this stream:
+ * enable the upper write service procedure for
+ * the upper control stream for this lower stream.
+ */
+ LOCK_LOWER_R;
+ uq = (queue_t *) q->q_ptr;
+ if (uq != 0)
+ qenable(uq);
+ UNLOCK_LOWER;
+ return 0;
+}
+
+/*
+ * This should only get called for control streams.
+ */
+static int
+pppurput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *ppa, *us;
+ int proto, len;
+ struct iocblk *iop;
+
+ ppa = (upperstr_t *) q->q_ptr;
+ if (ppa == 0) {
+ DPRINT("pppurput: q_ptr = 0!\n");
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+ case M_CTL:
+ MT_ENTER(&ppa->stats_lock);
+ switch (*mp->b_rptr) {
+ case PPPCTL_IERROR:
+#ifdef INCR_IERRORS
+ INCR_IERRORS(ppa);
+#endif
+ ppa->stats.ppp_ierrors++;
+ break;
+ case PPPCTL_OERROR:
+#ifdef INCR_OERRORS
+ INCR_OERRORS(ppa);
+#endif
+ ppa->stats.ppp_oerrors++;
+ break;
+ }
+ MT_EXIT(&ppa->stats_lock);
+ freemsg(mp);
+ break;
+
+ case M_IOCACK:
+ case M_IOCNAK:
+ /*
+ * Attempt to match up the response with the stream
+ * that the request came from.
+ */
+ iop = (struct iocblk *) mp->b_rptr;
+ for (us = ppa; us != 0; us = us->next)
+ if (us->ioc_id == iop->ioc_id)
+ break;
+ if (us == 0)
+ freemsg(mp);
+ else
+ putnext(us->q, mp);
+ break;
+
+ case M_HANGUP:
+ /*
+ * The serial device has hung up. We don't want to send
+ * the M_HANGUP message up to pppd because that will stop
+ * us from using the control stream any more. Instead we
+ * send a zero-length message as an end-of-file indication.
+ */
+ freemsg(mp);
+ mp = allocb(1, BPRI_HI);
+ if (mp == 0) {
+ DPRINT1("ppp/%d: couldn't allocate eof message!\n", ppa->mn);
+ break;
+ }
+ putnext(ppa->q, mp);
+ break;
+
+ default:
+ if (mp->b_datap->db_type == M_DATA) {
+ len = msgdsize(mp);
+ if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN) {
+ PULLUP(mp, PPP_HDRLEN);
+ if (mp == 0) {
+ DPRINT1("ppp_urput: msgpullup failed (len=%d)\n", len);
+ break;
+ }
+ }
+ MT_ENTER(&ppa->stats_lock);
+ ppa->stats.ppp_ipackets++;
+ ppa->stats.ppp_ibytes += len;
+#ifdef INCR_IPACKETS
+ INCR_IPACKETS(ppa);
+#endif
+ MT_EXIT(&ppa->stats_lock);
+
+ proto = PPP_PROTOCOL(mp->b_rptr);
+
+#if defined(SOL2)
+ /*
+ * Should there be any promiscuous stream(s), send the data
+ * up for each promiscuous stream that we recognize.
+ */
+ promisc_sendup(ppa, mp, proto, 1);
+#endif /* defined(SOL2) */
+
+ if (proto < 0x8000 && (us = find_dest(ppa, proto)) != 0) {
+ /*
+ * A data packet for some network protocol.
+ * Queue it on the upper stream for that protocol.
+ * XXX could we just putnext it? (would require thought)
+ * The rblocked flag is there to ensure that we keep
+ * messages in order for each network protocol.
+ */
+ if (!pass_packet(us, mp, 0))
+ break;
+ if (!us->rblocked && !canput(us->q))
+ us->rblocked = 1;
+ if (!us->rblocked)
+ putq(us->q, mp);
+ else
+ putq(q, mp);
+ break;
+ }
+ }
+ /*
+ * A control frame, a frame for an unknown protocol,
+ * or some other message type.
+ * Send it up to pppd via the control stream.
+ */
+ if (queclass(mp) == QPCTL || canputnext(ppa->q))
+ putnext(ppa->q, mp);
+ else
+ putq(q, mp);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+pppursrv(q)
+ queue_t *q;
+{
+ upperstr_t *us, *as;
+ mblk_t *mp, *hdr;
+#ifndef NO_DLPI
+ dl_unitdata_ind_t *ud;
+#endif
+ int proto;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("pppursrv: q_ptr = 0!\n");
+ return 0;
+ }
+
+ if (us->flags & US_CONTROL) {
+ /*
+ * A control stream.
+ * If there is no lower queue attached, run the write service
+ * routines of other upper streams attached to this PPA.
+ */
+ if (us->lowerq == 0) {
+ as = us;
+ do {
+ if (as->flags & US_BLOCKED)
+ qenable(WR(as->q));
+ as = as->next;
+ } while (as != 0);
+ }
+
+ /*
+ * Messages get queued on this stream's read queue if they
+ * can't be queued on the read queue of the attached stream
+ * that they are destined for. This is for flow control -
+ * when this queue fills up, the lower read put procedure will
+ * queue messages there and the flow control will propagate
+ * down from there.
+ */
+ while ((mp = getq(q)) != 0) {
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ if (proto < 0x8000 && (as = find_dest(us, proto)) != 0) {
+ if (!canput(as->q))
+ break;
+ putq(as->q, mp);
+ } else {
+ if (!canputnext(q))
+ break;
+ putnext(q, mp);
+ }
+ }
+ if (mp) {
+ putbq(q, mp);
+ } else {
+ /* can now put stuff directly on network protocol streams again */
+ for (as = us->next; as != 0; as = as->next)
+ as->rblocked = 0;
+ }
+
+ /*
+ * If this stream has a lower stream attached,
+ * enable the read queue's service routine.
+ * XXX we should really only do this if the queue length
+ * has dropped below the low-water mark.
+ */
+ if (us->lowerq != 0)
+ qenable(RD(us->lowerq));
+
+ } else {
+ /*
+ * A network protocol stream. Put a DLPI header on each
+ * packet and send it on.
+ * (Actually, it seems that the IP module will happily
+ * accept M_DATA messages without the DL_UNITDATA_IND header.)
+ */
+ while ((mp = getq(q)) != 0) {
+ if (!canputnext(q)) {
+ putbq(q, mp);
+ break;
+ }
+#ifndef NO_DLPI
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ mp->b_rptr += PPP_HDRLEN;
+ hdr = allocb(sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint),
+ BPRI_MED);
+ if (hdr == 0) {
+ /* XXX should put it back and use bufcall */
+ freemsg(mp);
+ continue;
+ }
+ hdr->b_datap->db_type = M_PROTO;
+ ud = (dl_unitdata_ind_t *) hdr->b_wptr;
+ hdr->b_wptr += sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint);
+ hdr->b_cont = mp;
+ ud->dl_primitive = DL_UNITDATA_IND;
+ ud->dl_dest_addr_length = sizeof(uint);
+ ud->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+ ud->dl_src_addr_length = sizeof(uint);
+ ud->dl_src_addr_offset = ud->dl_dest_addr_offset + sizeof(uint);
+#if DL_CURRENT_VERSION >= 2
+ ud->dl_group_address = 0;
+#endif
+ /* Send the DLPI client the data with the SAP they requested,
+ (e.g. ETHERTYPE_IP) rather than the PPP protocol number
+ (e.g. PPP_IP) */
+ ((uint *)(ud + 1))[0] = us->req_sap; /* dest SAP */
+ ((uint *)(ud + 1))[1] = us->req_sap; /* src SAP */
+ putnext(q, hdr);
+#else /* NO_DLPI */
+ putnext(q, mp);
+#endif /* NO_DLPI */
+ }
+ /*
+ * Now that we have consumed some packets from this queue,
+ * enable the control stream's read service routine so that we
+ * can process any packets for us that might have got queued
+ * there for flow control reasons.
+ */
+ if (us->ppa)
+ qenable(us->ppa->q);
+ }
+
+ return 0;
+}
+
+static upperstr_t *
+find_dest(ppa, proto)
+ upperstr_t *ppa;
+ int proto;
+{
+ upperstr_t *us;
+
+ for (us = ppa->next; us != 0; us = us->next)
+ if (proto == us->sap)
+ break;
+ return us;
+}
+
+#if defined (SOL2)
+/*
+ * Test upstream promiscuous conditions. As of now, only pass IPv4 and
+ * Ipv6 packets upstream (let PPP packets be decoded elsewhere).
+ */
+static upperstr_t *
+find_promisc(us, proto)
+ upperstr_t *us;
+ int proto;
+{
+
+ if ((proto != PPP_IP) && (proto != PPP_IPV6))
+ return (upperstr_t *)0;
+
+ for ( ; us; us = us->next) {
+ if ((us->flags & US_PROMISC) && (us->state == DL_IDLE))
+ return us;
+ }
+
+ return (upperstr_t *)0;
+}
+
+/*
+ * Prepend an empty Ethernet header to msg for snoop, et al.
+ */
+static mblk_t *
+prepend_ether(us, mp, proto)
+ upperstr_t *us;
+ mblk_t *mp;
+ int proto;
+{
+ mblk_t *eh;
+ int type;
+
+ if ((eh = allocb(sizeof(struct ether_header), BPRI_HI)) == 0) {
+ freemsg(mp);
+ return (mblk_t *)0;
+ }
+
+ if (proto == PPP_IP)
+ type = ETHERTYPE_IP;
+ else if (proto == PPP_IPV6)
+ type = ETHERTYPE_IPV6;
+ else
+ type = proto; /* What else? Let decoder decide */
+
+ eh->b_wptr += sizeof(struct ether_header);
+ bzero((caddr_t)eh->b_rptr, sizeof(struct ether_header));
+ ((struct ether_header *)eh->b_rptr)->ether_type = htons((short)type);
+ eh->b_cont = mp;
+ return (eh);
+}
+
+/*
+ * Prepend DL_UNITDATA_IND mblk to msg
+ */
+static mblk_t *
+prepend_udind(us, mp, proto)
+ upperstr_t *us;
+ mblk_t *mp;
+ int proto;
+{
+ dl_unitdata_ind_t *dlu;
+ mblk_t *dh;
+ size_t size;
+
+ size = sizeof(dl_unitdata_ind_t);
+ if ((dh = allocb(size, BPRI_MED)) == 0) {
+ freemsg(mp);
+ return (mblk_t *)0;
+ }
+
+ dh->b_datap->db_type = M_PROTO;
+ dh->b_wptr = dh->b_datap->db_lim;
+ dh->b_rptr = dh->b_wptr - size;
+
+ dlu = (dl_unitdata_ind_t *)dh->b_rptr;
+ dlu->dl_primitive = DL_UNITDATA_IND;
+ dlu->dl_dest_addr_length = 0;
+ dlu->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+ dlu->dl_src_addr_length = 0;
+ dlu->dl_src_addr_offset = sizeof(dl_unitdata_ind_t);
+ dlu->dl_group_address = 0;
+
+ dh->b_cont = mp;
+ return (dh);
+}
+
+/*
+ * For any recognized promiscuous streams, send data upstream
+ */
+static void
+promisc_sendup(ppa, mp, proto, skip)
+ upperstr_t *ppa;
+ mblk_t *mp;
+ int proto, skip;
+{
+ mblk_t *dup_mp, *dup_dup_mp;
+ upperstr_t *prus, *nprus;
+
+ if ((prus = find_promisc(ppa, proto)) != 0) {
+ if (dup_mp = dupmsg(mp)) {
+
+ if (skip)
+ dup_mp->b_rptr += PPP_HDRLEN;
+
+ for ( ; nprus = find_promisc(prus->next, proto);
+ prus = nprus) {
+
+ if (dup_dup_mp = dupmsg(dup_mp)) {
+ if (canputnext(prus->q)) {
+ if (prus->flags & US_RAWDATA) {
+ dup_dup_mp = prepend_ether(prus, dup_dup_mp, proto);
+ putnext(prus->q, dup_dup_mp);
+ } else {
+ dup_dup_mp = prepend_udind(prus, dup_dup_mp, proto);
+ putnext(prus->q, dup_dup_mp);
+ }
+ } else {
+ DPRINT("ppp_urput: data to promisc q dropped\n");
+ freemsg(dup_dup_mp);
+ }
+ }
+ }
+
+ if (canputnext(prus->q)) {
+ if (prus->flags & US_RAWDATA) {
+ dup_mp = prepend_ether(prus, dup_mp, proto);
+ putnext(prus->q, dup_mp);
+ } else {
+ dup_mp = prepend_udind(prus, dup_mp, proto);
+ putnext(prus->q, dup_mp);
+ }
+ } else {
+ DPRINT("ppp_urput: data to promisc q dropped\n");
+ freemsg(dup_mp);
+ }
+ }
+ }
+}
+#endif /* defined(SOL2) */
+
+/*
+ * We simply put the message on to the associated upper control stream
+ * (either here or in ppplrsrv). That way we enter the perimeters
+ * before looking through the list of attached streams to decide which
+ * stream it should go up.
+ */
+static int
+ppplrput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ queue_t *uq;
+ struct iocblk *iop;
+
+ switch (mp->b_datap->db_type) {
+ case M_IOCTL:
+ iop = (struct iocblk *) mp->b_rptr;
+ iop->ioc_error = EINVAL;
+ mp->b_datap->db_type = M_IOCNAK;
+ qreply(q, mp);
+ return 0;
+ case M_FLUSH:
+ if (*mp->b_rptr & FLUSHR)
+ flushq(q, FLUSHDATA);
+ if (*mp->b_rptr & FLUSHW) {
+ *mp->b_rptr &= ~FLUSHR;
+ qreply(q, mp);
+ } else
+ freemsg(mp);
+ return 0;
+ }
+
+ /*
+ * If we can't get the lower lock straight away, queue this one
+ * rather than blocking, to avoid the possibility of deadlock.
+ */
+ if (!TRYLOCK_LOWER_R) {
+ putq(q, mp);
+ return 0;
+ }
+
+ /*
+ * Check that we're still connected to the driver.
+ */
+ uq = (queue_t *) q->q_ptr;
+ if (uq == 0) {
+ UNLOCK_LOWER;
+ DPRINT1("ppplrput: q = %x, uq = 0??\n", q);
+ freemsg(mp);
+ return 0;
+ }
+
+ /*
+ * Try to forward the message to the put routine for the upper
+ * control stream for this lower stream.
+ * If there are already messages queued here, queue this one so
+ * they don't get out of order.
+ */
+ if (queclass(mp) == QPCTL || (qsize(q) == 0 && canput(uq)))
+ put(uq, mp);
+ else
+ putq(q, mp);
+
+ UNLOCK_LOWER;
+ return 0;
+}
+
+static int
+ppplrsrv(q)
+ queue_t *q;
+{
+ mblk_t *mp;
+ queue_t *uq;
+
+ /*
+ * Packets get queued here for flow control reasons
+ * or if the lrput routine couldn't get the lower lock
+ * without blocking.
+ */
+ LOCK_LOWER_R;
+ uq = (queue_t *) q->q_ptr;
+ if (uq == 0) {
+ UNLOCK_LOWER;
+ flushq(q, FLUSHALL);
+ DPRINT1("ppplrsrv: q = %x, uq = 0??\n", q);
+ return 0;
+ }
+ while ((mp = getq(q)) != 0) {
+ if (queclass(mp) == QPCTL || canput(uq))
+ put(uq, mp);
+ else {
+ putbq(q, mp);
+ break;
+ }
+ }
+ UNLOCK_LOWER;
+ return 0;
+}
+
+static int
+putctl2(q, type, code, val)
+ queue_t *q;
+ int type, code, val;
+{
+ mblk_t *mp;
+
+ mp = allocb(2, BPRI_HI);
+ if (mp == 0)
+ return 0;
+ mp->b_datap->db_type = type;
+ mp->b_wptr[0] = code;
+ mp->b_wptr[1] = val;
+ mp->b_wptr += 2;
+ putnext(q, mp);
+ return 1;
+}
+
+static int
+putctl4(q, type, code, val)
+ queue_t *q;
+ int type, code, val;
+{
+ mblk_t *mp;
+
+ mp = allocb(4, BPRI_HI);
+ if (mp == 0)
+ return 0;
+ mp->b_datap->db_type = type;
+ mp->b_wptr[0] = code;
+ ((short *)mp->b_wptr)[1] = val;
+ mp->b_wptr += 4;
+ putnext(q, mp);
+ return 1;
+}
+
+static void
+debug_dump(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us;
+ queue_t *uq, *lq;
+
+ DPRINT("ppp upper streams:\n");
+ for (us = minor_devs; us != 0; us = us->nextmn) {
+ uq = us->q;
+ DPRINT3(" %d: q=%x rlev=%d",
+ us->mn, uq, (uq? qsize(uq): 0));
+ DPRINT3(" wlev=%d flags=0x%b", (uq? qsize(WR(uq)): 0),
+ us->flags, "\020\1priv\2control\3blocked\4last");
+ DPRINT3(" state=%x sap=%x req_sap=%x", us->state, us->sap,
+ us->req_sap);
+ if (us->ppa == 0)
+ DPRINT(" ppa=?\n");
+ else
+ DPRINT1(" ppa=%d\n", us->ppa->ppa_id);
+ if (us->flags & US_CONTROL) {
+ lq = us->lowerq;
+ DPRINT3(" control for %d lq=%x rlev=%d",
+ us->ppa_id, lq, (lq? qsize(RD(lq)): 0));
+ DPRINT3(" wlev=%d mru=%d mtu=%d\n",
+ (lq? qsize(lq): 0), us->mru, us->mtu);
+ }
+ }
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+}
+
+#ifdef FILTER_PACKETS
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+
+#define MAX_IPHDR 128 /* max TCP/IP header size */
+
+
+/* The following table contains a hard-coded list of protocol/port pairs.
+ * Any matching packets are either discarded unconditionally, or,
+ * if ok_if_link_up is non-zero when a connection does not currently exist
+ * (i.e., they go through if the connection is present, but never initiate
+ * a dial-out).
+ * This idea came from a post by dm@garage.uun.org (David Mazieres)
+ */
+static struct pktfilt_tab {
+ int proto;
+ u_short port;
+ u_short ok_if_link_up;
+} pktfilt_tab[] = {
+ { IPPROTO_UDP, 520, 1 }, /* RIP, ok to pass if link is up */
+ { IPPROTO_UDP, 123, 1 }, /* NTP, don't keep up the link for it */
+ { -1, 0, 0 } /* terminator entry has port == -1 */
+};
+
+
+static int
+ip_hard_filter(us, mp, outbound)
+ upperstr_t *us;
+ mblk_t *mp;
+ int outbound;
+{
+ struct ip *ip;
+ struct pktfilt_tab *pft;
+ mblk_t *temp_mp;
+ int proto;
+ int len, hlen;
+
+
+ /* Note, the PPP header has already been pulled up in all cases */
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: filter, proto=0x%x, out=%d\n", us->mn, proto, outbound);
+
+ switch (proto)
+ {
+ case PPP_IP:
+ if ((mp->b_wptr - mp->b_rptr) == PPP_HDRLEN && mp->b_cont != 0) {
+ temp_mp = mp->b_cont;
+ len = msgdsize(temp_mp);
+ hlen = (len < MAX_IPHDR) ? len : MAX_IPHDR;
+ PULLUP(temp_mp, hlen);
+ if (temp_mp == 0) {
+ DPRINT2("ppp/%d: filter, pullup next failed, len=%d\n",
+ us->mn, hlen);
+ mp->b_cont = 0; /* PULLUP() freed the rest */
+ freemsg(mp);
+ return 0;
+ }
+ ip = (struct ip *)mp->b_cont->b_rptr;
+ }
+ else {
+ len = msgdsize(mp);
+ hlen = (len < (PPP_HDRLEN+MAX_IPHDR)) ? len : (PPP_HDRLEN+MAX_IPHDR);
+ PULLUP(mp, hlen);
+ if (mp == 0) {
+ DPRINT2("ppp/%d: filter, pullup failed, len=%d\n",
+ us->mn, hlen);
+ return 0;
+ }
+ ip = (struct ip *)(mp->b_rptr + PPP_HDRLEN);
+ }
+
+ /* For IP traffic, certain packets (e.g., RIP) may be either
+ * 1. ignored - dropped completely
+ * 2. will not initiate a connection, but
+ * will be passed if a connection is currently up.
+ */
+ for (pft=pktfilt_tab; pft->proto != -1; pft++) {
+ if (ip->ip_p == pft->proto) {
+ switch(pft->proto) {
+ case IPPROTO_UDP:
+ if (((struct udphdr *) &((int *)ip)[ip->ip_hl])->uh_dport
+ == htons(pft->port)) goto endfor;
+ break;
+ case IPPROTO_TCP:
+ if (((struct tcphdr *) &((int *)ip)[ip->ip_hl])->th_dport
+ == htons(pft->port)) goto endfor;
+ break;
+ }
+ }
+ }
+ endfor:
+ if (pft->proto != -1) {
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: found IP pkt, proto=0x%x (%d)\n",
+ us->mn, pft->proto, pft->port);
+ /* Discard if not connected, or if not pass_with_link_up */
+ /* else, if link is up let go by, but don't update time */
+ return pft->ok_if_link_up? -1: 0;
+ }
+ break;
+ } /* end switch (proto) */
+
+ return 1;
+}
+#endif /* FILTER_PACKETS */
+
diff --git a/mdk-stage1/ppp/modules/ppp_ahdlc.c b/mdk-stage1/ppp/modules/ppp_ahdlc.c
new file mode 100644
index 000000000..d0b961258
--- /dev/null
+++ b/mdk-stage1/ppp/modules/ppp_ahdlc.c
@@ -0,0 +1,878 @@
+/*
+ * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC.
+ *
+ * Re-written by Adi Masputra <adi.masputra@sun.com>, based on
+ * the original ppp_ahdlc.c
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stream.h>
+#include <sys/errno.h>
+
+#ifdef SVR4
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#else
+#include <sys/user.h>
+#ifdef __osf__
+#include <sys/cmn_err.h>
+#endif
+#endif /* SVR4 */
+
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include "ppp_mod.h"
+
+/*
+ * Right now, mutex is only enabled for Solaris 2.x
+ */
+#if defined(SOL2)
+#define USE_MUTEX
+#endif /* SOL2 */
+
+/*
+ * intpointer_t and uintpointer_t are signed and unsigned integer types
+ * large enough to hold any data pointer; that is, data pointers can be
+ * assigned into or from these integer types without losing precision.
+ * On recent Solaris releases, these types are defined in sys/int_types.h,
+ * but not on SunOS 4.x or the earlier Solaris versions.
+ */
+#if defined(_LP64) || defined(_I32LPx)
+typedef long intpointer_t;
+typedef unsigned long uintpointer_t;
+#else
+typedef int intpointer_t;
+typedef unsigned int uintpointer_t;
+#endif
+
+MOD_OPEN_DECL(ahdlc_open);
+MOD_CLOSE_DECL(ahdlc_close);
+static int ahdlc_wput __P((queue_t *, mblk_t *));
+static int ahdlc_rput __P((queue_t *, mblk_t *));
+static void ahdlc_encode __P((queue_t *, mblk_t *));
+static void ahdlc_decode __P((queue_t *, mblk_t *));
+static int msg_byte __P((mblk_t *, unsigned int));
+
+#if defined(SOL2)
+/*
+ * Don't send HDLC start flag is last transmit is within 1.5 seconds -
+ * FLAG_TIME is defined is microseconds
+ */
+#define FLAG_TIME 1500
+#define ABS(x) (x >= 0 ? x : (-x))
+#endif /* SOL2 */
+
+/*
+ * Extract byte i of message mp
+ */
+#define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
+ msg_byte((mp), (i)))
+
+/*
+ * Is this LCP packet one we have to transmit using LCP defaults?
+ */
+#define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
+
+/*
+ * Standard STREAMS declarations
+ */
+static struct module_info minfo = {
+ 0x7d23, "ppp_ahdl", 0, INFPSZ, 32768, 512
+};
+
+static struct qinit rinit = {
+ ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
+};
+
+static struct qinit winit = {
+ ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
+};
+
+#if defined(SVR4) && !defined(SOL2)
+int phdldevflag = 0;
+#define ppp_ahdlcinfo phdlinfo
+#endif /* defined(SVR4) && !defined(SOL2) */
+
+struct streamtab ppp_ahdlcinfo = {
+ &rinit, /* ptr to st_rdinit */
+ &winit, /* ptr to st_wrinit */
+ NULL, /* ptr to st_muxrinit */
+ NULL, /* ptr to st_muxwinit */
+#if defined(SUNOS4)
+ NULL /* ptr to ptr to st_modlist */
+#endif /* SUNOS4 */
+};
+
+#if defined(SUNOS4)
+int ppp_ahdlc_count = 0; /* open counter */
+#endif /* SUNOS4 */
+
+/*
+ * Per-stream state structure
+ */
+typedef struct ahdlc_state {
+#if defined(USE_MUTEX)
+ kmutex_t lock; /* lock for this structure */
+#endif /* USE_MUTEX */
+ int flags; /* link flags */
+ mblk_t *rx_buf; /* ptr to receive buffer */
+ int rx_buf_size; /* receive buffer size */
+ ushort_t infcs; /* calculated rx HDLC FCS */
+ u_int32_t xaccm[8]; /* 256-bit xmit ACCM */
+ u_int32_t raccm; /* 32-bit rcv ACCM */
+ int mtu; /* interface MTU */
+ int mru; /* link MRU */
+ int unit; /* current PPP unit number */
+ struct pppstat stats; /* statistic structure */
+#if defined(SOL2)
+ clock_t flag_time; /* time in usec between flags */
+ clock_t lbolt; /* last updated lbolt */
+#endif /* SOL2 */
+} ahdlc_state_t;
+
+/*
+ * Values for flags
+ */
+#define ESCAPED 0x100 /* last saw escape char on input */
+#define IFLUSH 0x200 /* flushing input due to error */
+
+/*
+ * RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also.
+ */
+#define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+static u_int32_t paritytab[8] =
+{
+ 0x96696996, 0x69969669, 0x69969669, 0x96696996,
+ 0x69969669, 0x96696996, 0x96696996, 0x69969669
+};
+
+/*
+ * STREAMS module open (entry) point
+ */
+MOD_OPEN(ahdlc_open)
+{
+ ahdlc_state_t *state;
+
+ /*
+ * Return if it's already opened
+ */
+ if (q->q_ptr) {
+ return 0;
+ }
+
+ /*
+ * This can only be opened as a module
+ */
+ if (sflag != MODOPEN) {
+ return 0;
+ }
+
+ state = (ahdlc_state_t *) ALLOC_NOSLEEP(sizeof(ahdlc_state_t));
+ if (state == 0)
+ OPEN_ERROR(ENOSR);
+ bzero((caddr_t) state, sizeof(ahdlc_state_t));
+
+ q->q_ptr = (caddr_t) state;
+ WR(q)->q_ptr = (caddr_t) state;
+
+#if defined(USE_MUTEX)
+ mutex_init(&state->lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+ state->xaccm[0] = ~0; /* escape 0x00 through 0x1f */
+ state->xaccm[3] = 0x60000000; /* escape 0x7d and 0x7e */
+ state->mru = PPP_MRU; /* default of 1500 bytes */
+#if defined(SOL2)
+ state->flag_time = drv_usectohz(FLAG_TIME);
+#endif /* SOL2 */
+
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+
+#if defined(SUNOS4)
+ ppp_ahdlc_count++;
+#endif /* SUNOS4 */
+
+ qprocson(q);
+
+ return 0;
+}
+
+/*
+ * STREAMS module close (exit) point
+ */
+MOD_CLOSE(ahdlc_close)
+{
+ ahdlc_state_t *state;
+
+ qprocsoff(q);
+
+ state = (ahdlc_state_t *) q->q_ptr;
+
+ if (state == 0) {
+ DPRINT("state == 0 in ahdlc_close\n");
+ return 0;
+ }
+
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+ if (state->rx_buf != 0) {
+ freemsg(state->rx_buf);
+ state->rx_buf = 0;
+ }
+
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+ mutex_destroy(&state->lock);
+#endif /* USE_MUTEX */
+
+ FREE(q->q_ptr, sizeof(ahdlc_state_t));
+ q->q_ptr = NULL;
+ OTHERQ(q)->q_ptr = NULL;
+
+#if defined(SUNOS4)
+ if (ppp_ahdlc_count)
+ ppp_ahdlc_count--;
+#endif /* SUNOS4 */
+
+ return 0;
+}
+
+/*
+ * Write side put routine
+ */
+static int
+ahdlc_wput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ ahdlc_state_t *state;
+ struct iocblk *iop;
+ int error;
+ mblk_t *np;
+ struct ppp_stats *psp;
+
+ state = (ahdlc_state_t *) q->q_ptr;
+ if (state == 0) {
+ DPRINT("state == 0 in ahdlc_wput\n");
+ freemsg(mp);
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+ case M_DATA:
+ /*
+ * A data packet - do character-stuffing and FCS, and
+ * send it onwards.
+ */
+ ahdlc_encode(q, mp);
+ freemsg(mp);
+ break;
+
+ case M_IOCTL:
+ iop = (struct iocblk *) mp->b_rptr;
+ error = EINVAL;
+ switch (iop->ioc_cmd) {
+ case PPPIO_XACCM:
+ if ((iop->ioc_count < sizeof(u_int32_t)) ||
+ (iop->ioc_count > sizeof(ext_accm))) {
+ break;
+ }
+ if (mp->b_cont == 0) {
+ DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit);
+ break;
+ }
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
+ iop->ioc_count);
+ state->xaccm[2] &= ~0x40000000; /* don't escape 0x5e */
+ state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+
+ case PPPIO_RACCM:
+ if (iop->ioc_count != sizeof(u_int32_t))
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit);
+ break;
+ }
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
+ sizeof(u_int32_t));
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+
+ case PPPIO_GCLEAN:
+ np = allocb(sizeof(int), BPRI_HI);
+ if (np == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = np;
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ *(int *)np->b_wptr = state->flags & RCV_FLAGS;
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ np->b_wptr += sizeof(int);
+ iop->ioc_count = sizeof(int);
+ error = 0;
+ break;
+
+ case PPPIO_GETSTAT:
+ np = allocb(sizeof(struct ppp_stats), BPRI_HI);
+ if (np == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = np;
+ psp = (struct ppp_stats *) np->b_wptr;
+ np->b_wptr += sizeof(struct ppp_stats);
+ bzero((caddr_t)psp, sizeof(struct ppp_stats));
+ psp->p = state->stats;
+ iop->ioc_count = sizeof(struct ppp_stats);
+ error = 0;
+ break;
+
+ case PPPIO_LASTMOD:
+ /* we knew this anyway */
+ error = 0;
+ break;
+
+ default:
+ error = -1;
+ break;
+ }
+
+ if (error < 0)
+ putnext(q, mp);
+ else if (error == 0) {
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+ } else {
+ mp->b_datap->db_type = M_IOCNAK;
+ iop->ioc_count = 0;
+ iop->ioc_error = error;
+ qreply(q, mp);
+ }
+ break;
+
+ case M_CTL:
+ switch (*mp->b_rptr) {
+ case PPPCTL_MTU:
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ state->mtu = ((unsigned short *)mp->b_rptr)[1];
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ freemsg(mp);
+ break;
+ case PPPCTL_MRU:
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ state->mru = ((unsigned short *)mp->b_rptr)[1];
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ freemsg(mp);
+ break;
+ case PPPCTL_UNIT:
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ state->unit = mp->b_rptr[1];
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ break;
+ default:
+ putnext(q, mp);
+ }
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+
+ return 0;
+}
+
+/*
+ * Read side put routine
+ */
+static int
+ahdlc_rput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ ahdlc_state_t *state;
+
+ state = (ahdlc_state_t *) q->q_ptr;
+ if (state == 0) {
+ DPRINT("state == 0 in ahdlc_rput\n");
+ freemsg(mp);
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+ case M_DATA:
+ ahdlc_decode(q, mp);
+ freemsg(mp);
+ break;
+
+ case M_HANGUP:
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ if (state->rx_buf != 0) {
+ /* XXX would like to send this up for debugging */
+ freemsg(state->rx_buf);
+ state->rx_buf = 0;
+ }
+ state->flags = IFLUSH;
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ putnext(q, mp);
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+ return 0;
+}
+
+/*
+ * Extract bit c from map m, to determine if c needs to be escaped
+ */
+#define IN_TX_MAP(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
+
+static void
+ahdlc_encode(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ ahdlc_state_t *state;
+ u_int32_t *xaccm, loc_xaccm[8];
+ ushort_t fcs;
+ size_t outmp_len;
+ mblk_t *outmp, *tmp;
+ uchar_t *dp, fcs_val;
+ int is_lcp, code;
+#if defined(SOL2)
+ clock_t lbolt;
+#endif /* SOL2 */
+
+ if (msgdsize(mp) < 4) {
+ return;
+ }
+
+ state = (ahdlc_state_t *)q->q_ptr;
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+ /*
+ * Allocate an output buffer large enough to handle a case where all
+ * characters need to be escaped
+ */
+ outmp_len = (msgdsize(mp) << 1) + /* input block x 2 */
+ (sizeof(fcs) << 2) + /* HDLC FCS x 4 */
+ (sizeof(uchar_t) << 1); /* HDLC flags x 2 */
+
+ outmp = allocb(outmp_len, BPRI_MED);
+ if (outmp == NULL) {
+ state->stats.ppp_oerrors++;
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+ return;
+ }
+
+#if defined(SOL2)
+ /*
+ * Check if our last transmit happenned within flag_time, using
+ * the system's LBOLT value in clock ticks
+ */
+ if (drv_getparm(LBOLT, &lbolt) != -1) {
+ if (ABS((clock_t)lbolt - state->lbolt) > state->flag_time) {
+ *outmp->b_wptr++ = PPP_FLAG;
+ }
+ state->lbolt = lbolt;
+ } else {
+ *outmp->b_wptr++ = PPP_FLAG;
+ }
+#else
+ /*
+ * If the driver below still has a message to process, skip the
+ * HDLC flag, otherwise, put one in the beginning
+ */
+ if (qsize(q->q_next) == 0) {
+ *outmp->b_wptr++ = PPP_FLAG;
+ }
+#endif
+
+ /*
+ * All control characters must be escaped for LCP packets with code
+ * values between 1 (Conf-Req) and 7 (Code-Rej).
+ */
+ is_lcp = ((MSG_BYTE(mp, 0) == PPP_ALLSTATIONS) &&
+ (MSG_BYTE(mp, 1) == PPP_UI) &&
+ (MSG_BYTE(mp, 2) == (PPP_LCP >> 8)) &&
+ (MSG_BYTE(mp, 3) == (PPP_LCP & 0xff)) &&
+ LCP_USE_DFLT(mp));
+
+ xaccm = state->xaccm;
+ if (is_lcp) {
+ bcopy((caddr_t)state->xaccm, (caddr_t)loc_xaccm, sizeof(loc_xaccm));
+ loc_xaccm[0] = ~0; /* force escape on 0x00 through 0x1f */
+ xaccm = loc_xaccm;
+ }
+
+ fcs = PPP_INITFCS; /* Initial FCS is 0xffff */
+
+ /*
+ * Process this block and the rest (if any) attached to the this one
+ */
+ for (tmp = mp; tmp; tmp = tmp->b_cont) {
+ if (tmp->b_datap->db_type == M_DATA) {
+ for (dp = tmp->b_rptr; dp < tmp->b_wptr; dp++) {
+ fcs = PPP_FCS(fcs, *dp);
+ if (IN_TX_MAP(*dp, xaccm)) {
+ *outmp->b_wptr++ = PPP_ESCAPE;
+ *outmp->b_wptr++ = *dp ^ PPP_TRANS;
+ } else {
+ *outmp->b_wptr++ = *dp;
+ }
+ }
+ } else {
+ continue; /* skip if db_type is something other than M_DATA */
+ }
+ }
+
+ /*
+ * Append the HDLC FCS, making sure that escaping is done on any
+ * necessary bytes
+ */
+ fcs_val = (fcs ^ 0xffff) & 0xff;
+ if (IN_TX_MAP(fcs_val, xaccm)) {
+ *outmp->b_wptr++ = PPP_ESCAPE;
+ *outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+ } else {
+ *outmp->b_wptr++ = fcs_val;
+ }
+
+ fcs_val = ((fcs ^ 0xffff) >> 8) & 0xff;
+ if (IN_TX_MAP(fcs_val, xaccm)) {
+ *outmp->b_wptr++ = PPP_ESCAPE;
+ *outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+ } else {
+ *outmp->b_wptr++ = fcs_val;
+ }
+
+ /*
+ * And finally, append the HDLC flag, and send it away
+ */
+ *outmp->b_wptr++ = PPP_FLAG;
+
+ state->stats.ppp_obytes += msgdsize(outmp);
+ state->stats.ppp_opackets++;
+
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+
+ putnext(q, outmp);
+ return;
+}
+
+/*
+ * Checks the 32-bit receive ACCM to see if the byte needs un-escaping
+ */
+#define IN_RX_MAP(c, m) ((((unsigned int) (uchar_t) (c)) < 0x20) && \
+ (m) & (1 << (c)))
+
+
+/*
+ * Process received characters.
+ */
+static void
+ahdlc_decode(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ ahdlc_state_t *state;
+ mblk_t *om;
+ uchar_t *dp;
+ ushort_t fcs;
+#if defined(SOL2)
+ mblk_t *zmp;
+#endif /* SOL2 */
+
+#if defined(SOL2)
+ /*
+ * In case the driver (or something below) doesn't send
+ * data upstream in one message block, concatenate everything
+ */
+ if (!((mp->b_wptr - mp->b_rptr == msgdsize(mp)) &&
+ ((intpointer_t)mp->b_rptr % sizeof(intpointer_t) == 0))) {
+
+ zmp = msgpullup(mp, -1);
+ freemsg(mp);
+ mp = zmp;
+ if (mp == 0)
+ return;
+ }
+#endif /* SOL2 */
+
+ state = (ahdlc_state_t *) q->q_ptr;
+
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+ state->stats.ppp_ibytes += msgdsize(mp);
+
+ for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) {
+
+ /*
+ * This should detect the lack of 8-bit communication channel
+ * which is necessary for PPP to work. In addition, it also
+ * checks on the parity.
+ */
+ if (*dp & 0x80)
+ state->flags |= RCV_B7_1;
+ else
+ state->flags |= RCV_B7_0;
+
+ if (paritytab[*dp >> 5] & (1 << (*dp & 0x1f)))
+ state->flags |= RCV_ODDP;
+ else
+ state->flags |= RCV_EVNP;
+
+ /*
+ * So we have a HDLC flag ...
+ */
+ if (*dp == PPP_FLAG) {
+
+ /*
+ * If we think that it marks the beginning of the frame,
+ * then continue to process the next octects
+ */
+ if ((state->flags & IFLUSH) ||
+ (state->rx_buf == 0) ||
+ (msgdsize(state->rx_buf) == 0)) {
+
+ state->flags &= ~IFLUSH;
+ continue;
+ }
+
+ /*
+ * We get here because the above condition isn't true,
+ * in which case the HDLC flag was there to mark the end
+ * of the frame (or so we think)
+ */
+ om = state->rx_buf;
+
+ if (state->infcs == PPP_GOODFCS) {
+ state->stats.ppp_ipackets++;
+ adjmsg(om, -PPP_FCSLEN);
+ putnext(q, om);
+ } else {
+ DPRINT2("ppp%d: bad fcs (len=%d)\n",
+ state->unit, msgdsize(state->rx_buf));
+ freemsg(state->rx_buf);
+ state->flags &= ~(IFLUSH | ESCAPED);
+ state->stats.ppp_ierrors++;
+ putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+ }
+
+ state->rx_buf = 0;
+ continue;
+ }
+
+ if (state->flags & IFLUSH) {
+ continue;
+ }
+
+ /*
+ * Allocate a receive buffer, large enough to store a frame (after
+ * un-escaping) of at least 1500 octets. If MRU is negotiated to
+ * be more than the default, then allocate that much. In addition,
+ * we add an extra 32-bytes for a fudge factor
+ */
+ if (state->rx_buf == 0) {
+ state->rx_buf_size = (state->mru < PPP_MRU ? PPP_MRU : state->mru);
+ state->rx_buf_size += (sizeof(u_int32_t) << 3);
+ state->rx_buf = allocb(state->rx_buf_size, BPRI_MED);
+
+ /*
+ * If allocation fails, try again on the next frame
+ */
+ if (state->rx_buf == 0) {
+ state->flags |= IFLUSH;
+ continue;
+ }
+ state->flags &= ~(IFLUSH | ESCAPED);
+ state->infcs = PPP_INITFCS;
+ }
+
+ if (*dp == PPP_ESCAPE) {
+ state->flags |= ESCAPED;
+ continue;
+ }
+
+ /*
+ * Make sure we un-escape the necessary characters, as well as the
+ * ones in our receive async control character map
+ */
+ if (state->flags & ESCAPED) {
+ *dp ^= PPP_TRANS;
+ state->flags &= ~ESCAPED;
+ } else if (IN_RX_MAP(*dp, state->raccm))
+ continue;
+
+ /*
+ * Unless the peer lied to us about the negotiated MRU, we should
+ * never get a frame which is too long. If it happens, toss it away
+ * and grab the next incoming one
+ */
+ if (msgdsize(state->rx_buf) < state->rx_buf_size) {
+ state->infcs = PPP_FCS(state->infcs, *dp);
+ *state->rx_buf->b_wptr++ = *dp;
+ } else {
+ DPRINT2("ppp%d: frame too long (%d)\n",
+ state->unit, msgdsize(state->rx_buf));
+ freemsg(state->rx_buf);
+ state->rx_buf = 0;
+ state->flags |= IFLUSH;
+ }
+ }
+
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+}
+
+static int
+msg_byte(mp, i)
+ mblk_t *mp;
+ unsigned int i;
+{
+ while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
+ mp = mp->b_cont;
+ if (mp == 0)
+ return -1;
+ return mp->b_rptr[i];
+}
diff --git a/mdk-stage1/ppp/modules/ppp_comp.c b/mdk-stage1/ppp/modules/ppp_comp.c
new file mode 100644
index 000000000..f6eef5ab1
--- /dev/null
+++ b/mdk-stage1/ppp/modules/ppp_comp.c
@@ -0,0 +1,1126 @@
+/*
+ * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/stream.h>
+
+#ifdef SVR4
+#include <sys/conf.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#else
+#include <sys/user.h>
+#ifdef __osf__
+#include <sys/cmn_err.h>
+#endif
+#endif /* SVR4 */
+
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include "ppp_mod.h"
+
+#ifdef __osf__
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <net/vjcompress.h>
+
+#define PACKETPTR mblk_t *
+#include <net/ppp-comp.h>
+
+MOD_OPEN_DECL(ppp_comp_open);
+MOD_CLOSE_DECL(ppp_comp_close);
+static int ppp_comp_rput __P((queue_t *, mblk_t *));
+static int ppp_comp_rsrv __P((queue_t *));
+static int ppp_comp_wput __P((queue_t *, mblk_t *));
+static int ppp_comp_wsrv __P((queue_t *));
+static void ppp_comp_ccp __P((queue_t *, mblk_t *, int));
+static int msg_byte __P((mblk_t *, unsigned int));
+
+/* Extract byte i of message mp. */
+#define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
+ msg_byte((mp), (i)))
+
+/* Is this LCP packet one we have to transmit using LCP defaults? */
+#define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
+
+#define PPP_COMP_ID 0xbadf
+static struct module_info minfo = {
+#ifdef PRIOQ
+ PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16512, 16384,
+#else
+ PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16384, 4096,
+#endif
+};
+
+static struct qinit r_init = {
+ ppp_comp_rput, ppp_comp_rsrv, ppp_comp_open, ppp_comp_close,
+ NULL, &minfo, NULL
+};
+
+static struct qinit w_init = {
+ ppp_comp_wput, ppp_comp_wsrv, NULL, NULL, NULL, &minfo, NULL
+};
+
+#if defined(SVR4) && !defined(SOL2)
+int pcmpdevflag = 0;
+#define ppp_compinfo pcmpinfo
+#endif
+struct streamtab ppp_compinfo = {
+ &r_init, &w_init, NULL, NULL
+};
+
+int ppp_comp_count; /* number of module instances in use */
+
+#ifdef __osf__
+
+static void ppp_comp_alloc __P((comp_state_t *));
+typedef struct memreq {
+ unsigned char comp_opts[20];
+ int cmd;
+ int thread_status;
+ char *returned_mem;
+} memreq_t;
+
+#endif
+
+typedef struct comp_state {
+ int flags;
+ int mru;
+ int mtu;
+ int unit;
+ struct compressor *xcomp;
+ void *xstate;
+ struct compressor *rcomp;
+ void *rstate;
+ struct vjcompress vj_comp;
+ int vj_last_ierrors;
+ struct pppstat stats;
+#ifdef __osf__
+ memreq_t memreq;
+ thread_t thread;
+#endif
+} comp_state_t;
+
+
+#ifdef __osf__
+extern task_t first_task;
+#endif
+
+/* Bits in flags are as defined in pppio.h. */
+#define CCP_ERR (CCP_ERROR | CCP_FATALERROR)
+#define LAST_MOD 0x1000000 /* no ppp modules below us */
+#define DBGLOG 0x2000000 /* log debugging stuff */
+
+#define MAX_IPHDR 128 /* max TCP/IP header size */
+#define MAX_VJHDR 20 /* max VJ compressed header size (?) */
+
+#undef MIN /* just in case */
+#define MIN(a, b) ((a) < (b)? (a): (b))
+
+/*
+ * List of compressors we know about.
+ */
+
+#if DO_BSD_COMPRESS
+extern struct compressor ppp_bsd_compress;
+#endif
+#if DO_DEFLATE
+extern struct compressor ppp_deflate, ppp_deflate_draft;
+#endif
+
+struct compressor *ppp_compressors[] = {
+#if DO_BSD_COMPRESS
+ &ppp_bsd_compress,
+#endif
+#if DO_DEFLATE
+ &ppp_deflate,
+ &ppp_deflate_draft,
+#endif
+ NULL
+};
+
+/*
+ * STREAMS module entry points.
+ */
+MOD_OPEN(ppp_comp_open)
+{
+ comp_state_t *cp;
+#ifdef __osf__
+ thread_t thread;
+#endif
+
+ if (q->q_ptr == NULL) {
+ cp = (comp_state_t *) ALLOC_SLEEP(sizeof(comp_state_t));
+ if (cp == NULL)
+ OPEN_ERROR(ENOSR);
+ bzero((caddr_t)cp, sizeof(comp_state_t));
+ WR(q)->q_ptr = q->q_ptr = (caddr_t) cp;
+ cp->mru = PPP_MRU;
+ cp->mtu = PPP_MTU;
+ cp->xstate = NULL;
+ cp->rstate = NULL;
+ vj_compress_init(&cp->vj_comp, -1);
+#ifdef __osf__
+ if (!(thread = kernel_thread_w_arg(first_task, ppp_comp_alloc, (void *)cp)))
+ OPEN_ERROR(ENOSR);
+ cp->thread = thread;
+#endif
+ ++ppp_comp_count;
+ qprocson(q);
+ }
+ return 0;
+}
+
+MOD_CLOSE(ppp_comp_close)
+{
+ comp_state_t *cp;
+
+ qprocsoff(q);
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp != NULL) {
+ if (cp->xstate != NULL)
+ (*cp->xcomp->comp_free)(cp->xstate);
+ if (cp->rstate != NULL)
+ (*cp->rcomp->decomp_free)(cp->rstate);
+#ifdef __osf__
+ if (!cp->thread)
+ printf("ppp_comp_close: NULL thread!\n");
+ else
+ thread_terminate(cp->thread);
+#endif
+ FREE(cp, sizeof(comp_state_t));
+ q->q_ptr = NULL;
+ OTHERQ(q)->q_ptr = NULL;
+ --ppp_comp_count;
+ }
+ return 0;
+}
+
+#ifdef __osf__
+
+/* thread for calling back to a compressor's memory allocator
+ * Needed for Digital UNIX since it's VM can't handle requests
+ * for large amounts of memory without blocking. The thread
+ * provides a context in which we can call a memory allocator
+ * that may block.
+ */
+static void
+ppp_comp_alloc(comp_state_t *cp)
+{
+ int len, cmd;
+ unsigned char *compressor_options;
+ thread_t thread;
+ void *(*comp_allocator)();
+
+
+#if defined(MAJOR_VERSION) && (MAJOR_VERSION <= 2)
+
+ /* In 2.x and earlier the argument gets passed
+ * in the thread structure itself. Yuck.
+ */
+ thread = current_thread();
+ cp = thread->reply_port;
+ thread->reply_port = PORT_NULL;
+
+#endif
+
+ for (;;) {
+ assert_wait((vm_offset_t)&cp->memreq.thread_status, TRUE);
+ thread_block();
+
+ if (thread_should_halt(current_thread()))
+ thread_halt_self();
+ cmd = cp->memreq.cmd;
+ compressor_options = &cp->memreq.comp_opts[0];
+ len = compressor_options[1];
+ if (cmd == PPPIO_XCOMP) {
+ cp->memreq.returned_mem = cp->xcomp->comp_alloc(compressor_options, len);
+ if (!cp->memreq.returned_mem) {
+ cp->memreq.thread_status = ENOSR;
+ } else {
+ cp->memreq.thread_status = 0;
+ }
+ } else {
+ cp->memreq.returned_mem = cp->rcomp->decomp_alloc(compressor_options, len);
+ if (!cp->memreq.returned_mem) {
+ cp->memreq.thread_status = ENOSR;
+ } else {
+ cp->memreq.thread_status = 0;
+ }
+ }
+ }
+}
+
+#endif /* __osf__ */
+
+/* here's the deal with memory allocation under Digital UNIX.
+ * Some other may also benefit from this...
+ * We can't ask for huge chunks of memory in a context where
+ * the caller can't be put to sleep (like, here.) The alloc
+ * is likely to fail. Instead we do this: the first time we
+ * get called, kick off a thread to do the allocation. Return
+ * immediately to the caller with EAGAIN, as an indication that
+ * they should send down the ioctl again. By the time the
+ * second call comes in it's likely that the memory allocation
+ * thread will have returned with the requested memory. We will
+ * continue to return EAGAIN however until the thread has completed.
+ * When it has, we return zero (and the memory) if the allocator
+ * was successful and ENOSR otherwise.
+ *
+ * Callers of the RCOMP and XCOMP ioctls are encouraged (but not
+ * required) to loop for some number of iterations with a small
+ * delay in the loop body (for instance a 1/10-th second "sleep"
+ * via select.)
+ */
+static int
+ppp_comp_wput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ struct iocblk *iop;
+ comp_state_t *cp;
+ int error, len, n;
+ int flags, mask;
+ mblk_t *np;
+ struct compressor **comp;
+ struct ppp_stats *psp;
+ struct ppp_comp_stats *csp;
+ unsigned char *opt_data;
+ int nxslots, nrslots;
+
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp == 0) {
+ DPRINT("cp == 0 in ppp_comp_wput\n");
+ freemsg(mp);
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+
+ case M_DATA:
+ putq(q, mp);
+ break;
+
+ case M_IOCTL:
+ iop = (struct iocblk *) mp->b_rptr;
+ error = EINVAL;
+ switch (iop->ioc_cmd) {
+
+ case PPPIO_CFLAGS:
+ /* set/get CCP state */
+ if (iop->ioc_count != 2 * sizeof(int))
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ppp_comp_wput/%d: PPPIO_CFLAGS b_cont = 0!\n", cp->unit);
+ break;
+ }
+ flags = ((int *) mp->b_cont->b_rptr)[0];
+ mask = ((int *) mp->b_cont->b_rptr)[1];
+ cp->flags = (cp->flags & ~mask) | (flags & mask);
+ if ((mask & CCP_ISOPEN) && (flags & CCP_ISOPEN) == 0) {
+ if (cp->xstate != NULL) {
+ (*cp->xcomp->comp_free)(cp->xstate);
+ cp->xstate = NULL;
+ }
+ if (cp->rstate != NULL) {
+ (*cp->rcomp->decomp_free)(cp->rstate);
+ cp->rstate = NULL;
+ }
+ cp->flags &= ~CCP_ISUP;
+ }
+ error = 0;
+ iop->ioc_count = sizeof(int);
+ ((int *) mp->b_cont->b_rptr)[0] = cp->flags;
+ mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int);
+ break;
+
+ case PPPIO_VJINIT:
+ /*
+ * Initialize VJ compressor/decompressor
+ */
+ if (iop->ioc_count != 2)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ppp_comp_wput/%d: PPPIO_VJINIT b_cont = 0!\n", cp->unit);
+ break;
+ }
+ nxslots = mp->b_cont->b_rptr[0] + 1;
+ nrslots = mp->b_cont->b_rptr[1] + 1;
+ if (nxslots > MAX_STATES || nrslots > MAX_STATES)
+ break;
+ vj_compress_init(&cp->vj_comp, nxslots);
+ cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+ error = 0;
+ iop->ioc_count = 0;
+ break;
+
+ case PPPIO_XCOMP:
+ case PPPIO_RCOMP:
+ if (iop->ioc_count <= 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ppp_comp_wput/%d: PPPIO_[XR]COMP b_cont = 0!\n", cp->unit);
+ break;
+ }
+ opt_data = mp->b_cont->b_rptr;
+ len = mp->b_cont->b_wptr - opt_data;
+ if (len > iop->ioc_count)
+ len = iop->ioc_count;
+ if (opt_data[1] < 2 || opt_data[1] > len)
+ break;
+ for (comp = ppp_compressors; *comp != NULL; ++comp)
+ if ((*comp)->compress_proto == opt_data[0]) {
+ /* here's the handler! */
+ error = 0;
+#ifndef __osf__
+ if (iop->ioc_cmd == PPPIO_XCOMP) {
+ /* A previous call may have fetched memory for a compressor
+ * that's now being retired or reset. Free it using it's
+ * mechanism for freeing stuff.
+ */
+ if (cp->xstate != NULL) {
+ (*cp->xcomp->comp_free)(cp->xstate);
+ cp->xstate = NULL;
+ }
+ cp->xcomp = *comp;
+ cp->xstate = (*comp)->comp_alloc(opt_data, len);
+ if (cp->xstate == NULL)
+ error = ENOSR;
+ } else {
+ if (cp->rstate != NULL) {
+ (*cp->rcomp->decomp_free)(cp->rstate);
+ cp->rstate = NULL;
+ }
+ cp->rcomp = *comp;
+ cp->rstate = (*comp)->decomp_alloc(opt_data, len);
+ if (cp->rstate == NULL)
+ error = ENOSR;
+ }
+#else
+ if ((error = cp->memreq.thread_status) != EAGAIN)
+ if (iop->ioc_cmd == PPPIO_XCOMP) {
+ if (cp->xstate) {
+ (*cp->xcomp->comp_free)(cp->xstate);
+ cp->xstate = 0;
+ }
+ /* sanity check for compressor options
+ */
+ if (sizeof (cp->memreq.comp_opts) < len) {
+ printf("can't handle options for compressor %d (%d)\n", opt_data[0],
+ opt_data[1]);
+ cp->memreq.thread_status = ENOSR;
+ cp->memreq.returned_mem = 0;
+ }
+ /* fill in request for the thread and kick it off
+ */
+ if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) {
+ bcopy(opt_data, cp->memreq.comp_opts, len);
+ cp->memreq.cmd = PPPIO_XCOMP;
+ cp->xcomp = *comp;
+ error = cp->memreq.thread_status = EAGAIN;
+ thread_wakeup((vm_offset_t)&cp->memreq.thread_status);
+ } else {
+ cp->xstate = cp->memreq.returned_mem;
+ cp->memreq.returned_mem = 0;
+ cp->memreq.thread_status = 0;
+ }
+ } else {
+ if (cp->rstate) {
+ (*cp->rcomp->decomp_free)(cp->rstate);
+ cp->rstate = NULL;
+ }
+ if (sizeof (cp->memreq.comp_opts) < len) {
+ printf("can't handle options for compressor %d (%d)\n", opt_data[0],
+ opt_data[1]);
+ cp->memreq.thread_status = ENOSR;
+ cp->memreq.returned_mem = 0;
+ }
+ if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) {
+ bcopy(opt_data, cp->memreq.comp_opts, len);
+ cp->memreq.cmd = PPPIO_RCOMP;
+ cp->rcomp = *comp;
+ error = cp->memreq.thread_status = EAGAIN;
+ thread_wakeup((vm_offset_t)&cp->memreq.thread_status);
+ } else {
+ cp->rstate = cp->memreq.returned_mem;
+ cp->memreq.returned_mem = 0;
+ cp->memreq.thread_status = 0;
+ }
+ }
+#endif
+ break;
+ }
+ iop->ioc_count = 0;
+ break;
+
+ case PPPIO_GETSTAT:
+ if ((cp->flags & LAST_MOD) == 0) {
+ error = -1; /* let the ppp_ahdl module handle it */
+ break;
+ }
+ np = allocb(sizeof(struct ppp_stats), BPRI_HI);
+ if (np == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = np;
+ psp = (struct ppp_stats *) np->b_wptr;
+ np->b_wptr += sizeof(struct ppp_stats);
+ iop->ioc_count = sizeof(struct ppp_stats);
+ psp->p = cp->stats;
+ psp->vj = cp->vj_comp.stats;
+ error = 0;
+ break;
+
+ case PPPIO_GETCSTAT:
+ np = allocb(sizeof(struct ppp_comp_stats), BPRI_HI);
+ if (np == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = np;
+ csp = (struct ppp_comp_stats *) np->b_wptr;
+ np->b_wptr += sizeof(struct ppp_comp_stats);
+ iop->ioc_count = sizeof(struct ppp_comp_stats);
+ bzero((caddr_t)csp, sizeof(struct ppp_comp_stats));
+ if (cp->xstate != 0)
+ (*cp->xcomp->comp_stat)(cp->xstate, &csp->c);
+ if (cp->rstate != 0)
+ (*cp->rcomp->decomp_stat)(cp->rstate, &csp->d);
+ error = 0;
+ break;
+
+ case PPPIO_DEBUG:
+ if (iop->ioc_count != sizeof(int))
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ppp_comp_wput/%d: PPPIO_DEBUG b_cont = 0!\n", cp->unit);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ if (n == PPPDBG_LOG + PPPDBG_COMP) {
+ DPRINT1("ppp_comp%d: debug log enabled\n", cp->unit);
+ cp->flags |= DBGLOG;
+ error = 0;
+ iop->ioc_count = 0;
+ } else {
+ error = -1;
+ }
+ break;
+
+ case PPPIO_LASTMOD:
+ cp->flags |= LAST_MOD;
+ error = 0;
+ break;
+
+ default:
+ error = -1;
+ break;
+ }
+
+ if (error < 0)
+ putnext(q, mp);
+ else if (error == 0) {
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+ } else {
+ mp->b_datap->db_type = M_IOCNAK;
+ iop->ioc_error = error;
+ iop->ioc_count = 0;
+ qreply(q, mp);
+ }
+ break;
+
+ case M_CTL:
+ switch (*mp->b_rptr) {
+ case PPPCTL_MTU:
+ cp->mtu = ((unsigned short *)mp->b_rptr)[1];
+ break;
+ case PPPCTL_MRU:
+ cp->mru = ((unsigned short *)mp->b_rptr)[1];
+ break;
+ case PPPCTL_UNIT:
+ cp->unit = mp->b_rptr[1];
+ break;
+ }
+ putnext(q, mp);
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+
+ return 0;
+}
+
+static int
+ppp_comp_wsrv(q)
+ queue_t *q;
+{
+ mblk_t *mp, *cmp = NULL;
+ comp_state_t *cp;
+ int len, proto, type, hlen, code;
+ struct ip *ip;
+ unsigned char *vjhdr, *dp;
+
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp == 0) {
+ DPRINT("cp == 0 in ppp_comp_wsrv\n");
+ return 0;
+ }
+
+ while ((mp = getq(q)) != 0) {
+ /* assert(mp->b_datap->db_type == M_DATA) */
+#ifdef PRIOQ
+ if (!bcanputnext(q,mp->b_band))
+#else
+ if (!canputnext(q))
+#endif PRIOQ
+ {
+ putbq(q, mp);
+ break;
+ }
+
+ /*
+ * First check the packet length and work out what the protocol is.
+ */
+ len = msgdsize(mp);
+ if (len < PPP_HDRLEN) {
+ DPRINT1("ppp_comp_wsrv: bogus short packet (%d)\n", len);
+ freemsg(mp);
+ cp->stats.ppp_oerrors++;
+ putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+ continue;
+ }
+ proto = (MSG_BYTE(mp, 2) << 8) + MSG_BYTE(mp, 3);
+
+ /*
+ * Make sure we've got enough data in the first mblk
+ * and that we are its only user.
+ */
+ if (proto == PPP_CCP)
+ hlen = len;
+ else if (proto == PPP_IP)
+ hlen = PPP_HDRLEN + MAX_IPHDR;
+ else
+ hlen = PPP_HDRLEN;
+ if (hlen > len)
+ hlen = len;
+ if (mp->b_wptr < mp->b_rptr + hlen || mp->b_datap->db_ref > 1) {
+ PULLUP(mp, hlen);
+ if (mp == 0) {
+ DPRINT1("ppp_comp_wsrv: pullup failed (%d)\n", hlen);
+ cp->stats.ppp_oerrors++;
+ putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+ continue;
+ }
+ }
+
+ /*
+ * Do VJ compression if requested.
+ */
+ if (proto == PPP_IP && (cp->flags & COMP_VJC)) {
+ ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN);
+ if (ip->ip_p == IPPROTO_TCP) {
+ type = vj_compress_tcp(ip, len - PPP_HDRLEN, &cp->vj_comp,
+ (cp->flags & COMP_VJCCID), &vjhdr);
+ switch (type) {
+ case TYPE_UNCOMPRESSED_TCP:
+ mp->b_rptr[3] = proto = PPP_VJC_UNCOMP;
+ break;
+ case TYPE_COMPRESSED_TCP:
+ dp = vjhdr - PPP_HDRLEN;
+ dp[1] = mp->b_rptr[1]; /* copy control field */
+ dp[0] = mp->b_rptr[0]; /* copy address field */
+ dp[2] = 0; /* set protocol field */
+ dp[3] = proto = PPP_VJC_COMP;
+ mp->b_rptr = dp;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Do packet compression if enabled.
+ */
+ if (proto == PPP_CCP)
+ ppp_comp_ccp(q, mp, 0);
+ else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN)
+ && cp->xstate != NULL) {
+ len = msgdsize(mp);
+ (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len,
+ (cp->flags & CCP_ISUP? cp->mtu + PPP_HDRLEN: 0));
+ if (cmp != NULL) {
+#ifdef PRIOQ
+ cmp->b_band=mp->b_band;
+#endif PRIOQ
+ freemsg(mp);
+ mp = cmp;
+ }
+ }
+
+ /*
+ * Do address/control and protocol compression if enabled.
+ */
+ if ((cp->flags & COMP_AC)
+ && !(proto == PPP_LCP && LCP_USE_DFLT(mp))) {
+ mp->b_rptr += 2; /* drop the address & ctrl fields */
+ if (proto < 0x100 && (cp->flags & COMP_PROT))
+ ++mp->b_rptr; /* drop the high protocol byte */
+ } else if (proto < 0x100 && (cp->flags & COMP_PROT)) {
+ /* shuffle up the address & ctrl fields */
+ mp->b_rptr[2] = mp->b_rptr[1];
+ mp->b_rptr[1] = mp->b_rptr[0];
+ ++mp->b_rptr;
+ }
+
+ cp->stats.ppp_opackets++;
+ cp->stats.ppp_obytes += msgdsize(mp);
+ putnext(q, mp);
+ }
+
+ return 0;
+}
+
+static int
+ppp_comp_rput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ comp_state_t *cp;
+ struct iocblk *iop;
+ struct ppp_stats *psp;
+
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp == 0) {
+ DPRINT("cp == 0 in ppp_comp_rput\n");
+ freemsg(mp);
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+
+ case M_DATA:
+ putq(q, mp);
+ break;
+
+ case M_IOCACK:
+ iop = (struct iocblk *) mp->b_rptr;
+ switch (iop->ioc_cmd) {
+ case PPPIO_GETSTAT:
+ /*
+ * Catch this on the way back from the ppp_ahdl module
+ * so we can fill in the VJ stats.
+ */
+ if (mp->b_cont == 0 || iop->ioc_count != sizeof(struct ppp_stats))
+ break;
+ psp = (struct ppp_stats *) mp->b_cont->b_rptr;
+ psp->vj = cp->vj_comp.stats;
+ break;
+ }
+ putnext(q, mp);
+ break;
+
+ case M_CTL:
+ switch (mp->b_rptr[0]) {
+ case PPPCTL_IERROR:
+ ++cp->stats.ppp_ierrors;
+ break;
+ case PPPCTL_OERROR:
+ ++cp->stats.ppp_oerrors;
+ break;
+ }
+ putnext(q, mp);
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+
+ return 0;
+}
+
+static int
+ppp_comp_rsrv(q)
+ queue_t *q;
+{
+ int proto, rv, i;
+ mblk_t *mp, *dmp = NULL, *np;
+ uchar_t *dp, *iphdr;
+ comp_state_t *cp;
+ int len, hlen, vjlen;
+ u_int iphlen;
+
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp == 0) {
+ DPRINT("cp == 0 in ppp_comp_rsrv\n");
+ return 0;
+ }
+
+ while ((mp = getq(q)) != 0) {
+ /* assert(mp->b_datap->db_type == M_DATA) */
+ if (!canputnext(q)) {
+ putbq(q, mp);
+ break;
+ }
+
+ len = msgdsize(mp);
+ cp->stats.ppp_ibytes += len;
+ cp->stats.ppp_ipackets++;
+
+ /*
+ * First work out the protocol and where the PPP header ends.
+ */
+ i = 0;
+ proto = MSG_BYTE(mp, 0);
+ if (proto == PPP_ALLSTATIONS) {
+ i = 2;
+ proto = MSG_BYTE(mp, 2);
+ }
+ if ((proto & 1) == 0) {
+ ++i;
+ proto = (proto << 8) + MSG_BYTE(mp, i);
+ }
+ hlen = i + 1;
+
+ /*
+ * Now reconstruct a complete, contiguous PPP header at the
+ * start of the packet.
+ */
+ if (hlen < ((cp->flags & DECOMP_AC)? 0: 2)
+ + ((cp->flags & DECOMP_PROT)? 1: 2)) {
+ /* count these? */
+ goto bad;
+ }
+ if (mp->b_rptr + hlen > mp->b_wptr) {
+ adjmsg(mp, hlen); /* XXX check this call */
+ hlen = 0;
+ }
+ if (hlen != PPP_HDRLEN) {
+ /*
+ * We need to put some bytes on the front of the packet
+ * to make a full-length PPP header.
+ * If we can put them in *mp, we do, otherwise we
+ * tack another mblk on the front.
+ * XXX we really shouldn't need to carry around
+ * the address and control at this stage.
+ */
+ dp = mp->b_rptr + hlen - PPP_HDRLEN;
+ if (dp < mp->b_datap->db_base || mp->b_datap->db_ref > 1) {
+ np = allocb(PPP_HDRLEN, BPRI_MED);
+ if (np == 0)
+ goto bad;
+ np->b_cont = mp;
+ mp->b_rptr += hlen;
+ mp = np;
+ dp = mp->b_wptr;
+ mp->b_wptr += PPP_HDRLEN;
+ } else
+ mp->b_rptr = dp;
+
+ dp[0] = PPP_ALLSTATIONS;
+ dp[1] = PPP_UI;
+ dp[2] = proto >> 8;
+ dp[3] = proto;
+ }
+
+ /*
+ * Now see if we have a compressed packet to decompress,
+ * or a CCP packet to take notice of.
+ */
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ if (proto == PPP_CCP) {
+ len = msgdsize(mp);
+ if (mp->b_wptr < mp->b_rptr + len) {
+ PULLUP(mp, len);
+ if (mp == 0)
+ goto bad;
+ }
+ ppp_comp_ccp(q, mp, 1);
+ } else if (proto == PPP_COMP) {
+ if ((cp->flags & CCP_ISUP)
+ && (cp->flags & CCP_DECOMP_RUN) && cp->rstate
+ && (cp->flags & CCP_ERR) == 0) {
+ rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp);
+ switch (rv) {
+ case DECOMP_OK:
+ freemsg(mp);
+ mp = dmp;
+ if (mp == NULL) {
+ /* no error, but no packet returned either. */
+ continue;
+ }
+ break;
+ case DECOMP_ERROR:
+ cp->flags |= CCP_ERROR;
+ ++cp->stats.ppp_ierrors;
+ putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+ break;
+ case DECOMP_FATALERROR:
+ cp->flags |= CCP_FATALERROR;
+ ++cp->stats.ppp_ierrors;
+ putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+ break;
+ }
+ }
+ } else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
+ (*cp->rcomp->incomp)(cp->rstate, mp);
+ }
+
+ /*
+ * Now do VJ decompression.
+ */
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) {
+ len = msgdsize(mp) - PPP_HDRLEN;
+ if ((cp->flags & DECOMP_VJC) == 0 || len <= 0)
+ goto bad;
+
+ /*
+ * Advance past the ppp header.
+ * Here we assume that the whole PPP header is in the first mblk.
+ */
+ np = mp;
+ dp = np->b_rptr + PPP_HDRLEN;
+ if (dp >= mp->b_wptr) {
+ np = np->b_cont;
+ dp = np->b_rptr;
+ }
+
+ /*
+ * Make sure we have sufficient contiguous data at this point.
+ */
+ hlen = (proto == PPP_VJC_COMP)? MAX_VJHDR: MAX_IPHDR;
+ if (hlen > len)
+ hlen = len;
+ if (np->b_wptr < dp + hlen || np->b_datap->db_ref > 1) {
+ PULLUP(mp, hlen + PPP_HDRLEN);
+ if (mp == 0)
+ goto bad;
+ np = mp;
+ dp = np->b_rptr + PPP_HDRLEN;
+ }
+
+ if (proto == PPP_VJC_COMP) {
+ /*
+ * Decompress VJ-compressed packet.
+ * First reset compressor if an input error has occurred.
+ */
+ if (cp->stats.ppp_ierrors != cp->vj_last_ierrors) {
+ if (cp->flags & DBGLOG)
+ DPRINT1("ppp%d: resetting VJ\n", cp->unit);
+ vj_uncompress_err(&cp->vj_comp);
+ cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+ }
+
+ vjlen = vj_uncompress_tcp(dp, np->b_wptr - dp, len,
+ &cp->vj_comp, &iphdr, &iphlen);
+ if (vjlen < 0) {
+ if (cp->flags & DBGLOG)
+ DPRINT2("ppp%d: vj_uncomp_tcp failed, pkt len %d\n",
+ cp->unit, len);
+ ++cp->vj_last_ierrors; /* so we don't reset next time */
+ goto bad;
+ }
+
+ /* drop ppp and vj headers off */
+ if (mp != np) {
+ freeb(mp);
+ mp = np;
+ }
+ mp->b_rptr = dp + vjlen;
+
+ /* allocate a new mblk for the ppp and ip headers */
+ if ((np = allocb(iphlen + PPP_HDRLEN + 4, BPRI_MED)) == 0)
+ goto bad;
+ dp = np->b_rptr; /* prepend mblk with TCP/IP hdr */
+ dp[0] = PPP_ALLSTATIONS; /* reconstruct PPP header */
+ dp[1] = PPP_UI;
+ dp[2] = PPP_IP >> 8;
+ dp[3] = PPP_IP;
+ bcopy((caddr_t)iphdr, (caddr_t)dp + PPP_HDRLEN, iphlen);
+ np->b_wptr = dp + iphlen + PPP_HDRLEN;
+ np->b_cont = mp;
+
+ /* XXX there seems to be a bug which causes panics in strread
+ if we make an mbuf with only the IP header in it :-( */
+ if (mp->b_wptr - mp->b_rptr > 4) {
+ bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, 4);
+ mp->b_rptr += 4;
+ np->b_wptr += 4;
+ } else {
+ bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr,
+ mp->b_wptr - mp->b_rptr);
+ np->b_wptr += mp->b_wptr - mp->b_rptr;
+ np->b_cont = mp->b_cont;
+ freeb(mp);
+ }
+
+ mp = np;
+
+ } else {
+ /*
+ * "Decompress" a VJ-uncompressed packet.
+ */
+ cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+ if (!vj_uncompress_uncomp(dp, hlen, &cp->vj_comp)) {
+ if (cp->flags & DBGLOG)
+ DPRINT2("ppp%d: vj_uncomp_uncomp failed, pkt len %d\n",
+ cp->unit, len);
+ ++cp->vj_last_ierrors; /* don't need to reset next time */
+ goto bad;
+ }
+ mp->b_rptr[3] = PPP_IP; /* fix up the PPP protocol field */
+ }
+ }
+
+ putnext(q, mp);
+ continue;
+
+ bad:
+ if (mp != 0)
+ freemsg(mp);
+ cp->stats.ppp_ierrors++;
+ putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+ }
+
+ return 0;
+}
+
+/*
+ * Handle a CCP packet being sent or received.
+ * Here all the data in the packet is in a single mbuf.
+ */
+static void
+ppp_comp_ccp(q, mp, rcvd)
+ queue_t *q;
+ mblk_t *mp;
+ int rcvd;
+{
+ int len, clen;
+ comp_state_t *cp;
+ unsigned char *dp;
+
+ len = msgdsize(mp);
+ if (len < PPP_HDRLEN + CCP_HDRLEN)
+ return;
+
+ cp = (comp_state_t *) q->q_ptr;
+ dp = mp->b_rptr + PPP_HDRLEN;
+ len -= PPP_HDRLEN;
+ clen = CCP_LENGTH(dp);
+ if (clen > len)
+ return;
+
+ switch (CCP_CODE(dp)) {
+ case CCP_CONFREQ:
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+ cp->flags &= ~CCP_ISUP;
+ break;
+
+ case CCP_CONFACK:
+ if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN
+ && clen >= CCP_HDRLEN + CCP_OPT_MINLEN
+ && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) {
+ if (!rcvd) {
+ if (cp->xstate != NULL
+ && (*cp->xcomp->comp_init)
+ (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
+ cp->unit, 0, ((cp->flags & DBGLOG) != 0)))
+ cp->flags |= CCP_COMP_RUN;
+ } else {
+ if (cp->rstate != NULL
+ && (*cp->rcomp->decomp_init)
+ (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
+ cp->unit, 0, cp->mru, ((cp->flags & DBGLOG) != 0)))
+ cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
+ }
+ }
+ break;
+
+ case CCP_RESETACK:
+ if (cp->flags & CCP_ISUP) {
+ if (!rcvd) {
+ if (cp->xstate && (cp->flags & CCP_COMP_RUN))
+ (*cp->xcomp->comp_reset)(cp->xstate);
+ } else {
+ if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
+ (*cp->rcomp->decomp_reset)(cp->rstate);
+ cp->flags &= ~CCP_ERROR;
+ }
+ }
+ }
+ break;
+ }
+}
+
+#if 0
+dump_msg(mp)
+ mblk_t *mp;
+{
+ dblk_t *db;
+
+ while (mp != 0) {
+ db = mp->b_datap;
+ DPRINT2("mp=%x cont=%x ", mp, mp->b_cont);
+ DPRINT3("rptr=%x wptr=%x datap=%x\n", mp->b_rptr, mp->b_wptr, db);
+ DPRINT2(" base=%x lim=%x", db->db_base, db->db_lim);
+ DPRINT2(" ref=%d type=%d\n", db->db_ref, db->db_type);
+ mp = mp->b_cont;
+ }
+}
+#endif
+
+static int
+msg_byte(mp, i)
+ mblk_t *mp;
+ unsigned int i;
+{
+ while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
+ mp = mp->b_cont;
+ if (mp == 0)
+ return -1;
+ return mp->b_rptr[i];
+}
diff --git a/mdk-stage1/ppp/modules/ppp_mod.h b/mdk-stage1/ppp/modules/ppp_mod.h
new file mode 100644
index 000000000..f0af00886
--- /dev/null
+++ b/mdk-stage1/ppp/modules/ppp_mod.h
@@ -0,0 +1,190 @@
+/*
+ * Miscellaneous definitions for PPP STREAMS modules.
+ */
+
+/*
+ * Macros for allocating and freeing kernel memory.
+ */
+#ifdef SVR4 /* SVR4, including Solaris 2 */
+#include <sys/kmem.h>
+#define ALLOC_SLEEP(n) kmem_alloc((n), KM_SLEEP)
+#define ALLOC_NOSLEEP(n) kmem_alloc((n), KM_NOSLEEP)
+#define FREE(p, n) kmem_free((p), (n))
+#endif
+
+#ifdef SUNOS4
+#include <sys/kmem_alloc.h> /* SunOS 4.x */
+#define ALLOC_SLEEP(n) kmem_alloc((n), KMEM_SLEEP)
+#define ALLOC_NOSLEEP(n) kmem_alloc((n), KMEM_NOSLEEP)
+#define FREE(p, n) kmem_free((p), (n))
+#define NOTSUSER() (suser()? 0: EPERM)
+#define bcanputnext(q, band) canputnext((q))
+#endif /* SunOS 4 */
+
+#ifdef __osf__
+#include <sys/malloc.h>
+
+/* caution: this mirrors macros in sys/malloc.h, and uses interfaces
+ * which are subject to change.
+ * The problems are that:
+ * - the official MALLOC macro wants the lhs of the assignment as an argument,
+ * and it takes care of the assignment itself (yuck.)
+ * - PPP insists on using "FREE" which conflicts with a macro of the same name.
+ *
+ */
+#ifdef BUCKETINDX /* V2.0 */
+#define ALLOC_SLEEP(n) (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_WAITOK)
+#define ALLOC_NOSLEEP(n) (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_NOWAIT)
+#else
+#define ALLOC_SLEEP(n) (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_WAITOK)
+#define ALLOC_NOSLEEP(n) (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_NOWAIT)
+#endif
+
+#define bcanputnext(q, band) canputnext((q))
+
+#ifdef FREE
+#undef FREE
+#endif
+#define FREE(p, n) free((void *)(p), M_DEVBUF)
+
+#define NO_DLPI 1
+
+#ifndef IFT_PPP
+#define IFT_PPP 0x17
+#endif
+
+#include <sys/proc.h>
+#define NOTSUSER() (suser(u.u_procp->p_rcred, &u.u_acflag) ? EPERM : 0)
+
+/* #include "ppp_osf.h" */
+
+#endif /* __osf__ */
+
+#ifdef AIX4
+#define ALLOC_SLEEP(n) xmalloc((n), 0, pinned_heap) /* AIX V4.x */
+#define ALLOC_NOSLEEP(n) xmalloc((n), 0, pinned_heap) /* AIX V4.x */
+#define FREE(p, n) xmfree((p), pinned_heap)
+#define NOTSUSER() (suser()? 0: EPERM)
+#endif /* AIX */
+
+/*
+ * Macros for printing debugging stuff.
+ */
+#ifdef DEBUG
+#if defined(SVR4) || defined(__osf__)
+#if defined(SNI)
+#include <sys/strlog.h>
+#define STRLOG_ID 4712
+#define DPRINT(f) strlog(STRLOG_ID, 0, 0, SL_TRACE, f)
+#define DPRINT1(f, a1) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1)
+#define DPRINT2(f, a1, a2) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2)
+#define DPRINT3(f, a1, a2, a3) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2, a3)
+#else
+#define DPRINT(f) cmn_err(CE_CONT, f)
+#define DPRINT1(f, a1) cmn_err(CE_CONT, f, a1)
+#define DPRINT2(f, a1, a2) cmn_err(CE_CONT, f, a1, a2)
+#define DPRINT3(f, a1, a2, a3) cmn_err(CE_CONT, f, a1, a2, a3)
+#endif /* SNI */
+#else
+#define DPRINT(f) printf(f)
+#define DPRINT1(f, a1) printf(f, a1)
+#define DPRINT2(f, a1, a2) printf(f, a1, a2)
+#define DPRINT3(f, a1, a2, a3) printf(f, a1, a2, a3)
+#endif /* SVR4 or OSF */
+
+#else
+#define DPRINT(f) 0
+#define DPRINT1(f, a1) 0
+#define DPRINT2(f, a1, a2) 0
+#define DPRINT3(f, a1, a2, a3) 0
+#endif /* DEBUG */
+
+#ifndef SVR4
+typedef unsigned char uchar_t;
+typedef unsigned short ushort_t;
+#ifndef __osf__
+typedef int minor_t;
+#endif
+#endif
+
+/*
+ * If we don't have multithreading support, define substitutes.
+ */
+#ifndef D_MP
+# define qprocson(q)
+# define qprocsoff(q)
+# define put(q, mp) ((*(q)->q_qinfo->qi_putp)((q), (mp)))
+# define canputnext(q) canput((q)->q_next)
+# define qwriter(q, mp, func, scope) (func)((q), (mp))
+#endif
+
+#ifdef D_MP
+/* Use msgpullup if we have other multithreading support. */
+#define PULLUP(mp, len) \
+ do { \
+ mblk_t *np = msgpullup((mp), (len)); \
+ freemsg((mp)); \
+ mp = np; \
+ } while (0)
+
+#else
+/* Use pullupmsg if we don't have any multithreading support. */
+#define PULLUP(mp, len) \
+ do { \
+ if (!pullupmsg((mp), (len))) { \
+ freemsg((mp)); \
+ mp = 0; \
+ } \
+ } while (0)
+#endif
+
+/*
+ * How to declare the open and close procedures for a module.
+ */
+#ifdef SVR4
+#define MOD_OPEN_DECL(name) \
+static int name __P((queue_t *, dev_t *, int, int, cred_t *))
+
+#define MOD_CLOSE_DECL(name) \
+static int name __P((queue_t *, int, cred_t *))
+
+#define MOD_OPEN(name) \
+static int name(q, devp, flag, sflag, credp) \
+ queue_t *q; \
+ dev_t *devp; \
+ int flag, sflag; \
+ cred_t *credp;
+
+#define MOD_CLOSE(name) \
+static int name(q, flag, credp) \
+ queue_t *q; \
+ int flag; \
+ cred_t *credp;
+
+#define OPEN_ERROR(x) return (x)
+#define DRV_OPEN_OK(dev) return 0
+
+#define NOTSUSER() (drv_priv(credp))
+
+#else /* not SVR4 */
+#define MOD_OPEN_DECL(name) \
+static int name __P((queue_t *, int, int, int))
+
+#define MOD_CLOSE_DECL(name) \
+static int name __P((queue_t *, int))
+
+#define MOD_OPEN(name) \
+static int name(q, dev, flag, sflag) \
+ queue_t *q; \
+ int dev; \
+ int flag, sflag;
+
+#define MOD_CLOSE(name) \
+static int name(q, flag) \
+ queue_t *q; \
+ int flag;
+
+#define OPEN_ERROR(x) { u.u_error = (x); return OPENFAIL; }
+#define DRV_OPEN_OK(dev) return (dev)
+
+#endif /* SVR4 */
diff --git a/mdk-stage1/ppp/modules/vjcompress.c b/mdk-stage1/ppp/modules/vjcompress.c
new file mode 100644
index 000000000..809b87231
--- /dev/null
+++ b/mdk-stage1/ppp/modules/vjcompress.c
@@ -0,0 +1,587 @@
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ *
+ * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
+ * so that the entire packet being decompressed doesn't have
+ * to be in contiguous memory (just the compressed header).
+ */
+
+/*
+ * This version is used under SunOS 4.x, Digital UNIX, AIX 4.x,
+ * and SVR4 systems including Solaris 2.
+ *
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef SVR4
+#ifndef __GNUC__
+#include <sys/byteorder.h> /* for ntohl, etc. */
+#else
+/* make sure we don't get the gnu "fixed" one! */
+#include "/usr/include/sys/byteorder.h"
+#endif
+#endif
+
+#ifdef __osf__
+#include <net/net_globals.h>
+#endif
+#include <netinet/in.h>
+
+#ifdef AIX4
+#define _NETINET_IN_SYSTM_H_
+typedef u_long n_long;
+#else
+#include <netinet/in_systm.h>
+#endif
+
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <net/ppp_defs.h>
+#include <net/vjcompress.h>
+
+#ifndef VJ_NO_STATS
+#define INCR(counter) ++comp->stats.counter
+#else
+#define INCR(counter)
+#endif
+
+#define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n))
+#undef BCOPY
+#define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n))
+#ifndef KERNEL
+#define ovbcopy bcopy
+#endif
+
+#ifdef __osf__
+#define getip_hl(base) (((base).ip_vhl)&0xf)
+#define getth_off(base) ((((base).th_xoff)&0xf0)>>4)
+
+#else
+#define getip_hl(base) ((base).ip_hl)
+#define getth_off(base) ((base).th_off)
+#endif
+
+void
+vj_compress_init(comp, max_state)
+ struct vjcompress *comp;
+ int max_state;
+{
+ register u_int i;
+ register struct cstate *tstate = comp->tstate;
+
+ if (max_state == -1)
+ max_state = MAX_STATES - 1;
+ bzero((char *)comp, sizeof(*comp));
+ for (i = max_state; i > 0; --i) {
+ tstate[i].cs_id = i;
+ tstate[i].cs_next = &tstate[i - 1];
+ }
+ tstate[0].cs_next = &tstate[max_state];
+ tstate[0].cs_id = 0;
+ comp->last_cs = &tstate[0];
+ comp->last_recv = 255;
+ comp->last_xmit = 255;
+ comp->flags = VJF_TOSS;
+}
+
+
+/* ENCODE encodes a number that is known to be non-zero. ENCODEZ
+ * checks for zero (since zero has to be encoded in the long, 3 byte
+ * form).
+ */
+#define ENCODE(n) { \
+ if ((u_short)(n) >= 256) { \
+ *cp++ = 0; \
+ cp[1] = (n); \
+ cp[0] = (n) >> 8; \
+ cp += 2; \
+ } else { \
+ *cp++ = (n); \
+ } \
+}
+#define ENCODEZ(n) { \
+ if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
+ *cp++ = 0; \
+ cp[1] = (n); \
+ cp[0] = (n) >> 8; \
+ cp += 2; \
+ } else { \
+ *cp++ = (n); \
+ } \
+}
+
+#define DECODEL(f) { \
+ if (*cp == 0) {\
+ u_int32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \
+ (f) = htonl(tmp); \
+ cp += 3; \
+ } else { \
+ u_int32_t tmp = ntohl(f) + (u_int32_t)*cp++; \
+ (f) = htonl(tmp); \
+ } \
+}
+
+#define DECODES(f) { \
+ if (*cp == 0) {\
+ u_short tmp = ntohs(f) + ((cp[1] << 8) | cp[2]); \
+ (f) = htons(tmp); \
+ cp += 3; \
+ } else { \
+ u_short tmp = ntohs(f) + (u_int32_t)*cp++; \
+ (f) = htons(tmp); \
+ } \
+}
+
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = htons((cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = htons((u_int32_t)*cp++); \
+ } \
+}
+
+u_int
+vj_compress_tcp(ip, mlen, comp, compress_cid, vjhdrp)
+ register struct ip *ip;
+ u_int mlen;
+ struct vjcompress *comp;
+ int compress_cid;
+ u_char **vjhdrp;
+{
+ register struct cstate *cs = comp->last_cs->cs_next;
+ register u_int hlen = getip_hl(*ip);
+ register struct tcphdr *oth;
+ register struct tcphdr *th;
+ register u_int deltaS, deltaA;
+ register u_int changes = 0;
+ u_char new_seq[16];
+ register u_char *cp = new_seq;
+
+ /*
+ * Bail if this is an IP fragment or if the TCP packet isn't
+ * `compressible' (i.e., ACK isn't set or some other control bit is
+ * set). (We assume that the caller has already made sure the
+ * packet is IP proto TCP).
+ */
+ if ((ip->ip_off & htons(0x3fff)) || mlen < 40)
+ return (TYPE_IP);
+
+ th = (struct tcphdr *)&((int *)ip)[hlen];
+ if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK)
+ return (TYPE_IP);
+ /*
+ * Packet is compressible -- we're going to send either a
+ * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need
+ * to locate (or create) the connection state. Special case the
+ * most recently used connection since it's most likely to be used
+ * again & we don't have to do any reordering if it's used.
+ */
+ INCR(vjs_packets);
+ if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
+ ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
+ *(int *)th != ((int *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) {
+ /*
+ * Wasn't the first -- search for it.
+ *
+ * States are kept in a circularly linked list with
+ * last_cs pointing to the end of the list. The
+ * list is kept in lru order by moving a state to the
+ * head of the list whenever it is referenced. Since
+ * the list is short and, empirically, the connection
+ * we want is almost always near the front, we locate
+ * states via linear search. If we don't find a state
+ * for the datagram, the oldest state is (re-)used.
+ */
+ register struct cstate *lcs;
+ register struct cstate *lastcs = comp->last_cs;
+
+ do {
+ lcs = cs; cs = cs->cs_next;
+ INCR(vjs_searches);
+ if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
+ && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
+ && *(int *)th == ((int *)&cs->cs_ip)[getip_hl(cs->cs_ip)])
+ goto found;
+ } while (cs != lastcs);
+
+ /*
+ * Didn't find it -- re-use oldest cstate. Send an
+ * uncompressed packet that tells the other side what
+ * connection number we're using for this conversation.
+ * Note that since the state list is circular, the oldest
+ * state points to the newest and we only need to set
+ * last_cs to update the lru linkage.
+ */
+ INCR(vjs_misses);
+ comp->last_cs = lcs;
+ hlen += getth_off(*th);
+ hlen <<= 2;
+ if (hlen > mlen)
+ return (TYPE_IP);
+ goto uncompressed;
+
+ found:
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (cs == lastcs)
+ comp->last_cs = lcs;
+ else {
+ lcs->cs_next = cs->cs_next;
+ cs->cs_next = lastcs->cs_next;
+ lastcs->cs_next = cs;
+ }
+ }
+
+ /*
+ * Make sure that only what we expect to change changed. The first
+ * line of the `if' checks the IP protocol version, header length &
+ * type of service. The 2nd line checks the "Don't fragment" bit.
+ * The 3rd line checks the time-to-live and protocol (the protocol
+ * check is unnecessary but costless). The 4th line checks the TCP
+ * header length. The 5th line checks IP options, if any. The 6th
+ * line checks TCP options, if any. If any of these things are
+ * different between the previous & current datagram, we send the
+ * current datagram `uncompressed'.
+ */
+ oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen];
+ deltaS = hlen;
+ hlen += getth_off(*th);
+ hlen <<= 2;
+ if (hlen > mlen)
+ return (TYPE_IP);
+
+ if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] ||
+ ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] ||
+ ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] ||
+ getth_off(*th) != getth_off(*oth) ||
+ (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
+ (getth_off(*th) > 5 && BCMP(th + 1, oth + 1, (getth_off(*th) - 5) << 2)))
+ goto uncompressed;
+
+ /*
+ * Figure out which of the changing fields changed. The
+ * receiver expects changes in the order: urgent, window,
+ * ack, seq (the order minimizes the number of temporaries
+ * needed in this section of code).
+ */
+ if (th->th_flags & TH_URG) {
+ deltaS = ntohs(th->th_urp);
+ ENCODEZ(deltaS);
+ changes |= NEW_U;
+ } else if (th->th_urp != oth->th_urp)
+ /* argh! URG not set but urp changed -- a sensible
+ * implementation should never do this but RFC793
+ * doesn't prohibit the change so we have to deal
+ * with it. */
+ goto uncompressed;
+
+ if ((deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) > 0) {
+ ENCODE(deltaS);
+ changes |= NEW_W;
+ }
+
+ if ((deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) > 0) {
+ if (deltaA > 0xffff)
+ goto uncompressed;
+ ENCODE(deltaA);
+ changes |= NEW_A;
+ }
+
+ if ((deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) > 0) {
+ if (deltaS > 0xffff)
+ goto uncompressed;
+ ENCODE(deltaS);
+ changes |= NEW_S;
+ }
+
+ switch(changes) {
+
+ case 0:
+ /*
+ * Nothing changed. If this packet contains data and the
+ * last one didn't, this is probably a data packet following
+ * an ack (normal on an interactive connection) and we send
+ * it compressed. Otherwise it's probably a retransmit,
+ * retransmitted ack or window probe. Send it uncompressed
+ * in case the other side missed the compressed version.
+ */
+ if (ip->ip_len != cs->cs_ip.ip_len &&
+ ntohs(cs->cs_ip.ip_len) == hlen)
+ break;
+
+ /* (fall through) */
+
+ case SPECIAL_I:
+ case SPECIAL_D:
+ /*
+ * actual changes match one of our special case encodings --
+ * send packet uncompressed.
+ */
+ goto uncompressed;
+
+ case NEW_S|NEW_A:
+ if (deltaS == deltaA && deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ /* special case for echoed terminal traffic */
+ changes = SPECIAL_I;
+ cp = new_seq;
+ }
+ break;
+
+ case NEW_S:
+ if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ }
+
+ deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
+ if (deltaS != 1) {
+ ENCODEZ(deltaS);
+ changes |= NEW_I;
+ }
+ if (th->th_flags & TH_PUSH)
+ changes |= TCP_PUSH_BIT;
+ /*
+ * Grab the cksum before we overwrite it below. Then update our
+ * state with this packet's header.
+ */
+ deltaA = ntohs(th->th_sum);
+ BCOPY(ip, &cs->cs_ip, hlen);
+
+ /*
+ * We want to use the original packet as our compressed packet.
+ * (cp - new_seq) is the number of bytes we need for compressed
+ * sequence numbers. In addition we need one byte for the change
+ * mask, one for the connection id and two for the tcp checksum.
+ * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how
+ * many bytes of the original packet to toss so subtract the two to
+ * get the new packet size.
+ */
+ deltaS = cp - new_seq;
+ cp = (u_char *)ip;
+ if (compress_cid == 0 || comp->last_xmit != cs->cs_id) {
+ comp->last_xmit = cs->cs_id;
+ hlen -= deltaS + 4;
+ *vjhdrp = (cp += hlen);
+ *cp++ = changes | NEW_C;
+ *cp++ = cs->cs_id;
+ } else {
+ hlen -= deltaS + 3;
+ *vjhdrp = (cp += hlen);
+ *cp++ = changes;
+ }
+ *cp++ = deltaA >> 8;
+ *cp++ = deltaA;
+ BCOPY(new_seq, cp, deltaS);
+ INCR(vjs_compressed);
+ return (TYPE_COMPRESSED_TCP);
+
+ /*
+ * Update connection state cs & send uncompressed packet (that is,
+ * a regular ip/tcp packet but with the 'conversation id' we hope
+ * to use on future compressed packets in the protocol field).
+ */
+ uncompressed:
+ BCOPY(ip, &cs->cs_ip, hlen);
+ ip->ip_p = cs->cs_id;
+ comp->last_xmit = cs->cs_id;
+ return (TYPE_UNCOMPRESSED_TCP);
+}
+
+/*
+ * Called when we may have missed a packet.
+ */
+void
+vj_uncompress_err(comp)
+ struct vjcompress *comp;
+{
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+}
+
+/*
+ * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP.
+ */
+int
+vj_uncompress_uncomp(buf, buflen, comp)
+ u_char *buf;
+ int buflen;
+ struct vjcompress *comp;
+{
+ register u_int hlen;
+ register struct cstate *cs;
+ register struct ip *ip;
+
+ ip = (struct ip *) buf;
+ hlen = getip_hl(*ip) << 2;
+ if (ip->ip_p >= MAX_STATES
+ || hlen + sizeof(struct tcphdr) > buflen
+ || (hlen += getth_off(*((struct tcphdr *)&((char *)ip)[hlen])) << 2)
+ > buflen
+ || hlen > MAX_HDR) {
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+ return (0);
+ }
+ cs = &comp->rstate[comp->last_recv = ip->ip_p];
+ comp->flags &=~ VJF_TOSS;
+ ip->ip_p = IPPROTO_TCP;
+ BCOPY(ip, &cs->cs_ip, hlen);
+ cs->cs_hlen = hlen;
+ INCR(vjs_uncompressedin);
+ return (1);
+}
+
+/*
+ * Uncompress a packet of type TYPE_COMPRESSED_TCP.
+ * The packet starts at buf and is of total length total_len.
+ * The first buflen bytes are at buf; this must include the entire
+ * compressed TCP/IP header. This procedure returns the length
+ * of the VJ header, with a pointer to the uncompressed IP header
+ * in *hdrp and its length in *hlenp.
+ */
+int
+vj_uncompress_tcp(buf, buflen, total_len, comp, hdrp, hlenp)
+ u_char *buf;
+ int buflen, total_len;
+ struct vjcompress *comp;
+ u_char **hdrp;
+ u_int *hlenp;
+{
+ register u_char *cp;
+ register u_int hlen, changes;
+ register struct tcphdr *th;
+ register struct cstate *cs;
+ register u_short *bp;
+ register u_int vjlen;
+ register u_int32_t tmp;
+
+ INCR(vjs_compressedin);
+ cp = buf;
+ changes = *cp++;
+ if (changes & NEW_C) {
+ /* Make sure the state index is in range, then grab the state.
+ * If we have a good state index, clear the 'discard' flag. */
+ if (*cp >= MAX_STATES)
+ goto bad;
+
+ comp->flags &=~ VJF_TOSS;
+ comp->last_recv = *cp++;
+ } else {
+ /* this packet has an implicit state index. If we've
+ * had a line error since the last time we got an
+ * explicit state index, we have to toss the packet. */
+ if (comp->flags & VJF_TOSS) {
+ INCR(vjs_tossed);
+ return (-1);
+ }
+ }
+ cs = &comp->rstate[comp->last_recv];
+ hlen = getip_hl(cs->cs_ip) << 2;
+ th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen];
+ th->th_sum = htons((*cp << 8) | cp[1]);
+ cp += 2;
+ if (changes & TCP_PUSH_BIT)
+ th->th_flags |= TH_PUSH;
+ else
+ th->th_flags &=~ TH_PUSH;
+
+ switch (changes & SPECIALS_MASK) {
+ case SPECIAL_I:
+ {
+ register u_int32_t i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
+ /* some compilers can't nest inline assembler.. */
+ tmp = ntohl(th->th_ack) + i;
+ th->th_ack = htonl(tmp);
+ tmp = ntohl(th->th_seq) + i;
+ th->th_seq = htonl(tmp);
+ }
+ break;
+
+ case SPECIAL_D:
+ /* some compilers can't nest inline assembler.. */
+ tmp = ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
+ th->th_seq = htonl(tmp);
+ break;
+
+ default:
+ if (changes & NEW_U) {
+ th->th_flags |= TH_URG;
+ DECODEU(th->th_urp);
+ } else
+ th->th_flags &=~ TH_URG;
+ if (changes & NEW_W)
+ DECODES(th->th_win);
+ if (changes & NEW_A)
+ DECODEL(th->th_ack);
+ if (changes & NEW_S)
+ DECODEL(th->th_seq);
+ break;
+ }
+ if (changes & NEW_I) {
+ DECODES(cs->cs_ip.ip_id);
+ } else {
+ cs->cs_ip.ip_id = ntohs(cs->cs_ip.ip_id) + 1;
+ cs->cs_ip.ip_id = htons(cs->cs_ip.ip_id);
+ }
+
+ /*
+ * At this point, cp points to the first byte of data in the
+ * packet. Fill in the IP total length and update the IP
+ * header checksum.
+ */
+ vjlen = cp - buf;
+ buflen -= vjlen;
+ if (buflen < 0)
+ /* we must have dropped some characters (crc should detect
+ * this but the old slip framing won't) */
+ goto bad;
+
+ total_len += cs->cs_hlen - vjlen;
+ cs->cs_ip.ip_len = htons(total_len);
+
+ /* recompute the ip header checksum */
+ bp = (u_short *) &cs->cs_ip;
+ cs->cs_ip.ip_sum = 0;
+ for (changes = 0; hlen > 0; hlen -= 2)
+ changes += *bp++;
+ changes = (changes & 0xffff) + (changes >> 16);
+ changes = (changes & 0xffff) + (changes >> 16);
+ cs->cs_ip.ip_sum = ~ changes;
+
+ *hdrp = (u_char *) &cs->cs_ip;
+ *hlenp = cs->cs_hlen;
+ return vjlen;
+
+ bad:
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+ return (-1);
+}
diff --git a/mdk-stage1/ppp/pppd/Makefile.linux b/mdk-stage1/ppp/pppd/Makefile.linux
new file mode 100644
index 000000000..47d6ba01e
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/Makefile.linux
@@ -0,0 +1,129 @@
+#
+# pppd makefile for Linux
+# $Id$
+#
+
+# Default installation locations
+BINDIR = $(DESTDIR)/usr/sbin
+MANDIR = $(DESTDIR)/usr/man
+
+PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
+ ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
+ demand.c utils.c multilink.c tdb.c tty.c
+HEADERS = callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
+ ipxcp.h cbcp.h tdb.h
+MANPAGES = pppd.8
+PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
+ auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \
+ tdb.o tty.o
+
+all: pppd
+
+#
+# include dependancies if present and backup if as a header file
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+CC = gcc
+#
+COPTS = -Wall $(RPM_OPT_FLAGS)
+LIBS = -lutil
+
+ifneq ($(wildcard /usr/lib/libcrypt.*),)
+LIBS += -lcrypt
+endif
+
+# Uncomment the next 2 lines to include support for Microsoft's
+# MS-CHAP authentication protocol.
+CHAPMS=y
+USE_CRYPT=y
+ifneq ($(wildcard /usr/lib/libcrypt.*),)
+HAVE_CRYPT_H=y
+endif
+
+# Uncomment the next line to include support for PPP packet filtering.
+# This requires that the libpcap library and headers be installed
+# and that the kernel driver support PPP packet filtering, which it
+# doesn't yet.
+#FILTER=y
+
+HAS_SHADOW=y
+USE_PAM=y
+#HAVE_INET6=y
+
+PLUGIN=y
+
+INCLUDE_DIRS= -I../include
+
+COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK -DHAVE_MMAP
+
+CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
+
+ifdef CHAPMS
+CFLAGS += -DCHAPMS=1
+ifndef USE_CRYPT
+LIBS := -ldes $(LIBS)
+else
+CFLAGS += -DUSE_CRYPT=1
+ifneq ($(wildcard /usr/include/crypt.h),)
+CFLAGS += -DHAVE_CRYPT_H=1
+endif
+endif
+PPPDOBJS += md4.o chap_ms.o
+ifdef MSLANMAN
+CFLAGS += -DMSLANMAN=1
+endif
+endif
+
+ifdef HAS_SHADOW
+CFLAGS += -DHAS_SHADOW
+#LIBS := -lshadow $(LIBS)
+endif
+
+# For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/.
+ifdef USE_PAM
+CFLAGS += -DUSE_PAM
+LIBS := -lpam -ldl $(LIBS)
+endif
+
+# Lock library binary for Linux is included in 'linux' subdirectory.
+ifdef LOCKLIB
+LIBS := -llock $(LIBS)
+CFLAGS += -DLOCKLIB=1
+endif
+
+ifdef PLUGIN
+CFLAGS += -DPLUGIN
+LDFLAGS += -Wl,-E
+LIBS += -ldl
+endif
+
+ifdef FILTER
+LIBS += -lpcap
+CFLAGS += -DPPP_FILTER -I/usr/include/pcap
+endif
+
+ifdef HAVE_INET6
+ PPPDSRCS += ipv6cp.c eui64.c
+ HEADERS += ipv6cp.h eui64.h
+ PPPDOBJS += ipv6cp.o eui64.o
+ CFLAGS += -DINET6=1
+endif
+
+
+INSTALL= install
+
+install: pppd
+ mkdir -p $(BINDIR) $(MANDIR)
+ $(INSTALL) -m 555 pppd $(BINDIR)/pppd
+ $(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8
+
+pppd: $(PPPDOBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS)
+
+clean:
+ rm -f $(PPPDOBJS) pppd *~ #* core
+
+depend:
+ $(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend
diff --git a/mdk-stage1/ppp/pppd/Makefile.linux.make b/mdk-stage1/ppp/pppd/Makefile.linux.make
new file mode 100644
index 000000000..d98a32562
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/Makefile.linux.make
@@ -0,0 +1,131 @@
+#
+# pppd makefile for Linux
+# $Id$
+#
+
+# Default installation locations
+BINDIR = /usr/sbin
+MANDIR = /usr/man
+
+PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
+ ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
+ demand.c utils.c multilink.c tdb.c tty.c
+HEADERS = callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
+ ipxcp.h cbcp.h tdb.h
+MANPAGES = pppd.8
+PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
+ auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \
+ tdb.o tty.o
+
+all: pppd
+
+#
+# include dependancies if present and backup if as a header file
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+# CC = gcc
+#
+COPTS = -O2 -pipe -Wall -g
+LIBS =
+
+ifneq ($(wildcard /usr/lib/libcrypt.*),)
+LIBS += -lcrypt
+endif
+
+# Uncomment the next 2 lines to include support for Microsoft's
+# MS-CHAP authentication protocol.
+CHAPMS=y
+USE_CRYPT=y
+ifneq ($(wildcard /usr/lib/libcrypt.*),)
+HAVE_CRYPT_H=y
+endif
+
+# Uncomment the next line to include support for PPP packet filtering.
+# This requires that the libpcap library and headers be installed
+# and that the kernel driver support PPP packet filtering, which it
+# doesn't yet.
+#FILTER=y
+
+HAS_SHADOW=y
+#USE_PAM=y
+#HAVE_INET6=y
+
+PLUGIN=y
+
+INCLUDE_DIRS= -I../include
+
+COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK -DHAVE_MMAP
+
+CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
+
+ifdef CHAPMS
+CFLAGS += -DCHAPMS=1
+ifndef USE_CRYPT
+LIBS := -ldes $(LIBS)
+else
+CFLAGS += -DUSE_CRYPT=1
+ifneq ($(wildcard /usr/include/crypt.h),)
+CFLAGS += -DHAVE_CRYPT_H=1
+endif
+endif
+PPPDOBJS += md4.o chap_ms.o
+ifdef MSLANMAN
+CFLAGS += -DMSLANMAN=1
+endif
+endif
+
+ifdef HAS_SHADOW
+CFLAGS += -DHAS_SHADOW
+#LIBS := -lshadow $(LIBS)
+endif
+
+# For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/.
+ifdef USE_PAM
+CFLAGS += -DUSE_PAM
+LIBS := -lpam -ldl $(LIBS)
+endif
+
+# Lock library binary for Linux is included in 'linux' subdirectory.
+ifdef LOCKLIB
+LIBS := -llock $(LIBS)
+CFLAGS += -DLOCKLIB=1
+endif
+
+ifdef PLUGIN
+CFLAGS += -DPLUGIN
+LDFLAGS += -Wl,-E
+LIBS += -ldl
+endif
+
+ifdef FILTER
+LIBS += -lpcap
+CFLAGS += -DPPP_FILTER -I/usr/include/pcap
+endif
+
+ifdef HAVE_INET6
+ PPPDSRCS += ipv6cp.c eui64.c
+ HEADERS += ipv6cp.h eui64.h
+ PPPDOBJS += ipv6cp.o eui64.o
+ CFLAGS += -DINET6=1
+endif
+
+
+INSTALL= install -o root
+
+install: pppd
+ mkdir -p $(BINDIR) $(MANDIR)
+ $(INSTALL) -s -c -m 555 pppd $(BINDIR)/pppd
+ if chgrp pppusers $(BINDIR)/pppd 2>/dev/null; then \
+ chmod o-rx,u+s $(BINDIR)/pppd; fi
+ $(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8
+
+pppd: $(PPPDOBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS)
+
+clean:
+ rm -f $(PPPDOBJS) pppd *~ #* core
+
+depend:
+ $(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend
diff --git a/mdk-stage1/ppp/pppd/Makefile.linux.makeopt b/mdk-stage1/ppp/pppd/Makefile.linux.makeopt
new file mode 100644
index 000000000..3094c941c
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/Makefile.linux.makeopt
@@ -0,0 +1,129 @@
+#
+# pppd makefile for Linux
+# $Id$
+#
+
+# Default installation locations
+BINDIR = $(DESTDIR)/usr/sbin
+MANDIR = $(DESTDIR)/usr/man
+
+PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
+ ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
+ demand.c utils.c multilink.c tdb.c tty.c
+HEADERS = callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
+ ipxcp.h cbcp.h tdb.h
+MANPAGES = pppd.8
+PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
+ auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \
+ tdb.o tty.o
+
+all: pppd
+
+#
+# include dependancies if present and backup if as a header file
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+CC = gcc
+#
+COPTS = -O2 -pipe -Wall -g
+LIBS = -lutil
+
+ifneq ($(wildcard /usr/lib/libcrypt.*),)
+LIBS += -lcrypt
+endif
+
+# Uncomment the next 2 lines to include support for Microsoft's
+# MS-CHAP authentication protocol.
+CHAPMS=y
+USE_CRYPT=y
+ifneq ($(wildcard /usr/lib/libcrypt.*),)
+HAVE_CRYPT_H=y
+endif
+
+# Uncomment the next line to include support for PPP packet filtering.
+# This requires that the libpcap library and headers be installed
+# and that the kernel driver support PPP packet filtering, which it
+# doesn't yet.
+#FILTER=y
+
+HAS_SHADOW=y
+USE_PAM=y
+#HAVE_INET6=y
+
+PLUGIN=y
+
+INCLUDE_DIRS= -I../include
+
+COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK -DHAVE_MMAP
+
+CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
+
+ifdef CHAPMS
+CFLAGS += -DCHAPMS=1
+ifndef USE_CRYPT
+LIBS := -ldes $(LIBS)
+else
+CFLAGS += -DUSE_CRYPT=1
+ifneq ($(wildcard /usr/include/crypt.h),)
+CFLAGS += -DHAVE_CRYPT_H=1
+endif
+endif
+PPPDOBJS += md4.o chap_ms.o
+ifdef MSLANMAN
+CFLAGS += -DMSLANMAN=1
+endif
+endif
+
+ifdef HAS_SHADOW
+CFLAGS += -DHAS_SHADOW
+#LIBS := -lshadow $(LIBS)
+endif
+
+# For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/.
+ifdef USE_PAM
+CFLAGS += -DUSE_PAM
+LIBS := -lpam -ldl $(LIBS)
+endif
+
+# Lock library binary for Linux is included in 'linux' subdirectory.
+ifdef LOCKLIB
+LIBS := -llock $(LIBS)
+CFLAGS += -DLOCKLIB=1
+endif
+
+ifdef PLUGIN
+CFLAGS += -DPLUGIN
+LDFLAGS += -Wl,-E
+LIBS += -ldl
+endif
+
+ifdef FILTER
+LIBS += -lpcap
+CFLAGS += -DPPP_FILTER -I/usr/include/pcap
+endif
+
+ifdef HAVE_INET6
+ PPPDSRCS += ipv6cp.c eui64.c
+ HEADERS += ipv6cp.h eui64.h
+ PPPDOBJS += ipv6cp.o eui64.o
+ CFLAGS += -DINET6=1
+endif
+
+
+INSTALL= install
+
+install: pppd
+ mkdir -p $(BINDIR) $(MANDIR)
+ $(INSTALL) -m 555 pppd $(BINDIR)/pppd
+ $(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8
+
+pppd: $(PPPDOBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS)
+
+clean:
+ rm -f $(PPPDOBJS) pppd *~ #* core
+
+depend:
+ $(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend
diff --git a/mdk-stage1/ppp/pppd/Makefile.sol2 b/mdk-stage1/ppp/pppd/Makefile.sol2
new file mode 100644
index 000000000..dfdcddd97
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/Makefile.sol2
@@ -0,0 +1,48 @@
+#
+# Makefile for pppd under Solaris 2.
+# $Id$
+#
+
+include ../solaris/Makedefs
+
+COPTS += -xO2 -xspace -W0,-Lt
+CFLAGS = -I../include -DSVR4 -DSOL2 $(COPTS)
+LIBS = -lsocket -lnsl
+
+OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o tty.o \
+ ccp.o auth.o options.o demand.o utils.o sys-solaris.o tdb.o
+
+#
+# uncomment the following to enable plugins
+#
+CFLAGS += -DPLUGIN
+LIBS += -ldl
+
+#
+# Solaris 8 and above accomodates /var/run, so uncomment the
+# following to place pppd process IDs on that location
+#
+#CFLAGS += -D_PATH_VARRUN='"/var/run/"'
+
+#
+# uncomment the following to enable IPv6
+#
+# Solaris 8 and on includes support for IPv6
+#
+#CFLAGS += -DINET6
+#OBJS += ipv6cp.o eui64.o
+
+#
+# Make targets
+#
+all: pppd
+
+pppd: $(OBJS)
+ $(CC) -o pppd $(OBJS) $(LIBS)
+
+install:
+ $(INSTALL) -f $(BINDIR) -m 4755 -u root pppd
+ $(INSTALL) -f $(MANDIR)/man8 -m 444 pppd.8
+
+clean:
+ rm -f $(OBJS) pppd *~ core y.tab.c y.tab.h
diff --git a/mdk-stage1/ppp/pppd/Makefile.sunos4 b/mdk-stage1/ppp/pppd/Makefile.sunos4
new file mode 100644
index 000000000..694ac341f
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/Makefile.sunos4
@@ -0,0 +1,26 @@
+#
+# Makefile for pppd under SunOS 4.
+# $Id$
+#
+
+include ../sunos4/Makedefs
+
+LIBS =
+
+CFLAGS = $(COPTS) -I../include -DSUNOS4 -DGIDSET_TYPE=int \
+ -DLOCK_DIR=\"/usr/spool/locks\"
+
+all: pppd
+
+OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
+ auth.o options.o demand.o utils.o sys-sunos4.o tty.o
+
+pppd: $(OBJS)
+ $(CC) -o pppd $(OBJS) $(LIBS)
+
+install:
+ $(INSTALL) -c -m 4555 pppd $(BINDIR)/pppd
+ $(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8/pppd.8
+
+clean:
+ rm -f $(OBJS) pppd *~ core
diff --git a/mdk-stage1/ppp/pppd/auth.c b/mdk-stage1/ppp/pppd/auth.c
new file mode 100644
index 000000000..c1912c252
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/auth.c
@@ -0,0 +1,1952 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <utmp.h>
+#include <fcntl.h>
+#if defined(_PATH_LASTLOG) && defined(_linux_)
+#include <lastlog.h>
+#endif
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#endif
+
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef PW_PPP
+#define PW_PPP PW_LOGIN
+#endif
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+#include "pathnames.h"
+
+static const char rcsid[] = RCSID;
+
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
+
+#define ISWILD(word) (word[0] == '*' && word[1] == 0)
+
+/* The name by which the peer authenticated itself to us. */
+char peer_authname[MAXNAMELEN];
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NUM_PPP];
+
+/* Set if we have successfully called plogin() */
+static int logged_in;
+
+/* List of addresses which the peer may use. */
+static struct permitted_ip *addresses[NUM_PPP];
+
+/* Wordlist giving addresses which the peer may use
+ without authenticating itself. */
+static struct wordlist *noauth_addrs;
+
+/* Extra options to apply, from the secrets file entry for the peer. */
+static struct wordlist *extra_options;
+
+/* Number of network protocols which we have opened. */
+static int num_np_open;
+
+/* Number of network protocols which have come up. */
+static int num_np_up;
+
+/* Set if we got the contents of passwd[] from the pap-secrets file. */
+static int passwd_from_file;
+
+/* Set if we require authentication only because we have a default route. */
+static bool default_auth;
+
+/* Hook to enable a plugin to control the idle time limit */
+int (*idle_time_hook) __P((struct ppp_idle *)) = NULL;
+
+/* Hook for a plugin to say whether we can possibly authenticate any peer */
+int (*pap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to check the PAP user and password */
+int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+ struct wordlist **paddrs,
+ struct wordlist **popts)) = NULL;
+
+/* Hook for a plugin to know about the PAP user logout */
+void (*pap_logout_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the PAP password for authenticating us */
+int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/*
+ * This is used to ensure that we don't start an auth-up/down
+ * script while one is already running.
+ */
+enum script_state {
+ s_down,
+ s_up
+};
+
+static enum script_state auth_state = s_down;
+static enum script_state auth_script_state = s_down;
+static pid_t auth_script_pid = 0;
+
+static int used_login; /* peer authenticated against login database */
+
+/*
+ * Option variables.
+ */
+bool uselogin = 0; /* Use /etc/passwd for checking PAP */
+bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */
+bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */
+bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */
+bool usehostname = 0; /* Use hostname for our_name */
+bool auth_required = 0; /* Always require authentication from peer */
+bool allow_any_ip = 0; /* Allow peer to use any IP address */
+bool explicit_remote = 0; /* User specified explicit remote name */
+char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+
+static char *uafname; /* name of most recent +ua file */
+
+/* Bits in auth_pending[] */
+#define PAP_WITHPEER 1
+#define PAP_PEER 2
+#define CHAP_WITHPEER 4
+#define CHAP_PEER 8
+
+extern char *crypt __P((const char *, const char *));
+
+/* Prototypes for procedures local to this file. */
+
+static void network_phase __P((int));
+static void check_idle __P((void *));
+static void connect_time_expired __P((void *));
+static int plogin __P((char *, char *, char **));
+static void plogout __P((void));
+static int null_login __P((int));
+static int get_pap_passwd __P((char *));
+static int have_pap_secret __P((int *));
+static int have_chap_secret __P((char *, char *, int, int *));
+static int ip_addr_check __P((u_int32_t, struct permitted_ip *));
+static int scan_authfile __P((FILE *, char *, char *, char *,
+ struct wordlist **, struct wordlist **,
+ char *));
+static void free_wordlist __P((struct wordlist *));
+static void auth_script __P((char *));
+static void auth_script_done __P((void *));
+static void set_allowed_addrs __P((int, struct wordlist *, struct wordlist *));
+static int some_ip_ok __P((struct wordlist *));
+static int setupapfile __P((char **));
+static int privgroup __P((char **));
+static int set_noauth_addr __P((char **));
+static void check_access __P((FILE *, char *));
+static int wordlist_count __P((struct wordlist *));
+
+/*
+ * Authentication-related options.
+ */
+option_t auth_options[] = {
+ { "auth", o_bool, &auth_required,
+ "Require authentication from peer", OPT_PRIO | 1 },
+ { "noauth", o_bool, &auth_required,
+ "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV,
+ &allow_any_ip },
+ { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer",
+ OPT_PRIOSUB | 1, &auth_required },
+ { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer",
+ OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
+ { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer",
+ OPT_PRIOSUB | 1, &auth_required },
+ { "+chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer",
+ OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
+
+ { "refuse-pap", o_bool, &refuse_pap,
+ "Don't agree to auth to peer with PAP", 1 },
+ { "-pap", o_bool, &refuse_pap,
+ "Don't allow PAP authentication with peer", OPT_ALIAS | 1 },
+
+ { "refuse-chap", o_bool, &refuse_chap,
+ "Don't agree to auth to peer with CHAP", 1 },
+ { "-chap", o_bool, &refuse_chap,
+ "Don't allow CHAP authentication with peer", OPT_ALIAS | 1 },
+
+ { "name", o_string, our_name,
+ "Set local name for authentication",
+ OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN },
+
+ { "+ua", o_special, (void *)setupapfile,
+ "Get PAP user and password from file",
+ OPT_PRIO | OPT_A2STRVAL, &uafname },
+
+ { "user", o_string, user,
+ "Set name for auth with peer", OPT_PRIO | OPT_STATIC, NULL, MAXNAMELEN },
+
+ { "password", o_string, passwd,
+ "Password for authenticating us to the peer",
+ OPT_PRIO | OPT_STATIC | OPT_HIDE, NULL, MAXSECRETLEN },
+
+ { "usehostname", o_bool, &usehostname,
+ "Must use hostname for authentication", 1 },
+
+ { "remotename", o_string, remote_name,
+ "Set remote name for authentication", OPT_PRIO | OPT_STATIC,
+ &explicit_remote, MAXNAMELEN },
+
+ { "login", o_bool, &uselogin,
+ "Use system password database for PAP", 1 },
+
+ { "papcrypt", o_bool, &cryptpap,
+ "PAP passwords are encrypted", 1 },
+
+ { "privgroup", o_special, (void *)privgroup,
+ "Allow group members to use privileged options", OPT_PRIV | OPT_A2LIST },
+
+ { "allow-ip", o_special, (void *)set_noauth_addr,
+ "Set IP address(es) which can be used without authentication",
+ OPT_PRIV | OPT_A2LIST },
+
+ { NULL }
+};
+
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(argv)
+ char **argv;
+{
+ FILE *ufile;
+ int l;
+ char u[MAXNAMELEN], p[MAXSECRETLEN];
+ char *fname;
+
+ lcp_allowoptions[0].neg_upap = 1;
+
+ /* open user info file */
+ fname = strdup(*argv);
+ if (fname == NULL)
+ novm("+ua file name");
+ seteuid(getuid());
+ ufile = fopen(fname, "r");
+ seteuid(0);
+ if (ufile == NULL) {
+ option_error("unable to open user login data file %s", fname);
+ return 0;
+ }
+ check_access(ufile, fname);
+ uafname = fname;
+
+ /* get username */
+ if (fgets(u, MAXNAMELEN - 1, ufile) == NULL
+ || fgets(p, MAXSECRETLEN - 1, ufile) == NULL){
+ option_error("unable to read user login data file %s", fname);
+ return 0;
+ }
+ fclose(ufile);
+
+ /* get rid of newlines */
+ l = strlen(u);
+ if (l > 0 && u[l-1] == '\n')
+ u[l-1] = 0;
+ l = strlen(p);
+ if (l > 0 && p[l-1] == '\n')
+ p[l-1] = 0;
+
+ if (override_value("user", option_priority, fname))
+ strlcpy(user, u, sizeof(user));
+ if (override_value("passwd", option_priority, fname))
+ strlcpy(passwd, p, sizeof(passwd));
+
+ return (1);
+}
+
+
+/*
+ * privgroup - allow members of the group to have privileged access.
+ */
+static int
+privgroup(argv)
+ char **argv;
+{
+ struct group *g;
+ int i;
+
+ g = getgrnam(*argv);
+ if (g == 0) {
+ option_error("group %s is unknown", *argv);
+ return 0;
+ }
+ for (i = 0; i < ngroups; ++i) {
+ if (groups[i] == g->gr_gid) {
+ privileged = 1;
+ break;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * set_noauth_addr - set address(es) that can be used without authentication.
+ * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets.
+ */
+static int
+set_noauth_addr(argv)
+ char **argv;
+{
+ char *addr = *argv;
+ int l = strlen(addr) + 1;
+ struct wordlist *wp;
+
+ wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+ if (wp == NULL)
+ novm("allow-ip argument");
+ wp->word = (char *) (wp + 1);
+ wp->next = noauth_addrs;
+ BCOPY(addr, wp->word, l);
+ noauth_addrs = wp;
+ return 1;
+}
+
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ * Do what's necessary to bring the physical layer up.
+ */
+void
+link_required(unit)
+ int unit;
+{
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void
+link_terminated(unit)
+ int unit;
+{
+ if (phase == PHASE_DEAD)
+ return;
+ if (pap_logout_hook) {
+ pap_logout_hook();
+ } else {
+ if (logged_in)
+ plogout();
+ }
+ new_phase(PHASE_DEAD);
+ notice("Connection terminated.");
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void
+link_down(unit)
+ int unit;
+{
+ int i;
+ struct protent *protp;
+
+ auth_state = s_down;
+ if (auth_script_state == s_up && auth_script_pid == 0) {
+ update_link_stats(unit);
+ auth_script_state = s_down;
+ auth_script(_PATH_AUTHDOWN);
+ }
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (!protp->enabled_flag)
+ continue;
+ if (protp->protocol != PPP_LCP && protp->lowerdown != NULL)
+ (*protp->lowerdown)(unit);
+ if (protp->protocol < 0xC000 && protp->close != NULL)
+ (*protp->close)(unit, "LCP down");
+ }
+ num_np_open = 0;
+ num_np_up = 0;
+ if (phase != PHASE_DEAD)
+ new_phase(PHASE_TERMINATE);
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(unit)
+ int unit;
+{
+ int auth;
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ho = &lcp_hisoptions[unit];
+ int i;
+ struct protent *protp;
+
+ /*
+ * Tell higher-level protocols that LCP is up.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol != PPP_LCP && protp->enabled_flag
+ && protp->lowerup != NULL)
+ (*protp->lowerup)(unit);
+
+ if (auth_required && !(go->neg_chap || go->neg_upap)) {
+ /*
+ * We wanted the peer to authenticate itself, and it refused:
+ * if we have some address(es) it can use without auth, fine,
+ * otherwise treat it as though it authenticated with PAP using
+ * a username * of "" and a password of "". If that's not OK,
+ * boot it out.
+ */
+ if (noauth_addrs != NULL) {
+ set_allowed_addrs(unit, NULL, NULL);
+ } else if (!wo->neg_upap || uselogin || !null_login(unit)) {
+ warn("peer refused to authenticate: terminating link");
+ lcp_close(unit, "peer refused to authenticate");
+ status = EXIT_PEER_AUTH_FAILED;
+ return;
+ }
+ }
+
+ new_phase(PHASE_AUTHENTICATE);
+ used_login = 0;
+ auth = 0;
+ if (go->neg_chap) {
+ ChapAuthPeer(unit, our_name, go->chap_mdtype);
+ auth |= CHAP_PEER;
+ } else if (go->neg_upap) {
+ upap_authpeer(unit);
+ auth |= PAP_PEER;
+ }
+ if (ho->neg_chap) {
+ ChapAuthWithPeer(unit, user, ho->chap_mdtype);
+ auth |= CHAP_WITHPEER;
+ } else if (ho->neg_upap) {
+ if (passwd[0] == 0) {
+ passwd_from_file = 1;
+ if (!get_pap_passwd(passwd))
+ error("No secret found for PAP login");
+ }
+ upap_authwithpeer(unit, user, passwd);
+ auth |= PAP_WITHPEER;
+ }
+ auth_pending[unit] = auth;
+
+ if (!auth)
+ network_phase(unit);
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void
+network_phase(unit)
+ int unit;
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+
+ /*
+ * If the peer had to authenticate, run the auth-up script now.
+ */
+ if (go->neg_chap || go->neg_upap) {
+ auth_state = s_up;
+ if (auth_script_state == s_down && auth_script_pid == 0) {
+ auth_script_state = s_up;
+ auth_script(_PATH_AUTHUP);
+ }
+ }
+
+#ifdef CBCP_SUPPORT
+ /*
+ * If we negotiated callback, do it now.
+ */
+ if (go->neg_cbcp) {
+ new_phase(PHASE_CALLBACK);
+ (*cbcp_protent.open)(unit);
+ return;
+ }
+#endif
+
+ /*
+ * Process extra options from the secrets file
+ */
+ if (extra_options) {
+ options_from_list(extra_options, 1);
+ free_wordlist(extra_options);
+ extra_options = 0;
+ }
+ start_networks();
+}
+
+void
+start_networks()
+{
+ int i;
+ struct protent *protp;
+
+ new_phase(PHASE_NETWORK);
+
+#ifdef HAVE_MULTILINK
+ if (multilink) {
+ if (mp_join_bundle()) {
+ if (updetach && !nodetach)
+ detach();
+ return;
+ }
+ }
+#endif /* HAVE_MULTILINK */
+
+#ifdef PPP_FILTER
+ if (!demand)
+ set_filters(&pass_filter, &active_filter);
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol < 0xC000 && protp->enabled_flag
+ && protp->open != NULL) {
+ (*protp->open)(0);
+ if (protp->protocol != PPP_CCP)
+ ++num_np_open;
+ }
+
+ if (num_np_open == 0)
+ /* nothing to do */
+ lcp_close(0, "No network protocols running");
+}
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void
+auth_peer_fail(unit, protocol)
+ int unit, protocol;
+{
+ /*
+ * Authentication failure: take the link down
+ */
+ lcp_close(unit, "Authentication failed");
+ status = EXIT_PEER_AUTH_FAILED;
+}
+
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(unit, protocol, name, namelen)
+ int unit, protocol;
+ char *name;
+ int namelen;
+{
+ int bit;
+
+ switch (protocol) {
+ case PPP_CHAP:
+ bit = CHAP_PEER;
+ break;
+ case PPP_PAP:
+ bit = PAP_PEER;
+ break;
+ default:
+ warn("auth_peer_success: unknown protocol %x", protocol);
+ return;
+ }
+
+ /*
+ * Save the authenticated name of the peer for later.
+ */
+ if (namelen > sizeof(peer_authname) - 1)
+ namelen = sizeof(peer_authname) - 1;
+ BCOPY(name, peer_authname, namelen);
+ peer_authname[namelen] = 0;
+ script_setenv("PEERNAME", peer_authname, 0);
+
+ /*
+ * If there is no more authentication still to be done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~bit) == 0)
+ network_phase(unit);
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void
+auth_withpeer_fail(unit, protocol)
+ int unit, protocol;
+{
+ if (passwd_from_file)
+ BZERO(passwd, MAXSECRETLEN);
+ /*
+ * We've failed to authenticate ourselves to our peer.
+ * Some servers keep sending CHAP challenges, but there
+ * is no point in persisting without any way to get updated
+ * authentication secrets.
+ */
+ lcp_close(unit, "Failed to authenticate ourselves to peer");
+ status = EXIT_AUTH_TOPEER_FAILED;
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(unit, protocol)
+ int unit, protocol;
+{
+ int bit;
+
+ switch (protocol) {
+ case PPP_CHAP:
+ bit = CHAP_WITHPEER;
+ break;
+ case PPP_PAP:
+ if (passwd_from_file)
+ BZERO(passwd, MAXSECRETLEN);
+ bit = PAP_WITHPEER;
+ break;
+ default:
+ warn("auth_withpeer_success: unknown protocol %x", protocol);
+ bit = 0;
+ }
+
+ /*
+ * If there is no more authentication still being done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~bit) == 0)
+ network_phase(unit);
+}
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void
+np_up(unit, proto)
+ int unit, proto;
+{
+ int tlim;
+
+ if (num_np_up == 0) {
+ /*
+ * At this point we consider that the link has come up successfully.
+ */
+ status = EXIT_OK;
+ unsuccess = 0;
+ new_phase(PHASE_RUNNING);
+
+ if (idle_time_hook != 0)
+ tlim = (*idle_time_hook)(NULL);
+ else
+ tlim = idle_time_limit;
+ if (tlim > 0)
+ TIMEOUT(check_idle, NULL, tlim);
+
+ /*
+ * Set a timeout to close the connection once the maximum
+ * connect time has expired.
+ */
+ if (maxconnect > 0)
+ TIMEOUT(connect_time_expired, 0, maxconnect);
+
+ /*
+ * Detach now, if the updetach option was given.
+ */
+ if (updetach && !nodetach)
+ detach();
+ }
+ ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void
+np_down(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_up == 0) {
+ UNTIMEOUT(check_idle, NULL);
+ new_phase(PHASE_NETWORK);
+ }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void
+np_finished(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_open <= 0) {
+ /* no further use for the link: shut up shop. */
+ lcp_close(0, "No network protocols running");
+ }
+}
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void
+check_idle(arg)
+ void *arg;
+{
+ struct ppp_idle idle;
+ time_t itime;
+ int tlim;
+
+ if (!get_idle_time(0, &idle))
+ return;
+ if (idle_time_hook != 0) {
+ tlim = idle_time_hook(&idle);
+ } else {
+ itime = MIN(idle.xmit_idle, idle.recv_idle);
+ tlim = idle_time_limit - itime;
+ }
+ if (tlim <= 0) {
+ /* link is idle: shut it down. */
+ notice("Terminating connection due to lack of activity.");
+ lcp_close(0, "Link inactive");
+ need_holdoff = 0;
+ status = EXIT_IDLE_TIMEOUT;
+ } else {
+ TIMEOUT(check_idle, NULL, tlim);
+ }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void
+connect_time_expired(arg)
+ void *arg;
+{
+ info("Connect time expired");
+ lcp_close(0, "Connect time expired"); /* Close connection */
+ status = EXIT_CONNECT_TIME;
+}
+
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options()
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ int can_auth;
+ int lacks_ip;
+
+ /* Default our_name to hostname, and user to our_name */
+ if (our_name[0] == 0 || usehostname)
+ strlcpy(our_name, hostname, sizeof(our_name));
+ if (user[0] == 0)
+ strlcpy(user, our_name, sizeof(user));
+
+ /*
+ * If we have a default route, require the peer to authenticate
+ * unless the noauth option was given or the real user is root.
+ */
+ if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) {
+ auth_required = 1;
+ default_auth = 1;
+ }
+
+ /* If authentication is required, ask peer for CHAP or PAP. */
+ if (auth_required) {
+ allow_any_ip = 0;
+ if (!wo->neg_chap && !wo->neg_upap) {
+ wo->neg_chap = 1;
+ wo->neg_upap = 1;
+ }
+ } else {
+ wo->neg_chap = 0;
+ wo->neg_upap = 0;
+ }
+
+ /*
+ * Check whether we have appropriate secrets to use
+ * to authenticate the peer.
+ */
+ lacks_ip = 0;
+ can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip));
+ if (!can_auth && wo->neg_chap) {
+ can_auth = have_chap_secret((explicit_remote? remote_name: NULL),
+ our_name, 1, &lacks_ip);
+ }
+
+ if (auth_required && !can_auth && noauth_addrs == NULL) {
+ if (default_auth) {
+ option_error(
+"By default the remote system is required to authenticate itself");
+ option_error(
+"(because this system has a default route to the internet)");
+ } else if (explicit_remote)
+ option_error(
+"The remote system (%s) is required to authenticate itself",
+ remote_name);
+ else
+ option_error(
+"The remote system is required to authenticate itself");
+ option_error(
+"but I couldn't find any suitable secret (password) for it to use to do so.");
+ if (lacks_ip)
+ option_error(
+"(None of the available passwords would let it use an IP address.)");
+
+ exit(1);
+ }
+}
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(unit)
+ int unit;
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[0];
+
+ ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
+ ao->neg_chap = !refuse_chap
+ && (passwd[0] != 0
+ || have_chap_secret(user, (explicit_remote? remote_name: NULL),
+ 0, NULL));
+
+ if (go->neg_upap && !uselogin && !have_pap_secret(NULL))
+ go->neg_upap = 0;
+ if (go->neg_chap) {
+ if (!have_chap_secret((explicit_remote? remote_name: NULL),
+ our_name, 1, NULL))
+ go->neg_chap = 0;
+ }
+}
+
+
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file. If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Authentication failed.
+ * UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
+ int unit;
+ char *auser;
+ int userlen;
+ char *apasswd;
+ int passwdlen;
+ char **msg;
+{
+ int ret;
+ char *filename;
+ FILE *f;
+ struct wordlist *addrs = NULL, *opts = NULL;
+ char passwd[256], user[256];
+ char secret[MAXWORDLEN];
+ static int attempts = 0;
+
+ /*
+ * Make copies of apasswd and auser, then null-terminate them.
+ * If there are unprintable characters in the password, make
+ * them visible.
+ */
+ slprintf(passwd, sizeof(passwd), "%.*v", passwdlen, apasswd);
+ slprintf(user, sizeof(user), "%.*v", userlen, auser);
+ *msg = "";
+
+ /*
+ * Check if a plugin wants to handle this.
+ */
+ if (pap_auth_hook) {
+ ret = (*pap_auth_hook)(user, passwd, msg, &addrs, &opts);
+ if (ret >= 0) {
+ if (ret)
+ set_allowed_addrs(unit, addrs, opts);
+ BZERO(passwd, sizeof(passwd));
+ if (addrs != 0)
+ free_wordlist(addrs);
+ return ret? UPAP_AUTHACK: UPAP_AUTHNAK;
+ }
+ }
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret
+ * for authenticating this user.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = opts = NULL;
+ ret = UPAP_AUTHNAK;
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ error("Can't open PAP password file %s: %m", filename);
+
+ } else {
+ check_access(f, filename);
+ if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename) < 0) {
+ warn("no PAP secret found for %s", user);
+ } else {
+ /*
+ * If the secret is "@login", it means to check
+ * the password against the login database.
+ */
+ int login_secret = strcmp(secret, "@login") == 0;
+ ret = UPAP_AUTHACK;
+ if (uselogin || login_secret) {
+ /* login option or secret is @login */
+ ret = plogin(user, passwd, msg);
+ if (ret == UPAP_AUTHNAK)
+ warn("PAP login failure for %s", user);
+ else
+ used_login = 1;
+ }
+ if (secret[0] != 0 && !login_secret) {
+ /* password given in pap-secrets - must match */
+ if ((cryptpap || strcmp(passwd, secret) != 0)
+ && strcmp(crypt(passwd, secret), secret) != 0) {
+ ret = UPAP_AUTHNAK;
+ warn("PAP authentication failure for %s", user);
+ }
+ }
+ }
+ fclose(f);
+ }
+
+ if (ret == UPAP_AUTHNAK) {
+ if (**msg == 0)
+ *msg = "Login incorrect";
+ /*
+ * XXX can we ever get here more than once??
+ * Frustrate passwd stealer programs.
+ * Allow 10 tries, but start backing off after 3 (stolen from login).
+ * On 10'th, drop the connection.
+ */
+ if (attempts++ >= 10) {
+ warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user);
+ lcp_close(unit, "login failed");
+ }
+ if (attempts > 3)
+ sleep((u_int) (attempts - 3) * 5);
+ if (opts != NULL)
+ free_wordlist(opts);
+
+ } else {
+ attempts = 0; /* Reset count */
+ if (**msg == 0)
+ *msg = "Login ok";
+ set_allowed_addrs(unit, addrs, opts);
+ }
+
+ if (addrs != NULL)
+ free_wordlist(addrs);
+ BZERO(passwd, sizeof(passwd));
+ BZERO(secret, sizeof(secret));
+
+ return ret;
+}
+
+/*
+ * This function is needed for PAM.
+ */
+
+#ifdef USE_PAM
+/* Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static char *PAM_username;
+static char *PAM_password;
+static int PAM_error = 0;
+static pam_handle_t *pamh = NULL;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static int PAM_conv (int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata_ptr)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+ reply = malloc(sizeof(struct pam_response) * num_msg);
+ if (!reply) return PAM_CONV_ERR;
+
+ for (replies = 0; replies < num_msg; replies++) {
+ switch (msg[replies]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(PAM_username);
+ /* PAM frees resp */
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(PAM_password);
+ /* PAM frees resp */
+ break;
+ case PAM_TEXT_INFO:
+ /* fall through */
+ case PAM_ERROR_MSG:
+ /* ignore it, but pam still wants a NULL response... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+ default:
+ /* Must be an error of some sort... */
+ free (reply);
+ PAM_error = 1;
+ return PAM_CONV_ERR;
+ }
+ }
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+static struct pam_conv PAM_conversation = {
+ &PAM_conv,
+ NULL
+};
+#endif /* USE_PAM */
+
+/*
+ * plogin - Check the user name and password against the system
+ * password database, and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Login failed.
+ * UPAP_AUTHACK: Login succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+
+static int
+plogin(user, passwd, msg)
+ char *user;
+ char *passwd;
+ char **msg;
+{
+ char *tty;
+
+#ifdef USE_PAM
+ int pam_error;
+
+ pam_error = pam_start ("ppp", user, &PAM_conversation, &pamh);
+ if (pam_error != PAM_SUCCESS) {
+ *msg = (char *) pam_strerror (pamh, pam_error);
+ reopen_log();
+ return UPAP_AUTHNAK;
+ }
+ /*
+ * Define the fields for the credential validation
+ */
+
+ PAM_username = user;
+ PAM_password = passwd;
+ PAM_error = 0;
+ pam_set_item (pamh, PAM_TTY, devnam); /* this might be useful to some modules */
+
+ /*
+ * Validate the user
+ */
+ pam_error = pam_authenticate (pamh, PAM_SILENT);
+ if (pam_error == PAM_SUCCESS && !PAM_error) {
+ pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
+ if (pam_error == PAM_SUCCESS)
+ pam_error = pam_open_session (pamh, PAM_SILENT);
+ }
+
+ *msg = (char *) pam_strerror (pamh, pam_error);
+
+ /*
+ * Clean up the mess
+ */
+ reopen_log(); /* apparently the PAM stuff does closelog() */
+ PAM_username = NULL;
+ PAM_password = NULL;
+ if (pam_error != PAM_SUCCESS)
+ return UPAP_AUTHNAK;
+#else /* #ifdef USE_PAM */
+
+/*
+ * Use the non-PAM methods directly
+ */
+
+#ifdef HAS_SHADOW
+ struct spwd *spwd;
+ struct spwd *getspnam();
+#endif
+ struct passwd *pw = getpwnam(user);
+
+ endpwent();
+ if (pw == NULL)
+ return (UPAP_AUTHNAK);
+
+#ifdef HAS_SHADOW
+ spwd = getspnam(user);
+ endspent();
+ if (spwd) {
+ /* check the age of the password entry */
+ long now = time(NULL) / 86400L;
+
+ if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
+ || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
+ && spwd->sp_lstchg >= 0
+ && now >= spwd->sp_lstchg + spwd->sp_max)) {
+ warn("Password for %s has expired", user);
+ return (UPAP_AUTHNAK);
+ }
+ pw->pw_passwd = spwd->sp_pwdp;
+ }
+#endif
+
+ /*
+ * If no passwd, don't let them login.
+ */
+ if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2
+ || strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0)
+ return (UPAP_AUTHNAK);
+
+#endif /* #ifdef USE_PAM */
+
+ /*
+ * Write a wtmp entry for this user.
+ */
+
+ tty = devnam;
+ if (strncmp(tty, "/dev/", 5) == 0)
+ tty += 5;
+ logwtmp(tty, user, remote_name); /* Add wtmp login entry */
+
+#if defined(_PATH_LASTLOG) && !defined(USE_PAM)
+ if (pw != (struct passwd *)NULL) {
+ struct lastlog ll;
+ int fd;
+
+ if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
+ (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
+ memset((void *)&ll, 0, sizeof(ll));
+ (void)time(&ll.ll_time);
+ (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
+ (void)write(fd, (char *)&ll, sizeof(ll));
+ (void)close(fd);
+ }
+ }
+#endif /* _PATH_LASTLOG and not USE_PAM */
+
+ info("user %s logged in", user);
+ logged_in = 1;
+
+ return (UPAP_AUTHACK);
+}
+
+/*
+ * plogout - Logout the user.
+ */
+static void
+plogout()
+{
+#ifdef USE_PAM
+ int pam_error;
+
+ if (pamh != NULL) {
+ pam_error = pam_close_session (pamh, PAM_SILENT);
+ pam_end (pamh, pam_error);
+ pamh = NULL;
+ }
+ /* Apparently the pam stuff does closelog(). */
+ reopen_log();
+#else /* ! USE_PAM */
+ char *tty;
+
+ tty = devnam;
+ if (strncmp(tty, "/dev/", 5) == 0)
+ tty += 5;
+ logwtmp(tty, "", ""); /* Wipe out utmp logout entry */
+#endif /* ! USE_PAM */
+ logged_in = 0;
+}
+
+
+/*
+ * null_login - Check if a username of "" and a password of "" are
+ * acceptable, and iff so, set the list of acceptable IP addresses
+ * and return 1.
+ */
+static int
+null_login(unit)
+ int unit;
+{
+ char *filename;
+ FILE *f;
+ int i, ret;
+ struct wordlist *addrs, *opts;
+ char secret[MAXWORDLEN];
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+
+ i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename);
+ ret = i >= 0 && secret[0] == 0;
+ BZERO(secret, sizeof(secret));
+
+ if (ret)
+ set_allowed_addrs(unit, addrs, opts);
+ else if (opts != 0)
+ free_wordlist(opts);
+ if (addrs != 0)
+ free_wordlist(addrs);
+
+ fclose(f);
+ return ret;
+}
+
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP. Returns 1 on success, 0 if no suitable password
+ * could be found.
+ * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null).
+ */
+static int
+get_pap_passwd(passwd)
+ char *passwd;
+{
+ char *filename;
+ FILE *f;
+ int ret;
+ char secret[MAXWORDLEN];
+
+ /*
+ * Check whether a plugin wants to supply this.
+ */
+ if (pap_passwd_hook) {
+ ret = (*pap_passwd_hook)(user, passwd);
+ if (ret >= 0)
+ return ret;
+ }
+
+ filename = _PATH_UPAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+ ret = scan_authfile(f, user,
+ (remote_name[0]? remote_name: NULL),
+ secret, NULL, NULL, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+ if (passwd != NULL)
+ strlcpy(passwd, secret, MAXSECRETLEN);
+ BZERO(secret, sizeof(secret));
+ return 1;
+}
+
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret(lacks_ipp)
+ int *lacks_ipp;
+{
+ FILE *f;
+ int ret;
+ char *filename;
+ struct wordlist *addrs;
+
+ /* let the plugin decide, if there is one */
+ if (pap_check_hook) {
+ ret = (*pap_check_hook)();
+ if (ret >= 0)
+ return ret;
+ }
+
+ filename = _PATH_UPAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name,
+ NULL, &addrs, NULL, filename);
+ fclose(f);
+ if (ret >= 0 && !some_ip_ok(addrs)) {
+ if (lacks_ipp != 0)
+ *lacks_ipp = 1;
+ ret = -1;
+ }
+ if (addrs != 0)
+ free_wordlist(addrs);
+
+ return ret >= 0;
+}
+
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'. Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_chap_secret(client, server, need_ip, lacks_ipp)
+ char *client;
+ char *server;
+ int need_ip;
+ int *lacks_ipp;
+{
+ FILE *f;
+ int ret;
+ char *filename;
+ struct wordlist *addrs;
+
+ filename = _PATH_CHAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ if (client != NULL && client[0] == 0)
+ client = NULL;
+ else if (server != NULL && server[0] == 0)
+ server = NULL;
+
+ ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename);
+ fclose(f);
+ if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+ if (lacks_ipp != 0)
+ *lacks_ipp = 1;
+ ret = -1;
+ }
+ if (addrs != 0)
+ free_wordlist(addrs);
+
+ return ret >= 0;
+}
+
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_secret(unit, client, server, secret, secret_len, am_server)
+ int unit;
+ char *client;
+ char *server;
+ char *secret;
+ int *secret_len;
+ int am_server;
+{
+ FILE *f;
+ int ret, len;
+ char *filename;
+ struct wordlist *addrs, *opts;
+ char secbuf[MAXWORDLEN];
+
+ if (!am_server && passwd[0] != 0) {
+ strlcpy(secbuf, passwd, sizeof(secbuf));
+ } else {
+ filename = _PATH_CHAPFILE;
+ addrs = NULL;
+ secbuf[0] = 0;
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ error("Can't open chap secret file %s: %m", filename);
+ return 0;
+ }
+ check_access(f, filename);
+
+ ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+
+ if (am_server)
+ set_allowed_addrs(unit, addrs, opts);
+ else if (opts != 0)
+ free_wordlist(opts);
+ if (addrs != 0)
+ free_wordlist(addrs);
+ }
+
+ len = strlen(secbuf);
+ if (len > MAXSECRETLEN) {
+ error("Secret for %s on %s is too long", client, server);
+ len = MAXSECRETLEN;
+ }
+ BCOPY(secbuf, secret, len);
+ BZERO(secbuf, sizeof(secbuf));
+ *secret_len = len;
+
+ return 1;
+}
+
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ * Also looks for `--' indicating options to apply for this peer
+ * and leaves the following words in extra_options.
+ */
+static void
+set_allowed_addrs(unit, addrs, opts)
+ int unit;
+ struct wordlist *addrs;
+ struct wordlist *opts;
+{
+ int n;
+ struct wordlist *ap, **plink;
+ struct permitted_ip *ip;
+ char *ptr_word, *ptr_mask;
+ struct hostent *hp;
+ struct netent *np;
+ u_int32_t a, mask, ah, offset;
+ struct ipcp_options *wo = &ipcp_wantoptions[unit];
+ u_int32_t suggested_ip = 0;
+
+ if (addresses[unit] != NULL)
+ free(addresses[unit]);
+ addresses[unit] = NULL;
+ if (extra_options != NULL)
+ free_wordlist(extra_options);
+ extra_options = opts;
+
+ /*
+ * Count the number of IP addresses given.
+ */
+ n = wordlist_count(addrs) + wordlist_count(noauth_addrs);
+ if (n == 0)
+ return;
+ ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip));
+ if (ip == 0)
+ return;
+
+ /* temporarily append the noauth_addrs list to addrs */
+ for (plink = &addrs; *plink != NULL; plink = &(*plink)->next)
+ ;
+ *plink = noauth_addrs;
+
+ n = 0;
+ for (ap = addrs; ap != NULL; ap = ap->next) {
+ /* "-" means no addresses authorized, "*" means any address allowed */
+ ptr_word = ap->word;
+ if (strcmp(ptr_word, "-") == 0)
+ break;
+ if (strcmp(ptr_word, "*") == 0) {
+ ip[n].permit = 1;
+ ip[n].base = ip[n].mask = 0;
+ ++n;
+ break;
+ }
+
+ ip[n].permit = 1;
+ if (*ptr_word == '!') {
+ ip[n].permit = 0;
+ ++ptr_word;
+ }
+
+ mask = ~ (u_int32_t) 0;
+ offset = 0;
+ ptr_mask = strchr (ptr_word, '/');
+ if (ptr_mask != NULL) {
+ int bit_count;
+ char *endp;
+
+ bit_count = (int) strtol (ptr_mask+1, &endp, 10);
+ if (bit_count <= 0 || bit_count > 32) {
+ warn("invalid address length %v in auth. address list",
+ ptr_mask+1);
+ continue;
+ }
+ bit_count = 32 - bit_count; /* # bits in host part */
+ if (*endp == '+') {
+ offset = ifunit + 1;
+ ++endp;
+ }
+ if (*endp != 0) {
+ warn("invalid address length syntax: %v", ptr_mask+1);
+ continue;
+ }
+ *ptr_mask = '\0';
+ mask <<= bit_count;
+ }
+
+ hp = gethostbyname(ptr_word);
+ if (hp != NULL && hp->h_addrtype == AF_INET) {
+ a = *(u_int32_t *)hp->h_addr;
+ } else {
+ np = getnetbyname (ptr_word);
+ if (np != NULL && np->n_addrtype == AF_INET) {
+ a = htonl (*(u_int32_t *)np->n_net);
+ if (ptr_mask == NULL) {
+ /* calculate appropriate mask for net */
+ ah = ntohl(a);
+ if (IN_CLASSA(ah))
+ mask = IN_CLASSA_NET;
+ else if (IN_CLASSB(ah))
+ mask = IN_CLASSB_NET;
+ else if (IN_CLASSC(ah))
+ mask = IN_CLASSC_NET;
+ }
+ } else {
+ a = inet_addr (ptr_word);
+ }
+ }
+
+ if (ptr_mask != NULL)
+ *ptr_mask = '/';
+
+ if (a == (u_int32_t)-1L) {
+ warn("unknown host %s in auth. address list", ap->word);
+ continue;
+ }
+ if (offset != 0) {
+ if (offset >= ~mask) {
+ warn("interface unit %d too large for subnet %v",
+ ifunit, ptr_word);
+ continue;
+ }
+ a = htonl((ntohl(a) & mask) + offset);
+ mask = ~(u_int32_t)0;
+ }
+ ip[n].mask = htonl(mask);
+ ip[n].base = a & ip[n].mask;
+ ++n;
+ if (~mask == 0 && suggested_ip == 0)
+ suggested_ip = a;
+ }
+ *plink = NULL;
+
+ ip[n].permit = 0; /* make the last entry forbid all addresses */
+ ip[n].base = 0; /* to terminate the list */
+ ip[n].mask = 0;
+
+ addresses[unit] = ip;
+
+ /*
+ * If the address given for the peer isn't authorized, or if
+ * the user hasn't given one, AND there is an authorized address
+ * which is a single host, then use that if we find one.
+ */
+ if (suggested_ip != 0
+ && (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr))) {
+ wo->hisaddr = suggested_ip;
+ /*
+ * Do we insist on this address? No, if there are other
+ * addresses authorized than the suggested one.
+ */
+ if (n > 1)
+ wo->accept_remote = 1;
+ }
+}
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address. Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(unit, addr)
+ int unit;
+ u_int32_t addr;
+{
+ int ok;
+
+ /* don't allow loopback or multicast address */
+ if (bad_ip_adrs(addr))
+ return 0;
+
+ if (addresses[unit] != NULL) {
+ ok = ip_addr_check(addr, addresses[unit]);
+ if (ok >= 0)
+ return ok;
+ }
+ if (auth_required)
+ return 0; /* no addresses authorized */
+ return allow_any_ip || privileged || !have_route_to(addr);
+}
+
+static int
+ip_addr_check(addr, addrs)
+ u_int32_t addr;
+ struct permitted_ip *addrs;
+{
+ for (; ; ++addrs)
+ if ((addr & addrs->mask) == addrs->base)
+ return addrs->permit;
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(addr)
+ u_int32_t addr;
+{
+ addr = ntohl(addr);
+ return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+ || IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+/*
+ * some_ip_ok - check a wordlist to see if it authorizes any
+ * IP address(es).
+ */
+static int
+some_ip_ok(addrs)
+ struct wordlist *addrs;
+{
+ for (; addrs != 0; addrs = addrs->next) {
+ if (addrs->word[0] == '-')
+ break;
+ if (addrs->word[0] != '!')
+ return 1; /* some IP address is allowed */
+ }
+ return 0;
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+static void
+check_access(f, filename)
+ FILE *f;
+ char *filename;
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(f), &sbuf) < 0) {
+ warn("cannot stat secret file %s: %m", filename);
+ } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ warn("Warning - secret file %s has world and/or group access",
+ filename);
+ }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'. The return value is -1
+ * if no secret is found, otherwise >= 0. The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line up to a "--" (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs. Any
+ * following words (extra options) are placed in a wordlist and
+ * returned in *opts.
+ * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ */
+static int
+scan_authfile(f, client, server, secret, addrs, opts, filename)
+ FILE *f;
+ char *client;
+ char *server;
+ char *secret;
+ struct wordlist **addrs;
+ struct wordlist **opts;
+ char *filename;
+{
+ int newline, xxx;
+ int got_flag, best_flag;
+ FILE *sf;
+ struct wordlist *ap, *addr_list, *alist, **app;
+ char word[MAXWORDLEN];
+ char atfile[MAXWORDLEN];
+ char lsecret[MAXWORDLEN];
+
+ if (addrs != NULL)
+ *addrs = NULL;
+ if (opts != NULL)
+ *opts = NULL;
+ addr_list = NULL;
+ if (!getword(f, word, &newline, filename))
+ return -1; /* file is empty??? */
+ newline = 1;
+ best_flag = -1;
+ for (;;) {
+ /*
+ * Skip until we find a word at the start of a line.
+ */
+ while (!newline && getword(f, word, &newline, filename))
+ ;
+ if (!newline)
+ break; /* got to end of file */
+
+ /*
+ * Got a client - check if it's a match or a wildcard.
+ */
+ got_flag = 0;
+ if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+ newline = 0;
+ continue;
+ }
+ if (!ISWILD(word))
+ got_flag = NONWILD_CLIENT;
+
+ /*
+ * Now get a server and check if it matches.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+ if (!ISWILD(word)) {
+ if (server != NULL && strcmp(word, server) != 0)
+ continue;
+ got_flag |= NONWILD_SERVER;
+ }
+
+ /*
+ * Got some sort of a match - see if it's better than what
+ * we have already.
+ */
+ if (got_flag <= best_flag)
+ continue;
+
+ /*
+ * Get the secret.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+
+ if (secret != NULL) {
+ /*
+ * Special syntax: @/pathname means read secret from file.
+ */
+ if (word[0] == '@' && word[1] == '/') {
+ strlcpy(atfile, word+1, sizeof(atfile));
+ if ((sf = fopen(atfile, "r")) == NULL) {
+ warn("can't open indirect secret file %s", atfile);
+ continue;
+ }
+ check_access(sf, atfile);
+ if (!getword(sf, word, &xxx, atfile)) {
+ warn("no secret in indirect secret file %s", atfile);
+ fclose(sf);
+ continue;
+ }
+ fclose(sf);
+ }
+ strlcpy(lsecret, word, sizeof(lsecret));
+ }
+
+ /*
+ * Now read address authorization info and make a wordlist.
+ */
+ app = &alist;
+ for (;;) {
+ if (!getword(f, word, &newline, filename) || newline)
+ break;
+ ap = (struct wordlist *)
+ malloc(sizeof(struct wordlist) + strlen(word) + 1);
+ if (ap == NULL)
+ novm("authorized addresses");
+ ap->word = (char *) (ap + 1);
+ strcpy(ap->word, word);
+ *app = ap;
+ app = &ap->next;
+ }
+ *app = NULL;
+
+ /*
+ * This is the best so far; remember it.
+ */
+ best_flag = got_flag;
+ if (addr_list)
+ free_wordlist(addr_list);
+ addr_list = alist;
+ if (secret != NULL)
+ strlcpy(secret, lsecret, MAXWORDLEN);
+
+ if (!newline)
+ break;
+ }
+
+ /* scan for a -- word indicating the start of options */
+ for (app = &addr_list; (ap = *app) != NULL; app = &ap->next)
+ if (strcmp(ap->word, "--") == 0)
+ break;
+ /* ap = start of options */
+ if (ap != NULL) {
+ ap = ap->next; /* first option */
+ free(*app); /* free the "--" word */
+ *app = NULL; /* terminate addr list */
+ }
+ if (opts != NULL)
+ *opts = ap;
+ else if (ap != NULL)
+ free_wordlist(ap);
+ if (addrs != NULL)
+ *addrs = addr_list;
+ else if (addr_list != NULL)
+ free_wordlist(addr_list);
+
+ return best_flag;
+}
+
+/*
+ * wordlist_count - return the number of items in a wordlist
+ */
+static int
+wordlist_count(wp)
+ struct wordlist *wp;
+{
+ int n;
+
+ for (n = 0; wp != NULL; wp = wp->next)
+ ++n;
+ return n;
+}
+
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(wp)
+ struct wordlist *wp;
+{
+ struct wordlist *next;
+
+ while (wp != NULL) {
+ next = wp->next;
+ free(wp);
+ wp = next;
+ }
+}
+
+/*
+ * auth_script_done - called when the auth-up or auth-down script
+ * has finished.
+ */
+static void
+auth_script_done(arg)
+ void *arg;
+{
+ auth_script_pid = 0;
+ switch (auth_script_state) {
+ case s_up:
+ if (auth_state == s_down) {
+ auth_script_state = s_down;
+ auth_script(_PATH_AUTHDOWN);
+ }
+ break;
+ case s_down:
+ if (auth_state == s_up) {
+ auth_script_state = s_up;
+ auth_script(_PATH_AUTHUP);
+ }
+ break;
+ }
+}
+
+/*
+ * auth_script - execute a script with arguments
+ * interface-name peer-name real-user tty speed
+ */
+static void
+auth_script(script)
+ char *script;
+{
+ char strspeed[32];
+ struct passwd *pw;
+ char struid[32];
+ char *user_name;
+ char *argv[8];
+
+ if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
+ user_name = pw->pw_name;
+ else {
+ slprintf(struid, sizeof(struid), "%d", getuid());
+ user_name = struid;
+ }
+ slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = peer_authname;
+ argv[3] = user_name;
+ argv[4] = devnam;
+ argv[5] = strspeed;
+ argv[6] = NULL;
+
+ auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL);
+}
diff --git a/mdk-stage1/ppp/pppd/cbcp.c b/mdk-stage1/ppp/pppd/cbcp.c
new file mode 100644
index 000000000..8c57b3cde
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/cbcp.c
@@ -0,0 +1,456 @@
+/*
+ * cbcp - Call Back Configuration Protocol.
+ *
+ * Copyright (c) 1995 Pedro Roque Marques
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Pedro Roque Marques. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "pppd.h"
+#include "cbcp.h"
+#include "fsm.h"
+#include "lcp.h"
+
+static const char rcsid[] = RCSID;
+
+/*
+ * Options.
+ */
+static int setcbcp __P((char **));
+
+static option_t cbcp_option_list[] = {
+ { "callback", o_special, setcbcp,
+ "Ask for callback", OPT_PRIO | OPT_A2STRVAL, &cbcp[0].us_number },
+ { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void cbcp_init __P((int unit));
+static void cbcp_open __P((int unit));
+static void cbcp_lowerup __P((int unit));
+static void cbcp_input __P((int unit, u_char *pkt, int len));
+static void cbcp_protrej __P((int unit));
+static int cbcp_printpkt __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+
+struct protent cbcp_protent = {
+ PPP_CBCP,
+ cbcp_init,
+ cbcp_input,
+ cbcp_protrej,
+ cbcp_lowerup,
+ NULL,
+ cbcp_open,
+ NULL,
+ cbcp_printpkt,
+ NULL,
+ 0,
+ "CBCP",
+ NULL,
+ cbcp_option_list,
+ NULL,
+ NULL,
+ NULL
+};
+
+cbcp_state cbcp[NUM_PPP];
+
+/* internal prototypes */
+
+static void cbcp_recvreq __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_resp __P((cbcp_state *us));
+static void cbcp_up __P((cbcp_state *us));
+static void cbcp_recvack __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_send __P((cbcp_state *us, u_char code, u_char *buf, int len));
+
+/* 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);
+}
+
+/* init state */
+static void
+cbcp_init(iface)
+ int iface;
+{
+ cbcp_state *us;
+
+ us = &cbcp[iface];
+ memset(us, 0, sizeof(cbcp_state));
+ us->us_unit = iface;
+ us->us_type |= (1 << CB_CONF_NO);
+}
+
+/* lower layer is up */
+static void
+cbcp_lowerup(iface)
+ int iface;
+{
+ cbcp_state *us = &cbcp[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
+cbcp_open(unit)
+ int unit;
+{
+ dbglog("cbcp_open");
+}
+
+/* process an incomming packet */
+static void
+cbcp_input(unit, inpacket, pktlen)
+ int unit;
+ u_char *inpacket;
+ int pktlen;
+{
+ u_char *inp;
+ u_char code, id;
+ u_short len;
+
+ cbcp_state *us = &cbcp[unit];
+
+ inp = inpacket;
+
+ if (pktlen < CBCP_MINLEN) {
+ error("CBCP packet is too small");
+ return;
+ }
+
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+
+#if 0
+ if (len > pktlen) {
+ error("CBCP packet: invalid length");
+ return;
+ }
+#endif
+
+ len -= CBCP_MINLEN;
+
+ switch(code) {
+ case CBCP_REQ:
+ us->us_id = id;
+ cbcp_recvreq(us, inp, len);
+ break;
+
+ case CBCP_RESP:
+ dbglog("CBCP_RESP received");
+ break;
+
+ case CBCP_ACK:
+ if (id != us->us_id)
+ dbglog("id doesn't match: expected %d recv %d",
+ us->us_id, id);
+
+ cbcp_recvack(us, inp, len);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* protocol was rejected by foe */
+void cbcp_protrej(int iface)
+{
+}
+
+char *cbcp_codenames[] = {
+ "Request", "Response", "Ack"
+};
+
+char *cbcp_optionnames[] = {
+ "NoCallback",
+ "UserDefined",
+ "AdminDefined",
+ "List"
+};
+
+/* pretty print a packet */
+static int
+cbcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, opt, id, len, olen, delay;
+ u_char *pstart;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(cbcp_codenames) / sizeof(char *))
+ printer(arg, " %s", cbcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+
+ switch (code) {
+ case CBCP_REQ:
+ case CBCP_RESP:
+ case CBCP_ACK:
+ while(len >= 2) {
+ GETCHAR(opt, p);
+ GETCHAR(olen, p);
+
+ if (olen < 2 || olen > len) {
+ break;
+ }
+
+ printer(arg, " <");
+ len -= olen;
+
+ if (opt >= 1 && opt <= sizeof(cbcp_optionnames) / sizeof(char *))
+ printer(arg, " %s", cbcp_optionnames[opt-1]);
+ else
+ printer(arg, " option=0x%x", opt);
+
+ if (olen > 2) {
+ GETCHAR(delay, p);
+ printer(arg, " delay = %d", delay);
+ }
+
+ if (olen > 3) {
+ int addrt;
+ char str[256];
+
+ GETCHAR(addrt, p);
+ memcpy(str, p, olen - 4);
+ str[olen - 4] = 0;
+ printer(arg, " number = %s", str);
+ }
+ printer(arg, ">");
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/* received CBCP request */
+static void
+cbcp_recvreq(us, pckt, pcktlen)
+ cbcp_state *us;
+ char *pckt;
+ int pcktlen;
+{
+ u_char type, opt_len, delay, addr_type;
+ char address[256];
+ int len = pcktlen;
+
+ address[0] = 0;
+
+ while (len) {
+ dbglog("length: %d", len);
+
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (opt_len > 2)
+ GETCHAR(delay, pckt);
+
+ us->us_allowed |= (1 << type);
+
+ switch(type) {
+ case CB_CONF_NO:
+ dbglog("no callback allowed");
+ break;
+
+ case CB_CONF_USER:
+ dbglog("user callback allowed");
+ if (opt_len > 4) {
+ GETCHAR(addr_type, pckt);
+ memcpy(address, pckt, opt_len - 4);
+ address[opt_len - 4] = 0;
+ if (address[0])
+ dbglog("address: %s", address);
+ }
+ break;
+
+ case CB_CONF_ADMIN:
+ dbglog("user admin defined allowed");
+ break;
+
+ case CB_CONF_LIST:
+ break;
+ }
+ len -= opt_len;
+ }
+
+ cbcp_resp(us);
+}
+
+static void
+cbcp_resp(us)
+ cbcp_state *us;
+{
+ u_char cb_type;
+ u_char buf[256];
+ u_char *bufp = buf;
+ int len = 0;
+
+ cb_type = us->us_allowed & us->us_type;
+ dbglog("cbcp_resp cb_type=%d", cb_type);
+
+#if 0
+ if (!cb_type)
+ lcp_down(us->us_unit);
+#endif
+
+ if (cb_type & ( 1 << CB_CONF_USER ) ) {
+ dbglog("cbcp_resp CONF_USER");
+ PUTCHAR(CB_CONF_USER, bufp);
+ len = 3 + 1 + strlen(us->us_number) + 1;
+ PUTCHAR(len , bufp);
+ PUTCHAR(5, bufp); /* delay */
+ PUTCHAR(1, bufp);
+ BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
+ dbglog("cbcp_resp CONF_ADMIN");
+ PUTCHAR(CB_CONF_ADMIN, bufp);
+ len = 3;
+ PUTCHAR(len, bufp);
+ PUTCHAR(5, bufp); /* delay */
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_NO ) ) {
+ dbglog("cbcp_resp CONF_NO");
+ PUTCHAR(CB_CONF_NO, bufp);
+ len = 3;
+ PUTCHAR(len , bufp);
+ PUTCHAR(0, bufp);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ start_networks();
+ return;
+ }
+}
+
+static void
+cbcp_send(us, code, buf, len)
+ cbcp_state *us;
+ u_char code;
+ u_char *buf;
+ int len;
+{
+ u_char *outp;
+ int outlen;
+
+ outp = outpacket_buf;
+
+ outlen = 4 + len;
+
+ MAKEHEADER(outp, PPP_CBCP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(us->us_id, outp);
+ PUTSHORT(outlen, outp);
+
+ if (len)
+ BCOPY(buf, outp, len);
+
+ output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+static void
+cbcp_recvack(us, pckt, len)
+ cbcp_state *us;
+ char *pckt;
+ int len;
+{
+ u_char type, delay, addr_type;
+ int opt_len;
+ char address[256];
+
+ if (len) {
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (opt_len > 2)
+ GETCHAR(delay, pckt);
+
+ if (opt_len > 4) {
+ GETCHAR(addr_type, pckt);
+ memcpy(address, pckt, opt_len - 4);
+ address[opt_len - 4] = 0;
+ if (address[0])
+ dbglog("peer will call: %s", address);
+ }
+ if (type == CB_CONF_NO)
+ return;
+ }
+
+ cbcp_up(us);
+}
+
+/* ok peer will do callback */
+static void
+cbcp_up(us)
+ cbcp_state *us;
+{
+ persist = 0;
+ lcp_close(0, "Call me back, please");
+ status = EXIT_CALLBACK;
+}
diff --git a/mdk-stage1/ppp/pppd/cbcp.h b/mdk-stage1/ppp/pppd/cbcp.h
new file mode 100644
index 000000000..c2ab3f689
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/cbcp.h
@@ -0,0 +1,26 @@
+#ifndef CBCP_H
+#define CBCP_H
+
+typedef struct cbcp_state {
+ int us_unit; /* Interface unit number */
+ u_char us_id; /* Current id */
+ u_char us_allowed;
+ int us_type;
+ char *us_number; /* Telefone Number */
+} cbcp_state;
+
+extern cbcp_state cbcp[];
+
+extern struct protent cbcp_protent;
+
+#define CBCP_MINLEN 4
+
+#define CBCP_REQ 1
+#define CBCP_RESP 2
+#define CBCP_ACK 3
+
+#define CB_CONF_NO 1
+#define CB_CONF_USER 2
+#define CB_CONF_ADMIN 3
+#define CB_CONF_LIST 4
+#endif
diff --git a/mdk-stage1/ppp/pppd/ccp.c b/mdk-stage1/ppp/pppd/ccp.c
new file mode 100644
index 000000000..7e36e384e
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/ccp.c
@@ -0,0 +1,1257 @@
+/*
+ * ccp.c - PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#define RCSID "$Id$"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ccp.h"
+#include <net/ppp-comp.h>
+
+static const char rcsid[] = RCSID;
+
+/*
+ * Unfortunately there is a bug in zlib which means that using a
+ * size of 8 (window size = 256) for Deflate compression will cause
+ * buffer overruns and kernel crashes in the deflate module.
+ * Until this is fixed we only accept sizes in the range 9 .. 15.
+ * Thanks to James Carlson for pointing this out.
+ */
+#define DEFLATE_MIN_WORKS 9
+
+/*
+ * Command-line options.
+ */
+static int setbsdcomp __P((char **));
+static int setdeflate __P((char **));
+static char bsd_value[8];
+static char deflate_value[8];
+
+static option_t ccp_option_list[] = {
+ { "noccp", o_bool, &ccp_protent.enabled_flag,
+ "Disable CCP negotiation" },
+ { "-ccp", o_bool, &ccp_protent.enabled_flag,
+ "Disable CCP negotiation", OPT_ALIAS },
+
+ { "bsdcomp", o_special, (void *)setbsdcomp,
+ "Request BSD-Compress packet compression",
+ OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, bsd_value },
+ { "nobsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+ "don't allow BSD-Compress", OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].bsd_compress },
+ { "-bsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+ "don't allow BSD-Compress", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].bsd_compress },
+
+ { "deflate", o_special, (void *)setdeflate,
+ "request Deflate compression",
+ OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, deflate_value },
+ { "nodeflate", o_bool, &ccp_wantoptions[0].deflate,
+ "don't allow Deflate compression", OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].deflate },
+ { "-deflate", o_bool, &ccp_wantoptions[0].deflate,
+ "don't allow Deflate compression", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].deflate },
+
+ { "nodeflatedraft", o_bool, &ccp_wantoptions[0].deflate_draft,
+ "don't use draft deflate #", OPT_A2COPY,
+ &ccp_allowoptions[0].deflate_draft },
+
+ { "predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+ "request Predictor-1", 1, &ccp_allowoptions[0].predictor_1, OPT_PRIO },
+ { "nopredictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+ "don't allow Predictor-1", OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].predictor_1 },
+ { "-predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+ "don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].predictor_1 },
+
+ { NULL }
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ccp_init __P((int unit));
+static void ccp_open __P((int unit));
+static void ccp_close __P((int unit, char *));
+static void ccp_lowerup __P((int unit));
+static void ccp_lowerdown __P((int));
+static void ccp_input __P((int unit, u_char *pkt, int len));
+static void ccp_protrej __P((int unit));
+static int ccp_printpkt __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+static void ccp_datainput __P((int unit, u_char *pkt, int len));
+
+struct protent ccp_protent = {
+ PPP_CCP,
+ ccp_init,
+ ccp_input,
+ ccp_protrej,
+ ccp_lowerup,
+ ccp_lowerdown,
+ ccp_open,
+ ccp_close,
+ ccp_printpkt,
+ ccp_datainput,
+ 1,
+ "CCP",
+ "Compressed",
+ ccp_option_list,
+ NULL,
+ NULL,
+ NULL
+};
+
+fsm ccp_fsm[NUM_PPP];
+ccp_options ccp_wantoptions[NUM_PPP]; /* what to request the peer to use */
+ccp_options ccp_gotoptions[NUM_PPP]; /* what the peer agreed to do */
+ccp_options ccp_allowoptions[NUM_PPP]; /* what we'll agree to do */
+ccp_options ccp_hisoptions[NUM_PPP]; /* what we agreed to do */
+
+/*
+ * Callbacks for fsm code.
+ */
+static void ccp_resetci __P((fsm *));
+static int ccp_cilen __P((fsm *));
+static void ccp_addci __P((fsm *, u_char *, int *));
+static int ccp_ackci __P((fsm *, u_char *, int));
+static int ccp_nakci __P((fsm *, u_char *, int));
+static int ccp_rejci __P((fsm *, u_char *, int));
+static int ccp_reqci __P((fsm *, u_char *, int *, int));
+static void ccp_up __P((fsm *));
+static void ccp_down __P((fsm *));
+static int ccp_extcode __P((fsm *, int, int, u_char *, int));
+static void ccp_rack_timeout __P((void *));
+static char *method_name __P((ccp_options *, ccp_options *));
+
+static fsm_callbacks ccp_callbacks = {
+ ccp_resetci,
+ ccp_cilen,
+ ccp_addci,
+ ccp_ackci,
+ ccp_nakci,
+ ccp_rejci,
+ ccp_reqci,
+ ccp_up,
+ ccp_down,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ccp_extcode,
+ "CCP"
+};
+
+/*
+ * Do we want / did we get any compression?
+ */
+#define ANY_COMPRESS(opt) ((opt).deflate || (opt).bsd_compress \
+ || (opt).predictor_1 || (opt).predictor_2)
+
+/*
+ * Local state (mainly for handling reset-reqs and reset-acks).
+ */
+static int ccp_localstate[NUM_PPP];
+#define RACK_PENDING 1 /* waiting for reset-ack */
+#define RREQ_REPEAT 2 /* send another reset-req if no reset-ack */
+
+#define RACKTIMEOUT 1 /* second */
+
+static int all_rejected[NUM_PPP]; /* we rejected all peer's options */
+
+/*
+ * Option parsing.
+ */
+static int
+setbsdcomp(argv)
+ char **argv;
+{
+ int rbits, abits;
+ char *str, *endp;
+
+ str = *argv;
+ abits = rbits = strtol(str, &endp, 0);
+ if (endp != str && *endp == ',') {
+ str = endp + 1;
+ abits = strtol(str, &endp, 0);
+ }
+ if (*endp != 0 || endp == str) {
+ option_error("invalid parameter '%s' for bsdcomp option", *argv);
+ return 0;
+ }
+ if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS))
+ || (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) {
+ option_error("bsdcomp option values must be 0 or %d .. %d",
+ BSD_MIN_BITS, BSD_MAX_BITS);
+ return 0;
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].bsd_compress = 1;
+ ccp_wantoptions[0].bsd_bits = rbits;
+ } else
+ ccp_wantoptions[0].bsd_compress = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].bsd_compress = 1;
+ ccp_allowoptions[0].bsd_bits = abits;
+ } else
+ ccp_allowoptions[0].bsd_compress = 0;
+ slprintf(bsd_value, sizeof(bsd_value),
+ rbits == abits? "%d": "%d,%d", rbits, abits);
+
+ return 1;
+}
+
+static int
+setdeflate(argv)
+ char **argv;
+{
+ int rbits, abits;
+ char *str, *endp;
+
+ str = *argv;
+ abits = rbits = strtol(str, &endp, 0);
+ if (endp != str && *endp == ',') {
+ str = endp + 1;
+ abits = strtol(str, &endp, 0);
+ }
+ if (*endp != 0 || endp == str) {
+ option_error("invalid parameter '%s' for deflate option", *argv);
+ return 0;
+ }
+ if ((rbits != 0 && (rbits < DEFLATE_MIN_SIZE || rbits > DEFLATE_MAX_SIZE))
+ || (abits != 0 && (abits < DEFLATE_MIN_SIZE
+ || abits > DEFLATE_MAX_SIZE))) {
+ option_error("deflate option values must be 0 or %d .. %d",
+ DEFLATE_MIN_SIZE, DEFLATE_MAX_SIZE);
+ return 0;
+ }
+ if (rbits == DEFLATE_MIN_SIZE || abits == DEFLATE_MIN_SIZE) {
+ if (rbits == DEFLATE_MIN_SIZE)
+ rbits = DEFLATE_MIN_WORKS;
+ if (abits == DEFLATE_MIN_SIZE)
+ abits = DEFLATE_MIN_WORKS;
+ warn("deflate option value of %d changed to %d to avoid zlib bug",
+ DEFLATE_MIN_SIZE, DEFLATE_MIN_WORKS);
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].deflate = 1;
+ ccp_wantoptions[0].deflate_size = rbits;
+ } else
+ ccp_wantoptions[0].deflate = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = abits;
+ } else
+ ccp_allowoptions[0].deflate = 0;
+ slprintf(deflate_value, sizeof(deflate_value),
+ rbits == abits? "%d": "%d,%d", rbits, abits);
+
+ return 1;
+}
+
+/*
+ * ccp_init - initialize CCP.
+ */
+static void
+ccp_init(unit)
+ int unit;
+{
+ fsm *f = &ccp_fsm[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_CCP;
+ f->callbacks = &ccp_callbacks;
+ fsm_init(f);
+
+ memset(&ccp_wantoptions[unit], 0, sizeof(ccp_options));
+ memset(&ccp_gotoptions[unit], 0, sizeof(ccp_options));
+ memset(&ccp_allowoptions[unit], 0, sizeof(ccp_options));
+ memset(&ccp_hisoptions[unit], 0, sizeof(ccp_options));
+
+ ccp_wantoptions[0].deflate = 1;
+ ccp_wantoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+ ccp_wantoptions[0].deflate_correct = 1;
+ ccp_wantoptions[0].deflate_draft = 1;
+ ccp_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+ ccp_allowoptions[0].deflate_correct = 1;
+ ccp_allowoptions[0].deflate_draft = 1;
+
+ ccp_wantoptions[0].bsd_compress = 1;
+ ccp_wantoptions[0].bsd_bits = BSD_MAX_BITS;
+ ccp_allowoptions[0].bsd_compress = 1;
+ ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS;
+
+ ccp_allowoptions[0].predictor_1 = 1;
+}
+
+/*
+ * ccp_open - CCP is allowed to come up.
+ */
+static void
+ccp_open(unit)
+ int unit;
+{
+ fsm *f = &ccp_fsm[unit];
+
+ if (f->state != OPENED)
+ ccp_flags_set(unit, 1, 0);
+
+ /*
+ * Find out which compressors the kernel supports before
+ * deciding whether to open in silent mode.
+ */
+ ccp_resetci(f);
+ if (!ANY_COMPRESS(ccp_gotoptions[unit]))
+ f->flags |= OPT_SILENT;
+
+ fsm_open(f);
+}
+
+/*
+ * ccp_close - Terminate CCP.
+ */
+static void
+ccp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ ccp_flags_set(unit, 0, 0);
+ fsm_close(&ccp_fsm[unit], reason);
+}
+
+/*
+ * ccp_lowerup - we may now transmit CCP packets.
+ */
+static void
+ccp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_lowerdown - we may not transmit CCP packets.
+ */
+static void
+ccp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_input - process a received CCP packet.
+ */
+static void
+ccp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm *f = &ccp_fsm[unit];
+ int oldstate;
+
+ /*
+ * Check for a terminate-request so we can print a message.
+ */
+ oldstate = f->state;
+ fsm_input(f, p, len);
+ if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED)
+ notice("Compression disabled by peer.");
+
+ /*
+ * If we get a terminate-ack and we're not asking for compression,
+ * close CCP.
+ */
+ if (oldstate == REQSENT && p[0] == TERMACK
+ && !ANY_COMPRESS(ccp_gotoptions[unit]))
+ ccp_close(unit, "No compression negotiated");
+}
+
+/*
+ * Handle a CCP-specific code.
+ */
+static int
+ccp_extcode(f, code, id, p, len)
+ fsm *f;
+ int code, id;
+ u_char *p;
+ int len;
+{
+ switch (code) {
+ case CCP_RESETREQ:
+ if (f->state != OPENED)
+ break;
+ /* send a reset-ack, which the transmitter will see and
+ reset its compression state. */
+ fsm_sdata(f, CCP_RESETACK, id, NULL, 0);
+ break;
+
+ case CCP_RESETACK:
+ if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) {
+ ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT);
+ UNTIMEOUT(ccp_rack_timeout, f);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * ccp_protrej - peer doesn't talk CCP.
+ */
+static void
+ccp_protrej(unit)
+ int unit;
+{
+ ccp_flags_set(unit, 0, 0);
+ fsm_lowerdown(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_resetci - initialize at start of negotiation.
+ */
+static void
+ccp_resetci(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char opt_buf[16];
+
+ *go = ccp_wantoptions[f->unit];
+ all_rejected[f->unit] = 0;
+
+ /*
+ * Check whether the kernel knows about the various
+ * compression methods we might request.
+ */
+ if (go->bsd_compress) {
+ opt_buf[0] = CI_BSD_COMPRESS;
+ opt_buf[1] = CILEN_BSD_COMPRESS;
+ opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, BSD_MIN_BITS);
+ if (ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0) <= 0)
+ go->bsd_compress = 0;
+ }
+ if (go->deflate) {
+ if (go->deflate_correct) {
+ opt_buf[0] = CI_DEFLATE;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_WORKS);
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+ go->deflate_correct = 0;
+ }
+ if (go->deflate_draft) {
+ opt_buf[0] = CI_DEFLATE_DRAFT;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_WORKS);
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+ go->deflate_draft = 0;
+ }
+ if (!go->deflate_correct && !go->deflate_draft)
+ go->deflate = 0;
+ }
+ if (go->predictor_1) {
+ opt_buf[0] = CI_PREDICTOR_1;
+ opt_buf[1] = CILEN_PREDICTOR_1;
+ if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_1, 0) <= 0)
+ go->predictor_1 = 0;
+ }
+ if (go->predictor_2) {
+ opt_buf[0] = CI_PREDICTOR_2;
+ opt_buf[1] = CILEN_PREDICTOR_2;
+ if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_2, 0) <= 0)
+ go->predictor_2 = 0;
+ }
+}
+
+/*
+ * ccp_cilen - Return total length of our configuration info.
+ */
+static int
+ccp_cilen(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+
+ return (go->bsd_compress? CILEN_BSD_COMPRESS: 0)
+ + (go->deflate? CILEN_DEFLATE: 0)
+ + (go->predictor_1? CILEN_PREDICTOR_1: 0)
+ + (go->predictor_2? CILEN_PREDICTOR_2: 0);
+}
+
+/*
+ * ccp_addci - put our requests in a packet.
+ */
+static void
+ccp_addci(f, p, lenp)
+ fsm *f;
+ u_char *p;
+ int *lenp;
+{
+ int res;
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char *p0 = p;
+
+ /*
+ * Add the compression types that we can receive, in decreasing
+ * preference order. Get the kernel to allocate the first one
+ * in case it gets Acked.
+ */
+ if (go->deflate) {
+ p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT;
+ p[1] = CILEN_DEFLATE;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_DEFLATE, 0);
+ if (res > 0) {
+ p += CILEN_DEFLATE;
+ break;
+ }
+ if (res < 0 || go->deflate_size <= DEFLATE_MIN_WORKS) {
+ go->deflate = 0;
+ break;
+ }
+ --go->deflate_size;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ }
+ if (p != p0 && go->deflate_correct && go->deflate_draft) {
+ p[0] = CI_DEFLATE_DRAFT;
+ p[1] = CILEN_DEFLATE;
+ p[2] = p[2 - CILEN_DEFLATE];
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ p += CILEN_DEFLATE;
+ }
+ }
+ if (go->bsd_compress) {
+ p[0] = CI_BSD_COMPRESS;
+ p[1] = CILEN_BSD_COMPRESS;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ if (p != p0) {
+ p += CILEN_BSD_COMPRESS; /* not the first option */
+ } else {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0);
+ if (res > 0) {
+ p += CILEN_BSD_COMPRESS;
+ break;
+ }
+ if (res < 0 || go->bsd_bits <= BSD_MIN_BITS) {
+ go->bsd_compress = 0;
+ break;
+ }
+ --go->bsd_bits;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ }
+ }
+ }
+ /* XXX Should Predictor 2 be preferable to Predictor 1? */
+ if (go->predictor_1) {
+ p[0] = CI_PREDICTOR_1;
+ p[1] = CILEN_PREDICTOR_1;
+ if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 0) <= 0) {
+ go->predictor_1 = 0;
+ } else {
+ p += CILEN_PREDICTOR_1;
+ }
+ }
+ if (go->predictor_2) {
+ p[0] = CI_PREDICTOR_2;
+ p[1] = CILEN_PREDICTOR_2;
+ if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 0) <= 0) {
+ go->predictor_2 = 0;
+ } else {
+ p += CILEN_PREDICTOR_2;
+ }
+ }
+
+ go->method = (p > p0)? p0[0]: -1;
+
+ *lenp = p - p0;
+}
+
+/*
+ * ccp_ackci - process a received configure-ack, and return
+ * 1 iff the packet was OK.
+ */
+static int
+ccp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char *p0 = p;
+
+ if (go->deflate) {
+ if (len < CILEN_DEFLATE
+ || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ /* XXX Cope with first/fast ack */
+ if (len == 0)
+ return 1;
+ if (go->deflate_correct && go->deflate_draft) {
+ if (len < CILEN_DEFLATE
+ || p[0] != CI_DEFLATE_DRAFT
+ || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ }
+ if (go->bsd_compress) {
+ if (len < CILEN_BSD_COMPRESS
+ || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS
+ || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+ if (go->predictor_1) {
+ if (len < CILEN_PREDICTOR_1
+ || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1)
+ return 0;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+ if (go->predictor_2) {
+ if (len < CILEN_PREDICTOR_2
+ || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2)
+ return 0;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+
+ if (len != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * ccp_nakci - process received configure-nak.
+ * Returns 1 iff the nak was OK.
+ */
+static int
+ccp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options no; /* options we've seen already */
+ ccp_options try; /* options to ask for next time */
+
+ memset(&no, 0, sizeof(no));
+ try = *go;
+
+ if (go->deflate && len >= CILEN_DEFLATE
+ && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ && p[1] == CILEN_DEFLATE) {
+ no.deflate = 1;
+ /*
+ * Peer wants us to use a different code size or something.
+ * Stop asking for Deflate if we don't understand his suggestion.
+ */
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_WORKS
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ try.deflate = 0;
+ else if (DEFLATE_SIZE(p[2]) < go->deflate_size)
+ try.deflate_size = DEFLATE_SIZE(p[2]);
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ if (go->deflate_correct && go->deflate_draft
+ && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
+ && p[1] == CILEN_DEFLATE) {
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ }
+
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+ && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+ no.bsd_compress = 1;
+ /*
+ * Peer wants us to use a different number of bits
+ * or a different version.
+ */
+ if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION)
+ try.bsd_compress = 0;
+ else if (BSD_NBITS(p[2]) < go->bsd_bits)
+ try.bsd_bits = BSD_NBITS(p[2]);
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+
+ /*
+ * Predictor-1 and 2 have no options, so they can't be Naked.
+ *
+ * There may be remaining options but we ignore them.
+ */
+
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+}
+
+/*
+ * ccp_rejci - reject some of our suggested compression methods.
+ */
+static int
+ccp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Cope with empty configure-rejects by ceasing to send
+ * configure-requests.
+ */
+ if (len == 0 && all_rejected[f->unit])
+ return -1;
+
+ if (go->deflate && len >= CILEN_DEFLATE
+ && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ if (go->deflate_correct)
+ try.deflate_correct = 0;
+ else
+ try.deflate_draft = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ if (go->deflate_correct && go->deflate_draft
+ && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
+ && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ try.deflate_draft = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (!try.deflate_correct && !try.deflate_draft)
+ try.deflate = 0;
+ }
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+ && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+ if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ try.bsd_compress = 0;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+ if (go->predictor_1 && len >= CILEN_PREDICTOR_1
+ && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) {
+ try.predictor_1 = 0;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ }
+ if (go->predictor_2 && len >= CILEN_PREDICTOR_2
+ && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) {
+ try.predictor_2 = 0;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ }
+
+ if (len != 0)
+ return 0;
+
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+}
+
+/*
+ * ccp_reqci - processed a received configure-request.
+ * Returns CONFACK, CONFNAK or CONFREJ and the packet modified
+ * appropriately.
+ */
+static int
+ccp_reqci(f, p, lenp, dont_nak)
+ fsm *f;
+ u_char *p;
+ int *lenp;
+ int dont_nak;
+{
+ int ret, newret, res;
+ u_char *p0, *retp;
+ int len, clen, type, nb;
+ ccp_options *ho = &ccp_hisoptions[f->unit];
+ ccp_options *ao = &ccp_allowoptions[f->unit];
+
+ ret = CONFACK;
+ retp = p0 = p;
+ len = *lenp;
+
+ memset(ho, 0, sizeof(ccp_options));
+ ho->method = (len > 0)? p[0]: -1;
+
+ while (len > 0) {
+ newret = CONFACK;
+ if (len < 2 || p[1] < 2 || p[1] > len) {
+ /* length is bad */
+ clen = len;
+ newret = CONFREJ;
+
+ } else {
+ type = p[0];
+ clen = p[1];
+
+ switch (type) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (!ao->deflate || clen != CILEN_DEFLATE
+ || (!ao->deflate_correct && type == CI_DEFLATE)
+ || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->deflate = 1;
+ ho->deflate_size = nb = DEFLATE_SIZE(p[2]);
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || p[3] != DEFLATE_CHK_SEQUENCE
+ || nb > ao->deflate_size || nb < DEFLATE_MIN_WORKS) {
+ newret = CONFNAK;
+ if (!dont_nak) {
+ p[2] = DEFLATE_MAKE_OPT(ao->deflate_size);
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ /* fall through to test this #bits below */
+ } else
+ break;
+ }
+
+ /*
+ * Check whether we can do Deflate with the window
+ * size they want. If the window is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_DEFLATE, 1);
+ if (res > 0)
+ break; /* it's OK now */
+ if (res < 0 || nb == DEFLATE_MIN_WORKS || dont_nak) {
+ newret = CONFREJ;
+ p[2] = DEFLATE_MAKE_OPT(ho->deflate_size);
+ break;
+ }
+ newret = CONFNAK;
+ --nb;
+ p[2] = DEFLATE_MAKE_OPT(nb);
+ }
+ }
+ break;
+
+ case CI_BSD_COMPRESS:
+ if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->bsd_compress = 1;
+ ho->bsd_bits = nb = BSD_NBITS(p[2]);
+ if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION
+ || nb > ao->bsd_bits || nb < BSD_MIN_BITS) {
+ newret = CONFNAK;
+ if (!dont_nak) {
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits);
+ /* fall through to test this #bits below */
+ } else
+ break;
+ }
+
+ /*
+ * Check whether we can do BSD-Compress with the code
+ * size they want. If the code size is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 1);
+ if (res > 0)
+ break;
+ if (res < 0 || nb == BSD_MIN_BITS || dont_nak) {
+ newret = CONFREJ;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION,
+ ho->bsd_bits);
+ break;
+ }
+ newret = CONFNAK;
+ --nb;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb);
+ }
+ }
+ break;
+
+ case CI_PREDICTOR_1:
+ if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->predictor_1 = 1;
+ if (p == p0
+ && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 1) <= 0) {
+ newret = CONFREJ;
+ }
+ break;
+
+ case CI_PREDICTOR_2:
+ if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->predictor_2 = 1;
+ if (p == p0
+ && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 1) <= 0) {
+ newret = CONFREJ;
+ }
+ break;
+
+ default:
+ newret = CONFREJ;
+ }
+ }
+
+ if (newret == CONFNAK && dont_nak)
+ newret = CONFREJ;
+ if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) {
+ /* we're returning this option */
+ if (newret == CONFREJ && ret == CONFNAK)
+ retp = p0;
+ ret = newret;
+ if (p != retp)
+ BCOPY(p, retp, clen);
+ retp += clen;
+ }
+
+ p += clen;
+ len -= clen;
+ }
+
+ if (ret != CONFACK) {
+ if (ret == CONFREJ && *lenp == retp - p0)
+ all_rejected[f->unit] = 1;
+ else
+ *lenp = retp - p0;
+ }
+ return ret;
+}
+
+/*
+ * Make a string name for a compression method (or 2).
+ */
+static char *
+method_name(opt, opt2)
+ ccp_options *opt, *opt2;
+{
+ static char result[64];
+
+ if (!ANY_COMPRESS(*opt))
+ return "(none)";
+ switch (opt->method) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
+ slprintf(result, sizeof(result), "Deflate%s (%d/%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size, opt2->deflate_size);
+ else
+ slprintf(result, sizeof(result), "Deflate%s (%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size);
+ break;
+ case CI_BSD_COMPRESS:
+ if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits)
+ slprintf(result, sizeof(result), "BSD-Compress (%d/%d)",
+ opt->bsd_bits, opt2->bsd_bits);
+ else
+ slprintf(result, sizeof(result), "BSD-Compress (%d)",
+ opt->bsd_bits);
+ break;
+ case CI_PREDICTOR_1:
+ return "Predictor 1";
+ case CI_PREDICTOR_2:
+ return "Predictor 2";
+ default:
+ slprintf(result, sizeof(result), "Method %d", opt->method);
+ }
+ return result;
+}
+
+/*
+ * CCP has come up - inform the kernel driver and log a message.
+ */
+static void
+ccp_up(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options *ho = &ccp_hisoptions[f->unit];
+ char method1[64];
+
+ ccp_flags_set(f->unit, 1, 1);
+ if (ANY_COMPRESS(*go)) {
+ if (ANY_COMPRESS(*ho)) {
+ if (go->method == ho->method) {
+ notice("%s compression enabled", method_name(go, ho));
+ } else {
+ strlcpy(method1, method_name(go, NULL), sizeof(method1));
+ notice("%s / %s compression enabled",
+ method1, method_name(ho, NULL));
+ }
+ } else
+ notice("%s receive compression enabled", method_name(go, NULL));
+ } else if (ANY_COMPRESS(*ho))
+ notice("%s transmit compression enabled", method_name(ho, NULL));
+}
+
+/*
+ * CCP has gone down - inform the kernel driver.
+ */
+static void
+ccp_down(f)
+ fsm *f;
+{
+ if (ccp_localstate[f->unit] & RACK_PENDING)
+ UNTIMEOUT(ccp_rack_timeout, f);
+ ccp_localstate[f->unit] = 0;
+ ccp_flags_set(f->unit, 1, 0);
+}
+
+/*
+ * Print the contents of a CCP packet.
+ */
+static char *ccp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej",
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ "ResetReq", "ResetAck",
+};
+
+static int
+ccp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ u_char *p0, *optend;
+ int code, id, len;
+ int optlen;
+
+ p0 = p;
+ if (plen < HEADERLEN)
+ return 0;
+ code = p[0];
+ id = p[1];
+ len = (p[2] << 8) + p[3];
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *)
+ && ccp_codenames[code-1] != NULL)
+ printer(arg, " %s", ccp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ p += HEADERLEN;
+
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print list of possible compression methods */
+ while (len >= 2) {
+ code = p[0];
+ optlen = p[1];
+ if (optlen < 2 || optlen > len)
+ break;
+ printer(arg, " <");
+ len -= optlen;
+ optend = p + optlen;
+ switch (code) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (optlen >= CILEN_DEFLATE) {
+ printer(arg, "deflate%s %d",
+ (code == CI_DEFLATE_DRAFT? "(old#)": ""),
+ DEFLATE_SIZE(p[2]));
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL)
+ printer(arg, " method %d", DEFLATE_METHOD(p[2]));
+ if (p[3] != DEFLATE_CHK_SEQUENCE)
+ printer(arg, " check %d", p[3]);
+ p += CILEN_DEFLATE;
+ }
+ break;
+ case CI_BSD_COMPRESS:
+ if (optlen >= CILEN_BSD_COMPRESS) {
+ printer(arg, "bsd v%d %d", BSD_VERSION(p[2]),
+ BSD_NBITS(p[2]));
+ p += CILEN_BSD_COMPRESS;
+ }
+ break;
+ case CI_PREDICTOR_1:
+ if (optlen >= CILEN_PREDICTOR_1) {
+ printer(arg, "predictor 1");
+ p += CILEN_PREDICTOR_1;
+ }
+ break;
+ case CI_PREDICTOR_2:
+ if (optlen >= CILEN_PREDICTOR_2) {
+ printer(arg, "predictor 2");
+ p += CILEN_PREDICTOR_2;
+ }
+ break;
+ }
+ while (p < optend)
+ printer(arg, " %.2x", *p++);
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* dump out the rest of the packet in hex */
+ while (--len >= 0)
+ printer(arg, " %.2x", *p++);
+
+ return p - p0;
+}
+
+/*
+ * We have received a packet that the decompressor failed to
+ * decompress. Here we would expect to issue a reset-request, but
+ * Motorola has a patent on resetting the compressor as a result of
+ * detecting an error in the decompressed data after decompression.
+ * (See US patent 5,130,993; international patent publication number
+ * WO 91/10289; Australian patent 73296/91.)
+ *
+ * So we ask the kernel whether the error was detected after
+ * decompression; if it was, we take CCP down, thus disabling
+ * compression :-(, otherwise we issue the reset-request.
+ */
+static void
+ccp_datainput(unit, pkt, len)
+ int unit;
+ u_char *pkt;
+ int len;
+{
+ fsm *f;
+
+ f = &ccp_fsm[unit];
+ if (f->state == OPENED) {
+ if (ccp_fatal_error(unit)) {
+ /*
+ * Disable compression by taking CCP down.
+ */
+ error("Lost compression sync: disabling compression");
+ ccp_close(unit, "Lost compression sync");
+ } else {
+ /*
+ * Send a reset-request to reset the peer's compressor.
+ * We don't do that if we are still waiting for an
+ * acknowledgement to a previous reset-request.
+ */
+ if (!(ccp_localstate[f->unit] & RACK_PENDING)) {
+ fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ ccp_localstate[f->unit] |= RACK_PENDING;
+ } else
+ ccp_localstate[f->unit] |= RREQ_REPEAT;
+ }
+ }
+}
+
+/*
+ * Timeout waiting for reset-ack.
+ */
+static void
+ccp_rack_timeout(arg)
+ void *arg;
+{
+ fsm *f = arg;
+
+ if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) {
+ fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ ccp_localstate[f->unit] &= ~RREQ_REPEAT;
+ } else
+ ccp_localstate[f->unit] &= ~RACK_PENDING;
+}
+
diff --git a/mdk-stage1/ppp/pppd/ccp.h b/mdk-stage1/ppp/pppd/ccp.h
new file mode 100644
index 000000000..609d858c5
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/ccp.h
@@ -0,0 +1,48 @@
+/*
+ * ccp.h - Definitions for PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+typedef struct ccp_options {
+ bool bsd_compress; /* do BSD Compress? */
+ bool deflate; /* do Deflate? */
+ bool predictor_1; /* do Predictor-1? */
+ bool predictor_2; /* do Predictor-2? */
+ bool deflate_correct; /* use correct code for deflate? */
+ bool deflate_draft; /* use draft RFC code for deflate? */
+ u_short bsd_bits; /* # bits/code for BSD Compress */
+ u_short deflate_size; /* lg(window size) for Deflate */
+ short method; /* code for chosen compression method */
+} ccp_options;
+
+extern fsm ccp_fsm[];
+extern ccp_options ccp_wantoptions[];
+extern ccp_options ccp_gotoptions[];
+extern ccp_options ccp_allowoptions[];
+extern ccp_options ccp_hisoptions[];
+
+extern struct protent ccp_protent;
diff --git a/mdk-stage1/ppp/pppd/chap.c b/mdk-stage1/ppp/pppd/chap.c
new file mode 100644
index 000000000..54c0e0095
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/chap.c
@@ -0,0 +1,860 @@
+/*
+ * chap.c - Challenge Handshake Authentication Protocol.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Gregory M. Christy. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "pppd.h"
+#include "chap.h"
+#include "md5.h"
+#ifdef CHAPMS
+#include "chap_ms.h"
+#endif
+
+static const char rcsid[] = RCSID;
+
+/*
+ * Command-line options.
+ */
+static option_t chap_option_list[] = {
+ { "chap-restart", o_int, &chap[0].timeouttime,
+ "Set timeout for CHAP", OPT_PRIO },
+ { "chap-max-challenge", o_int, &chap[0].max_transmits,
+ "Set max #xmits for challenge", OPT_PRIO },
+ { "chap-interval", o_int, &chap[0].chal_interval,
+ "Set interval for rechallenge", OPT_PRIO },
+#ifdef MSLANMAN
+ { "ms-lanman", o_bool, &ms_lanman,
+ "Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+ { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void ChapInit __P((int));
+static void ChapLowerUp __P((int));
+static void ChapLowerDown __P((int));
+static void ChapInput __P((int, u_char *, int));
+static void ChapProtocolReject __P((int));
+static int ChapPrintPkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+
+struct protent chap_protent = {
+ PPP_CHAP,
+ ChapInit,
+ ChapInput,
+ ChapProtocolReject,
+ ChapLowerUp,
+ ChapLowerDown,
+ NULL,
+ NULL,
+ ChapPrintPkt,
+ NULL,
+ 1,
+ "CHAP",
+ NULL,
+ chap_option_list,
+ NULL,
+ NULL,
+ NULL
+};
+
+chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
+
+static void ChapChallengeTimeout __P((void *));
+static void ChapResponseTimeout __P((void *));
+static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
+static void ChapRechallenge __P((void *));
+static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
+static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
+static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
+static void ChapSendStatus __P((chap_state *, int));
+static void ChapSendChallenge __P((chap_state *));
+static void ChapSendResponse __P((chap_state *));
+static void ChapGenChallenge __P((chap_state *));
+
+extern double drand48 __P((void));
+extern void srand48 __P((long));
+
+/*
+ * ChapInit - Initialize a CHAP unit.
+ */
+static void
+ChapInit(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ BZERO(cstate, sizeof(*cstate));
+ cstate->unit = unit;
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+ cstate->timeouttime = CHAP_DEFTIMEOUT;
+ cstate->max_transmits = CHAP_DEFTRANSMITS;
+ /* random number generator is initialized in magic_init */
+}
+
+
+/*
+ * ChapAuthWithPeer - Authenticate us with our peer (start client).
+ *
+ */
+void
+ChapAuthWithPeer(unit, our_name, digest)
+ int unit;
+ char *our_name;
+ int digest;
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->resp_name = our_name;
+ cstate->resp_type = digest;
+
+ if (cstate->clientstate == CHAPCS_INITIAL ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->clientstate = CHAPCS_PENDING;
+ return;
+ }
+
+ /*
+ * We get here as a result of LCP coming up.
+ * So even if CHAP was open before, we will
+ * have to re-authenticate ourselves.
+ */
+ cstate->clientstate = CHAPCS_LISTEN;
+}
+
+
+/*
+ * ChapAuthPeer - Authenticate our peer (start server).
+ */
+void
+ChapAuthPeer(unit, our_name, digest)
+ int unit;
+ char *our_name;
+ int digest;
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->chal_name = our_name;
+ cstate->chal_type = digest;
+
+ if (cstate->serverstate == CHAPSS_INITIAL ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->serverstate = CHAPSS_PENDING;
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate); /* crank it up dude! */
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+}
+
+
+/*
+ * ChapChallengeTimeout - Timeout expired on sending challenge.
+ */
+static void
+ChapChallengeTimeout(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending challenges, don't worry. then again we */
+ /* probably shouldn't be here either */
+ if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
+ cstate->serverstate != CHAPSS_RECHALLENGE)
+ return;
+
+ if (cstate->chal_transmits >= cstate->max_transmits) {
+ /* give up on peer */
+ error("Peer failed to respond to CHAP challenge");
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ return;
+ }
+
+ ChapSendChallenge(cstate); /* Re-send challenge */
+}
+
+
+/*
+ * ChapResponseTimeout - Timeout expired on sending response.
+ */
+static void
+ChapResponseTimeout(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->clientstate != CHAPCS_RESPONSE)
+ return;
+
+ ChapSendResponse(cstate); /* re-send response */
+}
+
+
+/*
+ * ChapRechallenge - Time to challenge the peer again.
+ */
+static void
+ChapRechallenge(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->serverstate != CHAPSS_OPEN)
+ return;
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_RECHALLENGE;
+}
+
+
+/*
+ * ChapLowerUp - The lower layer is up.
+ *
+ * Start up if we have pending requests.
+ */
+static void
+ChapLowerUp(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->clientstate == CHAPCS_INITIAL)
+ cstate->clientstate = CHAPCS_CLOSED;
+ else if (cstate->clientstate == CHAPCS_PENDING)
+ cstate->clientstate = CHAPCS_LISTEN;
+
+ if (cstate->serverstate == CHAPSS_INITIAL)
+ cstate->serverstate = CHAPSS_CLOSED;
+ else if (cstate->serverstate == CHAPSS_PENDING) {
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+ }
+}
+
+
+/*
+ * ChapLowerDown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+ChapLowerDown(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ /* Timeout(s) pending? Cancel if so. */
+ if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
+ cstate->serverstate == CHAPSS_RECHALLENGE)
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+ else if (cstate->serverstate == CHAPSS_OPEN
+ && cstate->chal_interval != 0)
+ UNTIMEOUT(ChapRechallenge, cstate);
+ if (cstate->clientstate == CHAPCS_RESPONSE)
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+}
+
+
+/*
+ * ChapProtocolReject - Peer doesn't grok CHAP.
+ */
+static void
+ChapProtocolReject(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->serverstate != CHAPSS_INITIAL &&
+ cstate->serverstate != CHAPSS_CLOSED)
+ auth_peer_fail(unit, PPP_CHAP);
+ if (cstate->clientstate != CHAPCS_INITIAL &&
+ cstate->clientstate != CHAPCS_CLOSED)
+ auth_withpeer_fail(unit, PPP_CHAP);
+ ChapLowerDown(unit); /* shutdown chap */
+}
+
+
+/*
+ * ChapInput - Input CHAP packet.
+ */
+static void
+ChapInput(unit, inpacket, packet_len)
+ int unit;
+ u_char *inpacket;
+ int packet_len;
+{
+ chap_state *cstate = &chap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (packet_len < CHAP_HEADERLEN) {
+ CHAPDEBUG(("ChapInput: rcvd short header."));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < CHAP_HEADERLEN) {
+ CHAPDEBUG(("ChapInput: rcvd illegal length."));
+ return;
+ }
+ if (len > packet_len) {
+ CHAPDEBUG(("ChapInput: rcvd short packet."));
+ return;
+ }
+ len -= CHAP_HEADERLEN;
+
+ /*
+ * Action depends on code (as in fact it usually does :-).
+ */
+ switch (code) {
+ case CHAP_CHALLENGE:
+ ChapReceiveChallenge(cstate, inp, id, len);
+ break;
+
+ case CHAP_RESPONSE:
+ ChapReceiveResponse(cstate, inp, id, len);
+ break;
+
+ case CHAP_FAILURE:
+ ChapReceiveFailure(cstate, inp, id, len);
+ break;
+
+ case CHAP_SUCCESS:
+ ChapReceiveSuccess(cstate, inp, id, len);
+ break;
+
+ default: /* Need code reject? */
+ warn("Unknown CHAP code (%d) received.", code);
+ break;
+ }
+}
+
+
+/*
+ * ChapReceiveChallenge - Receive Challenge and send Response.
+ */
+static void
+ChapReceiveChallenge(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ int id;
+ int len;
+{
+ int rchallenge_len;
+ u_char *rchallenge;
+ int secret_len;
+ char secret[MAXSECRETLEN];
+ char rhostname[256];
+ MD5_CTX mdContext;
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ if (cstate->clientstate == CHAPCS_CLOSED ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
+ return;
+ }
+
+ GETCHAR(rchallenge_len, inp);
+ len -= sizeof (u_char) + rchallenge_len; /* now name field length */
+ if (len < 0) {
+ CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
+ return;
+ }
+ rchallenge = inp;
+ INCPTR(rchallenge_len, inp);
+
+ if (len >= sizeof(rhostname))
+ len = sizeof(rhostname) - 1;
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ /* Microsoft doesn't send their name back in the PPP packet */
+ if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) {
+ strlcpy(rhostname, remote_name, sizeof(rhostname));
+ CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
+ rhostname));
+ }
+
+ /* get secret for authenticating ourselves with the specified host */
+ if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
+ secret, &secret_len, 0)) {
+ secret_len = 0; /* assume null secret if can't find one */
+ warn("No CHAP secret found for authenticating us to %q", rhostname);
+ }
+
+ /* cancel response send timeout if necessary */
+ if (cstate->clientstate == CHAPCS_RESPONSE)
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ cstate->resp_id = id;
+ cstate->resp_transmits = 0;
+
+ /* generate MD based on negotiated type */
+ switch (cstate->resp_type) {
+
+ case CHAP_DIGEST_MD5:
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->resp_id, 1);
+ MD5Update(&mdContext, secret, secret_len);
+ MD5Update(&mdContext, rchallenge, rchallenge_len);
+ MD5Final(hash, &mdContext);
+ BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
+ cstate->resp_length = MD5_SIGNATURE_SIZE;
+ break;
+
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
+ break;
+#endif
+
+ default:
+ CHAPDEBUG(("unknown digest type %d", cstate->resp_type));
+ return;
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendResponse(cstate);
+}
+
+
+/*
+ * ChapReceiveResponse - Receive and process response.
+ */
+static void
+ChapReceiveResponse(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char *remmd, remmd_len;
+ int secret_len, old_state;
+ int code;
+ char rhostname[256];
+ MD5_CTX mdContext;
+ char secret[MAXSECRETLEN];
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ if (cstate->serverstate == CHAPSS_CLOSED ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate->serverstate));
+ return;
+ }
+
+ if (id != cstate->chal_id)
+ return; /* doesn't match ID of last challenge */
+
+ /*
+ * If we have received a duplicate or bogus Response,
+ * we have to send the same answer (Success/Failure)
+ * as we did for the first Response we saw.
+ */
+ if (cstate->serverstate == CHAPSS_OPEN) {
+ ChapSendStatus(cstate, CHAP_SUCCESS);
+ return;
+ }
+ if (cstate->serverstate == CHAPSS_BADAUTH) {
+ ChapSendStatus(cstate, CHAP_FAILURE);
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
+ return;
+ }
+ GETCHAR(remmd_len, inp); /* get length of MD */
+ remmd = inp; /* get pointer to MD */
+ INCPTR(remmd_len, inp);
+
+ len -= sizeof (u_char) + remmd_len;
+ if (len < 0) {
+ CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
+ return;
+ }
+
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+
+ if (len >= sizeof(rhostname))
+ len = sizeof(rhostname) - 1;
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ /*
+ * Get secret for authenticating them with us,
+ * do the hash ourselves, and compare the result.
+ */
+ code = CHAP_FAILURE;
+ if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
+ cstate->chal_name, secret, &secret_len, 1)) {
+ warn("No CHAP secret found for authenticating %q", rhostname);
+ } else {
+
+ /* generate MD based on negotiated type */
+ switch (cstate->chal_type) {
+
+ case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
+ if (remmd_len != MD5_SIGNATURE_SIZE)
+ break; /* it's not even the right length */
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->chal_id, 1);
+ MD5Update(&mdContext, secret, secret_len);
+ MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+ MD5Final(hash, &mdContext);
+
+ /* compare local and remote MDs and send the appropriate status */
+ if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
+ code = CHAP_SUCCESS; /* they are the same! */
+ break;
+
+ default:
+ CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
+ }
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendStatus(cstate, code);
+
+ if (code == CHAP_SUCCESS) {
+ old_state = cstate->serverstate;
+ cstate->serverstate = CHAPSS_OPEN;
+ if (old_state == CHAPSS_INITIAL_CHAL) {
+ auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
+ }
+ if (cstate->chal_interval != 0)
+ TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
+ notice("CHAP peer authentication succeeded for %q", rhostname);
+
+ } else {
+ error("CHAP peer authentication failed for remote host %q", rhostname);
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ }
+}
+
+/*
+ * ChapReceiveSuccess - Receive Success
+ */
+static void
+ChapReceiveSuccess(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+
+ if (cstate->clientstate == CHAPCS_OPEN)
+ /* presumably an answer to a duplicate response */
+ return;
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ cstate->clientstate = CHAPCS_OPEN;
+
+ auth_withpeer_success(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapReceiveFailure - Receive failure.
+ */
+static void
+ChapReceiveFailure(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ error("CHAP authentication failed");
+ auth_withpeer_fail(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapSendChallenge - Send an Authenticate challenge.
+ */
+static void
+ChapSendChallenge(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int chal_len, name_len;
+ int outlen;
+
+ chal_len = cstate->chal_len;
+ name_len = strlen(cstate->chal_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
+
+ PUTCHAR(CHAP_CHALLENGE, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+
+ PUTCHAR(chal_len, outp); /* put length of challenge */
+ BCOPY(cstate->challenge, outp, chal_len);
+ INCPTR(chal_len, outp);
+
+ BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
+
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
+ ++cstate->chal_transmits;
+}
+
+
+/*
+ * ChapSendStatus - Send a status response (ack or nak).
+ */
+static void
+ChapSendStatus(cstate, code)
+ chap_state *cstate;
+ int code;
+{
+ u_char *outp;
+ int outlen, msglen;
+ char msg[256];
+
+ if (code == CHAP_SUCCESS)
+ slprintf(msg, sizeof(msg), "Welcome to %s.", hostname);
+ else
+ slprintf(msg, sizeof(msg), "I don't like you. Go 'way.");
+ msglen = strlen(msg);
+
+ outlen = CHAP_HEADERLEN + msglen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
+
+ PUTCHAR(code, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+ BCOPY(msg, outp, msglen);
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+/*
+ * ChapGenChallenge is used to generate a pseudo-random challenge string of
+ * a pseudo-random length between min_len and max_len. The challenge
+ * string and its length are stored in *cstate, and various other fields of
+ * *cstate are initialized.
+ */
+
+static void
+ChapGenChallenge(cstate)
+ chap_state *cstate;
+{
+ int chal_len;
+ u_char *ptr = cstate->challenge;
+ int i;
+
+ /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
+ MAX_CHALLENGE_LENGTH */
+ chal_len = (unsigned) ((drand48() *
+ (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+ MIN_CHALLENGE_LENGTH);
+ cstate->chal_len = chal_len;
+ cstate->chal_id = ++cstate->id;
+ cstate->chal_transmits = 0;
+
+ /* generate a random string */
+ for (i = 0; i < chal_len; i++)
+ *ptr++ = (char) (drand48() * 0xff);
+}
+
+/*
+ * ChapSendResponse - send a response packet with values as specified
+ * in *cstate.
+ */
+/* ARGSUSED */
+static void
+ChapSendResponse(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int outlen, md_len, name_len;
+
+ md_len = cstate->resp_length;
+ name_len = strlen(cstate->resp_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP);
+
+ PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
+ PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
+ PUTSHORT(outlen, outp); /* packet length */
+
+ PUTCHAR(md_len, outp); /* length of MD */
+ BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
+ INCPTR(md_len, outp);
+
+ BCOPY(cstate->resp_name, outp, name_len); /* append our name */
+
+ /* send the packet */
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ cstate->clientstate = CHAPCS_RESPONSE;
+ TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
+ ++cstate->resp_transmits;
+}
+
+/*
+ * ChapPrintPkt - print the contents of a CHAP packet.
+ */
+static char *ChapCodenames[] = {
+ "Challenge", "Response", "Success", "Failure"
+};
+
+static int
+ChapPrintPkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len;
+ int clen, nlen;
+ u_char x;
+
+ if (plen < CHAP_HEADERLEN)
+ return 0;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < CHAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
+ printer(arg, " %s", ChapCodenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= CHAP_HEADERLEN;
+ switch (code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (len < 1)
+ break;
+ clen = p[0];
+ if (len < clen + 1)
+ break;
+ ++p;
+ nlen = len - clen - 1;
+ printer(arg, " <");
+ for (; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, "%.2x", x);
+ }
+ printer(arg, ">, name = ");
+ print_string((char *)p, nlen, printer, arg);
+ break;
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ break;
+ default:
+ for (clen = len; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+ }
+
+ return len + CHAP_HEADERLEN;
+}
diff --git a/mdk-stage1/ppp/pppd/chap.h b/mdk-stage1/ppp/pppd/chap.h
new file mode 100644
index 000000000..945d051d0
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/chap.h
@@ -0,0 +1,124 @@
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the author.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+#ifndef __CHAP_INCLUDE__
+
+/* Code + ID + length */
+#define CHAP_HEADERLEN 4
+
+/*
+ * CHAP codes.
+ */
+
+#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */
+#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */
+#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */
+#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+/*
+ * Challenge lengths (for challenges we send) and other limits.
+ */
+#define MIN_CHALLENGE_LENGTH 16
+#define MAX_CHALLENGE_LENGTH 24
+#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */
+
+/*
+ * Each interface is described by a chap structure.
+ */
+
+typedef struct chap_state {
+ int unit; /* Interface unit number */
+ int clientstate; /* Client state */
+ int serverstate; /* Server state */
+ u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
+ u_char chal_len; /* challenge length */
+ u_char chal_id; /* ID of last challenge */
+ u_char chal_type; /* hash algorithm for challenges */
+ u_char id; /* Current id */
+ char *chal_name; /* Our name to use with challenge */
+ int chal_interval; /* Time until we challenge peer again */
+ int timeouttime; /* Timeout time in seconds */
+ int max_transmits; /* Maximum # of challenge transmissions */
+ int chal_transmits; /* Number of transmissions of challenge */
+ int resp_transmits; /* Number of transmissions of response */
+ u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */
+ u_char resp_length; /* length of response */
+ u_char resp_id; /* ID for response messages */
+ u_char resp_type; /* hash algorithm for responses */
+ char *resp_name; /* Our name to send with response */
+} chap_state;
+
+
+/*
+ * Client (peer) states.
+ */
+#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */
+#define CHAPCS_LISTEN 3 /* Listening for a challenge */
+#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */
+#define CHAPCS_OPEN 5 /* We've received Success */
+
+/*
+ * Server (authenticator) states.
+ */
+#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPSS_PENDING 2 /* Auth peer when lower up */
+#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */
+#define CHAPSS_OPEN 4 /* We've sent a Success msg */
+#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */
+#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */
+
+/*
+ * Timeouts.
+ */
+#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */
+#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */
+
+extern chap_state chap[];
+
+void ChapAuthWithPeer __P((int, char *, int));
+void ChapAuthPeer __P((int, char *, int));
+
+extern struct protent chap_protent;
+
+#define __CHAP_INCLUDE__
+#endif /* __CHAP_INCLUDE__ */
diff --git a/mdk-stage1/ppp/pppd/chap_ms.c b/mdk-stage1/ppp/pppd/chap_ms.c
new file mode 100644
index 000000000..81a57bb5a
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/chap_ms.c
@@ -0,0 +1,338 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ * Implemented LANManager type password response to MS-CHAP challenges.
+ * Now pppd provides both NT style and LANMan style blocks, and the
+ * prefered is set by option "ms-lanman". Default is to use NT.
+ * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+#define RCSID "$Id$"
+
+#ifdef CHAPMS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#include "pppd.h"
+#include "chap.h"
+#include "chap_ms.h"
+#include "md4.h"
+
+#ifndef USE_CRYPT
+#include <des.h>
+#endif
+
+static const char rcsid[] = RCSID;
+
+typedef struct {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+} MS_ChapResponse;
+/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
+ in case this struct gets padded. */
+
+
+static void ChallengeResponse __P((u_char *, u_char *, u_char *));
+static void DesEncrypt __P((u_char *, u_char *, u_char *));
+static void MakeKey __P((u_char *, u_char *));
+static u_char Get7Bits __P((u_char *, int));
+static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *));
+#ifdef MSLANMAN
+static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *));
+#endif
+
+#ifdef USE_CRYPT
+static void Expand __P((u_char *, u_char *));
+static void Collapse __P((u_char *, u_char *));
+#endif
+
+#ifdef MSLANMAN
+bool ms_lanman = 0; /* Use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+static void
+ChallengeResponse(challenge, pwHash, response)
+ u_char *challenge; /* IN 8 octets */
+ u_char *pwHash; /* IN 16 octets */
+ u_char *response; /* OUT 24 octets */
+{
+ char ZPasswordHash[21];
+
+ BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+ BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
+
+#if 0
+ dbglog("ChallengeResponse - ZPasswordHash %.*B",
+ sizeof(ZPasswordHash), ZPasswordHash);
+#endif
+
+ DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
+ DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
+ DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+
+#if 0
+ dbglog("ChallengeResponse - response %.24B", response);
+#endif
+}
+
+
+#ifdef USE_CRYPT
+static void
+DesEncrypt(clear, key, cipher)
+ u_char *clear; /* IN 8 octets */
+ u_char *key; /* IN 7 octets */
+ u_char *cipher; /* OUT 8 octets */
+{
+ u_char des_key[8];
+ u_char crypt_key[66];
+ u_char des_input[66];
+
+ MakeKey(key, des_key);
+
+ Expand(des_key, crypt_key);
+ setkey(crypt_key);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
+#endif
+
+ Expand(clear, des_input);
+ encrypt(des_input, 0);
+ Collapse(des_input, cipher);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
+#endif
+}
+
+#else /* USE_CRYPT */
+
+static void
+DesEncrypt(clear, key, cipher)
+ u_char *clear; /* IN 8 octets */
+ u_char *key; /* IN 7 octets */
+ u_char *cipher; /* OUT 8 octets */
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ MakeKey(key, des_key);
+
+ des_set_key(&des_key, key_schedule);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
+#endif
+
+ des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
+#endif
+}
+
+#endif /* USE_CRYPT */
+
+
+static u_char Get7Bits(input, startBit)
+ u_char *input;
+ int startBit;
+{
+ register unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+#ifdef USE_CRYPT
+
+/* in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void Expand(in, out)
+ u_char *in;
+ u_char *out;
+{
+ int j, c;
+ int i;
+
+ for(i = 0; i < 64; in++){
+ c = *in;
+ for(j = 7; j >= 0; j--)
+ *out++ = (c >> j) & 01;
+ i += 8;
+ }
+}
+
+/* The inverse of Expand
+ */
+static void Collapse(in, out)
+ u_char *in;
+ u_char *out;
+{
+ int j;
+ int i;
+ unsigned int c;
+
+ for (i = 0; i < 64; i += 8, out++) {
+ c = 0;
+ for (j = 7; j >= 0; j--, in++)
+ c |= *in << j;
+ *out = c & 0xff;
+ }
+}
+#endif
+
+static void MakeKey(key, des_key)
+ u_char *key; /* IN 56 bit DES key missing parity bits */
+ u_char *des_key; /* OUT 64 bit DES key with parity bits added */
+{
+ des_key[0] = Get7Bits(key, 0);
+ des_key[1] = Get7Bits(key, 7);
+ des_key[2] = Get7Bits(key, 14);
+ des_key[3] = Get7Bits(key, 21);
+ des_key[4] = Get7Bits(key, 28);
+ des_key[5] = Get7Bits(key, 35);
+ des_key[6] = Get7Bits(key, 42);
+ des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+ des_set_odd_parity((des_cblock *)des_key);
+#endif
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key));
+ CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key));
+#endif
+}
+
+static void
+ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response)
+ char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+ MS_ChapResponse *response;
+{
+ int i;
+#ifdef __NetBSD__
+ /* NetBSD uses the libc md4 routines which take bytes instead of bits */
+ int mdlen = secret_len * 2;
+#else
+ int mdlen = secret_len * 2 * 8;
+#endif
+ MD4_CTX md4Context;
+ u_char hash[MD4_SIGNATURE_SIZE];
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+
+ /* Initialize the Unicode version of the secret (== password). */
+ /* This implicitly supports 8-bit ISO8859/1 characters. */
+ BZERO(unicodePassword, sizeof(unicodePassword));
+ for (i = 0; i < secret_len; i++)
+ unicodePassword[i * 2] = (u_char)secret[i];
+
+ MD4Init(&md4Context);
+ MD4Update(&md4Context, unicodePassword, mdlen);
+
+ MD4Final(hash, &md4Context); /* Tell MD4 we're done */
+
+ ChallengeResponse(rchallenge, hash, response->NTResp);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void
+ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response)
+ char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+ MS_ChapResponse *response;
+{
+ int i;
+ u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+
+ /* LANMan password is case insensitive */
+ BZERO(UcasePassword, sizeof(UcasePassword));
+ for (i = 0; i < secret_len; i++)
+ UcasePassword[i] = (u_char)toupper(secret[i]);
+ DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
+ DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
+ ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+}
+#endif
+
+void
+ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
+ chap_state *cstate;
+ char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+{
+ MS_ChapResponse response;
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
+#endif
+ BZERO(&response, sizeof(response));
+
+ /* Calculate both always */
+ ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+#ifdef MSLANMAN
+ ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+ /* prefered method is set by option */
+ response.UseNT = !ms_lanman;
+#else
+ response.UseNT = 1;
+#endif
+
+ BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
+ cstate->resp_length = MS_CHAP_RESPONSE_LEN;
+}
+
+#endif /* CHAPMS */
diff --git a/mdk-stage1/ppp/pppd/chap_ms.h b/mdk-stage1/ppp/pppd/chap_ms.h
new file mode 100644
index 000000000..fad2755c8
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/chap_ms.h
@@ -0,0 +1,33 @@
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+#ifndef __CHAPMS_INCLUDE__
+
+#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */
+#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */
+
+void ChapMS __P((chap_state *, char *, int, char *, int));
+
+#define __CHAPMS_INCLUDE__
+#endif /* __CHAPMS_INCLUDE__ */
diff --git a/mdk-stage1/ppp/pppd/demand.c b/mdk-stage1/ppp/pppd/demand.c
new file mode 100644
index 000000000..9a740be8d
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/demand.c
@@ -0,0 +1,351 @@
+/*
+ * demand.c - Support routines for demand-dialling.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#ifdef PPP_FILTER
+#include <net/if.h>
+#include <net/bpf.h>
+#include <pcap.h>
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "lcp.h"
+
+static const char rcsid[] = RCSID;
+
+char *frame;
+int framelen;
+int framemax;
+int escape_flag;
+int flush_flag;
+int fcs;
+
+struct packet {
+ int length;
+ struct packet *next;
+ unsigned char data[1];
+};
+
+struct packet *pend_q;
+struct packet *pend_qtail;
+
+static int active_packet __P((unsigned char *, int));
+
+/*
+ * demand_conf - configure the interface for doing dial-on-demand.
+ */
+void
+demand_conf()
+{
+ int i;
+ struct protent *protp;
+
+/* framemax = lcp_allowoptions[0].mru;
+ if (framemax < PPP_MRU) */
+ framemax = PPP_MRU;
+ framemax += PPP_HDRLEN + PPP_FCSLEN;
+ frame = malloc(framemax);
+ if (frame == NULL)
+ novm("demand frame");
+ framelen = 0;
+ pend_q = NULL;
+ escape_flag = 0;
+ flush_flag = 0;
+ fcs = PPP_INITFCS;
+
+ netif_set_mtu(0, MIN(lcp_allowoptions[0].mru, PPP_MRU));
+ ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
+ ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
+
+#ifdef PPP_FILTER
+ set_filters(&pass_filter, &active_filter);
+#endif
+
+ /*
+ * Call the demand_conf procedure for each protocol that's got one.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ if (!((*protp->demand_conf)(0)))
+ die(1);
+}
+
+
+/*
+ * demand_block - set each network protocol to block further packets.
+ */
+void
+demand_block()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_QUEUE);
+ get_loop_output();
+}
+
+/*
+ * demand_discard - set each network protocol to discard packets
+ * with an error.
+ */
+void
+demand_discard()
+{
+ struct packet *pkt, *nextpkt;
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_ERROR);
+ get_loop_output();
+
+ /* discard all saved packets */
+ for (pkt = pend_q; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ free(pkt);
+ }
+ pend_q = NULL;
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+}
+
+/*
+ * demand_unblock - set each enabled network protocol to pass packets.
+ */
+void
+demand_unblock()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_PASS);
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*
+ * loop_chars - process characters received from the loopback.
+ * Calls loop_frame when a complete frame has been accumulated.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+loop_chars(p, n)
+ unsigned char *p;
+ int n;
+{
+ int c, rv;
+
+ rv = 0;
+ for (; n > 0; --n) {
+ c = *p++;
+ if (c == PPP_FLAG) {
+ if (!escape_flag && !flush_flag
+ && framelen > 2 && fcs == PPP_GOODFCS) {
+ framelen -= 2;
+ if (loop_frame((unsigned char *)frame, framelen))
+ rv = 1;
+ }
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+ continue;
+ }
+ if (flush_flag)
+ continue;
+ if (escape_flag) {
+ c ^= PPP_TRANS;
+ escape_flag = 0;
+ } else if (c == PPP_ESCAPE) {
+ escape_flag = 1;
+ continue;
+ }
+ if (framelen >= framemax) {
+ flush_flag = 1;
+ continue;
+ }
+ frame[framelen++] = c;
+ fcs = PPP_FCS(fcs, c);
+ }
+ return rv;
+}
+
+/*
+ * loop_frame - given a frame obtained from the loopback,
+ * decide whether to bring up the link or not, and, if we want
+ * to transmit this frame later, put it on the pending queue.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ * We assume that the kernel driver has already applied the
+ * pass_filter, so we won't get packets it rejected.
+ * We apply the active_filter to see if we want this packet to
+ * bring up the link.
+ */
+int
+loop_frame(frame, len)
+ unsigned char *frame;
+ int len;
+{
+ struct packet *pkt;
+
+ /* dbglog("from loop: %P", frame, len); */
+ if (len < PPP_HDRLEN)
+ return 0;
+ if ((PPP_PROTOCOL(frame) & 0x8000) != 0)
+ return 0; /* shouldn't get any of these anyway */
+ if (!active_packet(frame, len))
+ return 0;
+
+ pkt = (struct packet *) malloc(sizeof(struct packet) + len);
+ if (pkt != NULL) {
+ pkt->length = len;
+ pkt->next = NULL;
+ memcpy(pkt->data, frame, len);
+ if (pend_q == NULL)
+ pend_q = pkt;
+ else
+ pend_qtail->next = pkt;
+ pend_qtail = pkt;
+ }
+ return 1;
+}
+
+/*
+ * demand_rexmit - Resend all those frames which we got via the
+ * loopback, now that the real serial link is up.
+ */
+void
+demand_rexmit(proto)
+ int proto;
+{
+ struct packet *pkt, *prev, *nextpkt;
+
+ prev = NULL;
+ pkt = pend_q;
+ pend_q = NULL;
+ for (; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ if (PPP_PROTOCOL(pkt->data) == proto) {
+ output(0, pkt->data, pkt->length);
+ free(pkt);
+ } else {
+ if (prev == NULL)
+ pend_q = pkt;
+ else
+ prev->next = pkt;
+ prev = pkt;
+ }
+ }
+ pend_qtail = prev;
+ if (prev != NULL)
+ prev->next = NULL;
+}
+
+/*
+ * Scan a packet to decide whether it is an "active" packet,
+ * that is, whether it is worth bringing up the link for.
+ */
+static int
+active_packet(p, len)
+ unsigned char *p;
+ int len;
+{
+ int proto, i;
+ struct protent *protp;
+
+ if (len < PPP_HDRLEN)
+ return 0;
+ proto = PPP_PROTOCOL(p);
+#ifdef PPP_FILTER
+ if (pass_filter.bf_len != 0
+ && bpf_filter(pass_filter.bf_insns, p, len, len) == 0)
+ return 0;
+ if (active_filter.bf_len != 0
+ && bpf_filter(active_filter.bf_insns, p, len, len) == 0)
+ return 0;
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
+ if (!protp->enabled_flag)
+ return 0;
+ if (protp->active_pkt == NULL)
+ return 1;
+ return (*protp->active_pkt)(p, len);
+ }
+ }
+ return 0; /* not a supported protocol !!?? */
+}
diff --git a/mdk-stage1/ppp/pppd/eui64.c b/mdk-stage1/ppp/pppd/eui64.c
new file mode 100644
index 000000000..7f3176035
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/eui64.c
@@ -0,0 +1,40 @@
+/*
+ eui64.c - EUI64 routines for IPv6CP.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms and that any documentation,
+ advertising materials, and other materials related to such
+ distribution and use acknowledge that the software was developed
+ by Tommi Komulainen. The name of the author may not be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+ $Id$
+*/
+
+#define RCSID "$Id$"
+
+#include "pppd.h"
+
+static const char rcsid[] = RCSID;
+
+/*
+ * eui64_ntoa - Make an ascii representation of an interface identifier
+ */
+char *
+eui64_ntoa(e)
+ eui64_t e;
+{
+ static char buf[32];
+
+ snprintf(buf, 32, "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ e.e8[0], e.e8[1], e.e8[2], e.e8[3],
+ e.e8[4], e.e8[5], e.e8[6], e.e8[7]);
+ return buf;
+}
diff --git a/mdk-stage1/ppp/pppd/eui64.h b/mdk-stage1/ppp/pppd/eui64.h
new file mode 100644
index 000000000..ca4445345
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/eui64.h
@@ -0,0 +1,97 @@
+/*
+ eui64.h - EUI64 routines for IPv6CP.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms and that any documentation,
+ advertising materials, and other materials related to such
+ distribution and use acknowledge that the software was developed
+ by Tommi Komulainen. The name of the author may not be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+ $Id$
+*/
+
+#ifndef __EUI64_H__
+#define __EUI64_H__
+
+#if !defined(INET6)
+#error "this file should only be included when INET6 is defined"
+#endif /* not defined(INET6) */
+
+#if defined(SOL2)
+#include <netinet/in.h>
+
+typedef union {
+ uint8_t e8[8]; /* lower 64-bit IPv6 address */
+ uint32_t e32[2]; /* lower 64-bit IPv6 address */
+} eui64_t;
+
+/*
+ * Declare the two below, since in.h only defines them when _KERNEL
+ * is declared - which shouldn't be true when dealing with user-land programs
+ */
+#define s6_addr8 _S6_un._S6_u8
+#define s6_addr32 _S6_un._S6_u32
+
+#else /* else if not defined(SOL2) */
+
+/*
+ * TODO:
+ *
+ * Maybe this should be done by processing struct in6_addr directly...
+ */
+typedef union
+{
+ u_int8_t e8[8];
+ u_int16_t e16[4];
+ u_int32_t e32[2];
+} eui64_t;
+
+#endif /* defined(SOL2) */
+
+#define eui64_iszero(e) (((e).e32[0] | (e).e32[1]) == 0)
+#define eui64_equals(e, o) (((e).e32[0] == (o).e32[0]) && \
+ ((e).e32[1] == (o).e32[1]))
+#define eui64_zero(e) (e).e32[0] = (e).e32[1] = 0;
+
+#define eui64_copy(s, d) memcpy(&(d), &(s), sizeof(eui64_t))
+
+#define eui64_magic(e) do { \
+ (e).e32[0] = magic(); \
+ (e).e32[1] = magic(); \
+ (e).e8[0] &= ~2; \
+ } while (0)
+#define eui64_magic_nz(x) do { \
+ eui64_magic(x); \
+ } while (eui64_iszero(x))
+#define eui64_magic_ne(x, y) do { \
+ eui64_magic(x); \
+ } while (eui64_equals(x, y))
+
+#define eui64_get(ll, cp) do { \
+ eui64_copy((*cp), (ll)); \
+ (cp) += sizeof(eui64_t); \
+ } while (0)
+
+#define eui64_put(ll, cp) do { \
+ eui64_copy((ll), (*cp)); \
+ (cp) += sizeof(eui64_t); \
+ } while (0)
+
+#define eui64_set32(e, l) do { \
+ (e).e32[0] = 0; \
+ (e).e32[1] = htonl(l); \
+ } while (0)
+#define eui64_setlo32(e, l) eui64_set32(e, l)
+
+char *eui64_ntoa __P((eui64_t)); /* Returns ascii representation of id */
+
+#endif /* __EUI64_H__ */
+
diff --git a/mdk-stage1/ppp/pppd/fsm.c b/mdk-stage1/ppp/pppd/fsm.c
new file mode 100644
index 000000000..07a8c11f1
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/fsm.c
@@ -0,0 +1,762 @@
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pppd.h"
+#include "fsm.h"
+
+static const char rcsid[] = RCSID;
+
+static void fsm_timeout __P((void *));
+static void fsm_rconfreq __P((fsm *, int, u_char *, int));
+static void fsm_rconfack __P((fsm *, int, u_char *, int));
+static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int));
+static void fsm_rtermreq __P((fsm *, int, u_char *, int));
+static void fsm_rtermack __P((fsm *));
+static void fsm_rcoderej __P((fsm *, u_char *, int));
+static void fsm_sconfreq __P((fsm *, int));
+
+#define PROTO_NAME(f) ((f)->callbacks->proto_name)
+
+int peer_mru[NUM_PPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(f)
+ fsm *f;
+{
+ f->state = INITIAL;
+ f->flags = 0;
+ f->id = 0; /* XXX Start with random id? */
+ f->timeouttime = DEFTIMEOUT;
+ f->maxconfreqtransmits = DEFMAXCONFREQS;
+ f->maxtermtransmits = DEFMAXTERMREQS;
+ f->maxnakloops = DEFMAXNAKLOOPS;
+ f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = CLOSED;
+ break;
+
+ case STARTING:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case CLOSED:
+ f->state = INITIAL;
+ break;
+
+ case STOPPED:
+ f->state = STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSING:
+ f->state = INITIAL;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ f->state = STARTING;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case OPENED:
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f);
+ f->state = STARTING;
+ break;
+
+ default:
+ FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSED:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ case CLOSING:
+ f->state = STOPPING;
+ /* fall through */
+ case STOPPED:
+ case OPENED:
+ if( f->flags & OPT_RESTART ){
+ fsm_lowerdown(f);
+ fsm_lowerup(f);
+ }
+ break;
+ }
+}
+
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the CLOSED state.
+ */
+void
+fsm_close(f, reason)
+ fsm *f;
+ char *reason;
+{
+ f->term_reason = reason;
+ f->term_reason_len = (reason == NULL? 0: strlen(reason));
+ switch( f->state ){
+ case STARTING:
+ f->state = INITIAL;
+ break;
+ case STOPPED:
+ f->state = CLOSED;
+ break;
+ case STOPPING:
+ f->state = CLOSING;
+ break;
+
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ case OPENED:
+ if( f->state != OPENED )
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ else if( f->callbacks->down )
+ (*f->callbacks->down)(f); /* Inform upper layers we're down */
+
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = CLOSING;
+ break;
+ }
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(arg)
+ void *arg;
+{
+ fsm *f = (fsm *) arg;
+
+ switch (f->state) {
+ case CLOSING:
+ case STOPPING:
+ if( f->retransmits <= 0 ){
+ /*
+ * We've waited for an ack long enough. Peer probably heard us.
+ */
+ f->state = (f->state == CLOSING)? CLOSED: STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ } else {
+ /* Send Terminate-Request */
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ }
+ break;
+
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ if (f->retransmits <= 0) {
+ warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f));
+ f->state = STOPPED;
+ if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+
+ } else {
+ /* Retransmit the configure-request */
+ if (f->callbacks->retransmit)
+ (*f->callbacks->retransmit)(f);
+ fsm_sconfreq(f, 1); /* Re-send Configure-Request */
+ if( f->state == ACKRCVD )
+ f->state = REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void
+fsm_input(f, inpacket, l)
+ fsm *f;
+ u_char *inpacket;
+ int l;
+{
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < HEADERLEN) {
+ FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < HEADERLEN) {
+ FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
+ return;
+ }
+ if (len > l) {
+ FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
+ return;
+ }
+ len -= HEADERLEN; /* subtract header length */
+
+ if( f->state == INITIAL || f->state == STARTING ){
+ FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.",
+ f->protocol, f->state));
+ return;
+ }
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case CONFREQ:
+ fsm_rconfreq(f, id, inp, len);
+ break;
+
+ case CONFACK:
+ fsm_rconfack(f, id, inp, len);
+ break;
+
+ case CONFNAK:
+ case CONFREJ:
+ fsm_rconfnakrej(f, code, id, inp, len);
+ break;
+
+ case TERMREQ:
+ fsm_rtermreq(f, id, inp, len);
+ break;
+
+ case TERMACK:
+ fsm_rtermack(f);
+ break;
+
+ case CODEREJ:
+ fsm_rcoderej(f, inp, len);
+ break;
+
+ default:
+ if( !f->callbacks->extcode
+ || !(*f->callbacks->extcode)(f, code, id, inp, len) )
+ fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(f, id, inp, len)
+ fsm *f;
+ u_char id;
+ u_char *inp;
+ int len;
+{
+ int code, reject_if_disagree;
+
+ switch( f->state ){
+ case CLOSED:
+ /* Go away, we're closed */
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ return;
+ case CLOSING:
+ case STOPPING:
+ return;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ break;
+
+ case STOPPED:
+ /* Negotiation started by our peer */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ break;
+ }
+
+ /*
+ * Pass the requested configuration options
+ * to protocol-specific code for checking.
+ */
+ if (f->callbacks->reqci){ /* Check CI */
+ reject_if_disagree = (f->nakloops >= f->maxnakloops);
+ code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+ } else if (len)
+ code = CONFREJ; /* Reject all CI */
+ else
+ code = CONFACK;
+
+ /* send the Ack, Nak or Rej to the peer */
+ fsm_sdata(f, code, id, inp, len);
+
+ if (code == CONFACK) {
+ if (f->state == ACKRCVD) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = OPENED;
+ if (f->callbacks->up)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ } else
+ f->state = ACKSENT;
+ f->nakloops = 0;
+
+ } else {
+ /* we sent CONFACK or CONFREJ */
+ if (f->state != ACKRCVD)
+ f->state = REQSENT;
+ if( code == CONFNAK )
+ ++f->nakloops;
+ }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(f, id, inp, len)
+ fsm *f;
+ int id;
+ u_char *inp;
+ int len;
+{
+ if (id != f->reqid || f->seen_ack) /* Expected id? */
+ return; /* Nope, toss... */
+ if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
+ (len == 0)) ){
+ /* Ack is bad - ignore it */
+ error("Received bad configure-ack: %P", inp, len);
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case CLOSED:
+ case STOPPED:
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ break;
+
+ case REQSENT:
+ f->state = ACKRCVD;
+ f->retransmits = f->maxconfreqtransmits;
+ break;
+
+ case ACKRCVD:
+ /* Huh? an extra valid Ack? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ break;
+
+ case ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = OPENED;
+ f->retransmits = f->maxconfreqtransmits;
+ if (f->callbacks->up)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ break;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(f, code, id, inp, len)
+ fsm *f;
+ int code, id;
+ u_char *inp;
+ int len;
+{
+ int (*proc) __P((fsm *, u_char *, int));
+ int ret;
+
+ if (id != f->reqid || f->seen_ack) /* Expected id? */
+ return; /* Nope, toss... */
+ proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
+ if (!proc || !(ret = proc(f, inp, len))) {
+ /* Nak/reject is bad - ignore it */
+ error("Received bad configure-nak/rej: %P", inp, len);
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case CLOSED:
+ case STOPPED:
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ break;
+
+ case REQSENT:
+ case ACKSENT:
+ /* They didn't agree to what we wanted - try another request */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ if (ret < 0)
+ f->state = STOPPED; /* kludge for stopping CCP */
+ else
+ fsm_sconfreq(f, 0); /* Send Configure-Request */
+ break;
+
+ case ACKRCVD:
+ /* Got a Nak/reject when we had already had an Ack?? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ break;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(f, id, p, len)
+ fsm *f;
+ int id;
+ u_char *p;
+ int len;
+{
+ switch (f->state) {
+ case ACKRCVD:
+ case ACKSENT:
+ f->state = REQSENT; /* Start over but keep trying */
+ break;
+
+ case OPENED:
+ if (len > 0) {
+ info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
+ } else
+ info("%s terminated by peer", PROTO_NAME(f));
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ f->retransmits = 0;
+ f->state = STOPPING;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ break;
+ }
+
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void
+fsm_rtermack(f)
+ fsm *f;
+{
+ switch (f->state) {
+ case CLOSING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = CLOSED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+ case STOPPING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case ACKRCVD:
+ f->state = REQSENT;
+ break;
+
+ case OPENED:
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0);
+ break;
+ }
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void
+fsm_rcoderej(f, inp, len)
+ fsm *f;
+ u_char *inp;
+ int len;
+{
+ u_char code, id;
+
+ if (len < HEADERLEN) {
+ FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
+
+ if( f->state == ACKRCVD )
+ f->state = REQSENT;
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case CLOSING:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case CLOSED:
+ f->state = CLOSED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case STOPPED:
+ f->state = STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case OPENED:
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f);
+
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = STOPPING;
+ break;
+
+ default:
+ FSMDEBUG(("%s: Protocol-reject event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(f, retransmit)
+ fsm *f;
+ int retransmit;
+{
+ u_char *outp;
+ int cilen;
+
+ if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
+ /* Not currently negotiating - reset options */
+ if( f->callbacks->resetci )
+ (*f->callbacks->resetci)(f);
+ f->nakloops = 0;
+ }
+
+ if( !retransmit ){
+ /* New request - reset retransmission counter, use new ID */
+ f->retransmits = f->maxconfreqtransmits;
+ f->reqid = ++f->id;
+ }
+
+ f->seen_ack = 0;
+
+ /*
+ * Make up the request packet
+ */
+ outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
+ if( f->callbacks->cilen && f->callbacks->addci ){
+ cilen = (*f->callbacks->cilen)(f);
+ if( cilen > peer_mru[f->unit] - HEADERLEN )
+ cilen = peer_mru[f->unit] - HEADERLEN;
+ if (f->callbacks->addci)
+ (*f->callbacks->addci)(f, outp, &cilen);
+ } else
+ cilen = 0;
+
+ /* send the request to our peer */
+ fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
+
+ /* start the retransmit timer */
+ --f->retransmits;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata(f, code, id, data, datalen)
+ fsm *f;
+ u_char code, id;
+ u_char *data;
+ int datalen;
+{
+ u_char *outp;
+ int outlen;
+
+ /* Adjust length to be smaller than MTU */
+ outp = outpacket_buf;
+ if (datalen > peer_mru[f->unit] - HEADERLEN)
+ datalen = peer_mru[f->unit] - HEADERLEN;
+ if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
+ BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
+ outlen = datalen + HEADERLEN;
+ MAKEHEADER(outp, f->protocol);
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
diff --git a/mdk-stage1/ppp/pppd/fsm.h b/mdk-stage1/ppp/pppd/fsm.h
new file mode 100644
index 000000000..c94a68e6c
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/fsm.h
@@ -0,0 +1,144 @@
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define HEADERLEN 4
+
+
+/*
+ * CP (LCP, IPCP, etc.) codes.
+ */
+#define CONFREQ 1 /* Configuration Request */
+#define CONFACK 2 /* Configuration Ack */
+#define CONFNAK 3 /* Configuration Nak */
+#define CONFREJ 4 /* Configuration Reject */
+#define TERMREQ 5 /* Termination Request */
+#define TERMACK 6 /* Termination Ack */
+#define CODEREJ 7 /* Code Reject */
+
+
+/*
+ * Each FSM is described by an fsm structure and fsm callbacks.
+ */
+typedef struct fsm {
+ int unit; /* Interface unit number */
+ int protocol; /* Data Link Layer Protocol field value */
+ int state; /* State */
+ int flags; /* Contains option bits */
+ u_char id; /* Current id */
+ u_char reqid; /* Current request id */
+ u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */
+ int timeouttime; /* Timeout time in milliseconds */
+ int maxconfreqtransmits; /* Maximum Configure-Request transmissions */
+ int retransmits; /* Number of retransmissions left */
+ int maxtermtransmits; /* Maximum Terminate-Request transmissions */
+ int nakloops; /* Number of nak loops since last ack */
+ int maxnakloops; /* Maximum number of nak loops tolerated */
+ struct fsm_callbacks *callbacks; /* Callback routines */
+ char *term_reason; /* Reason for closing protocol */
+ int term_reason_len; /* Length of term_reason */
+} fsm;
+
+
+typedef struct fsm_callbacks {
+ void (*resetci) /* Reset our Configuration Information */
+ __P((fsm *));
+ int (*cilen) /* Length of our Configuration Information */
+ __P((fsm *));
+ void (*addci) /* Add our Configuration Information */
+ __P((fsm *, u_char *, int *));
+ int (*ackci) /* ACK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*nakci) /* NAK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*rejci) /* Reject our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*reqci) /* Request peer's Configuration Information */
+ __P((fsm *, u_char *, int *, int));
+ void (*up) /* Called when fsm reaches OPENED state */
+ __P((fsm *));
+ void (*down) /* Called when fsm leaves OPENED state */
+ __P((fsm *));
+ void (*starting) /* Called when we want the lower layer */
+ __P((fsm *));
+ void (*finished) /* Called when we don't want the lower layer */
+ __P((fsm *));
+ void (*protreject) /* Called when Protocol-Reject received */
+ __P((int));
+ void (*retransmit) /* Retransmission is necessary */
+ __P((fsm *));
+ int (*extcode) /* Called when unknown code received */
+ __P((fsm *, int, int, u_char *, int));
+ char *proto_name; /* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+/*
+ * Link states.
+ */
+#define INITIAL 0 /* Down, hasn't been opened */
+#define STARTING 1 /* Down, been opened */
+#define CLOSED 2 /* Up, hasn't been opened */
+#define STOPPED 3 /* Open, waiting for down event */
+#define CLOSING 4 /* Terminating the connection, not open */
+#define STOPPING 5 /* Terminating, but open */
+#define REQSENT 6 /* We've sent a Config Request */
+#define ACKRCVD 7 /* We've received a Config Ack */
+#define ACKSENT 8 /* We've sent a Config Ack */
+#define OPENED 9 /* Connection available */
+
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE 1 /* Don't die if we don't get a response */
+#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT 4 /* Wait for peer to speak first */
+
+
+/*
+ * Timeouts.
+ */
+#define DEFTIMEOUT 3 /* Timeout time in seconds */
+#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */
+#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */
+#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init __P((fsm *));
+void fsm_lowerup __P((fsm *));
+void fsm_lowerdown __P((fsm *));
+void fsm_open __P((fsm *));
+void fsm_close __P((fsm *, char *));
+void fsm_input __P((fsm *, u_char *, int));
+void fsm_protreject __P((fsm *));
+void fsm_sdata __P((fsm *, int, int, u_char *, int));
+
+
+/*
+ * Variables
+ */
+extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */
diff --git a/mdk-stage1/ppp/pppd/ipcp.c b/mdk-stage1/ppp/pppd/ipcp.c
new file mode 100644
index 000000000..ac5bd39fa
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/ipcp.c
@@ -0,0 +1,2054 @@
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "pathnames.h"
+
+static const char rcsid[] = RCSID;
+
+/* global vars */
+ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+u_int32_t netmask = 0; /* IP netmask to set on interface */
+
+bool disable_defaultip = 0; /* Don't use hostname for default IP adrs */
+
+/* Hook for a plugin to know when IP protocol has come up */
+void (*ip_up_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to know when IP protocol has come down */
+void (*ip_down_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to choose the remote IP address */
+void (*ip_choose_hook) __P((u_int32_t *)) = NULL;
+
+/* local vars */
+static int default_route_set[NUM_PPP]; /* Have set up a default route */
+static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */
+static bool usepeerdns; /* Ask peer for DNS addrs */
+static int ipcp_is_up; /* have called np_up() */
+static bool ask_for_local; /* request our address from peer */
+static char vj_value[8]; /* string form of vj option value */
+static char netmask_str[20]; /* string form of netmask value */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipcp_resetci __P((fsm *)); /* Reset our CI */
+static int ipcp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipcp_up __P((fsm *)); /* We're UP */
+static void ipcp_down __P((fsm *)); /* We're DOWN */
+static void ipcp_finished __P((fsm *)); /* Don't need lower layer */
+
+fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
+
+static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+ ipcp_resetci, /* Reset our Configuration Information */
+ ipcp_cilen, /* Length of our Configuration Information */
+ ipcp_addci, /* Add our Configuration Information */
+ ipcp_ackci, /* ACK our Configuration Information */
+ ipcp_nakci, /* NAK our Configuration Information */
+ ipcp_rejci, /* Reject our Configuration Information */
+ ipcp_reqci, /* Request peer's Configuration Information */
+ ipcp_up, /* Called when fsm reaches OPENED state */
+ ipcp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPCP" /* String name of protocol */
+};
+
+/*
+ * Command-line options.
+ */
+static int setvjslots __P((char **));
+static int setdnsaddr __P((char **));
+static int setwinsaddr __P((char **));
+static int setnetmask __P((char **));
+static int setipaddr __P((char *, char **, int));
+static void printipaddr __P((option_t *, void (*)(void *, char *,...),void *));
+
+static option_t ipcp_option_list[] = {
+ { "noip", o_bool, &ipcp_protent.enabled_flag,
+ "Disable IP and IPCP" },
+ { "-ip", o_bool, &ipcp_protent.enabled_flag,
+ "Disable IP and IPCP", OPT_ALIAS },
+
+ { "novj", o_bool, &ipcp_wantoptions[0].neg_vj,
+ "Disable VJ compression", OPT_A2CLR, &ipcp_allowoptions[0].neg_vj },
+ { "-vj", o_bool, &ipcp_wantoptions[0].neg_vj,
+ "Disable VJ compression", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_allowoptions[0].neg_vj },
+
+ { "novjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+ "Disable VJ connection-ID compression", OPT_A2CLR,
+ &ipcp_allowoptions[0].cflag },
+ { "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+ "Disable VJ connection-ID compression", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_allowoptions[0].cflag },
+
+ { "vj-max-slots", o_special, (void *)setvjslots,
+ "Set maximum VJ header slots",
+ OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, vj_value },
+
+ { "ipcp-accept-local", o_bool, &ipcp_wantoptions[0].accept_local,
+ "Accept peer's address for us", 1 },
+ { "ipcp-accept-remote", o_bool, &ipcp_wantoptions[0].accept_remote,
+ "Accept peer's address for it", 1 },
+
+ { "ipparam", o_string, &ipparam,
+ "Set ip script parameter", OPT_PRIO },
+
+ { "noipdefault", o_bool, &disable_defaultip,
+ "Don't use name for default IP adrs", 1 },
+
+ { "ms-dns", 1, (void *)setdnsaddr,
+ "DNS address for the peer's use" },
+ { "ms-wins", 1, (void *)setwinsaddr,
+ "Nameserver for SMB over TCP/IP for peer" },
+
+ { "ipcp-restart", o_int, &ipcp_fsm[0].timeouttime,
+ "Set timeout for IPCP", OPT_PRIO },
+ { "ipcp-max-terminate", o_int, &ipcp_fsm[0].maxtermtransmits,
+ "Set max #xmits for term-reqs", OPT_PRIO },
+ { "ipcp-max-configure", o_int, &ipcp_fsm[0].maxconfreqtransmits,
+ "Set max #xmits for conf-reqs", OPT_PRIO },
+ { "ipcp-max-failure", o_int, &ipcp_fsm[0].maxnakloops,
+ "Set max #conf-naks for IPCP", OPT_PRIO },
+
+ { "defaultroute", o_bool, &ipcp_wantoptions[0].default_route,
+ "Add default route", OPT_ENABLE|1, &ipcp_allowoptions[0].default_route },
+ { "nodefaultroute", o_bool, &ipcp_allowoptions[0].default_route,
+ "disable defaultroute option", OPT_A2CLR,
+ &ipcp_wantoptions[0].default_route },
+ { "-defaultroute", o_bool, &ipcp_allowoptions[0].default_route,
+ "disable defaultroute option", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_wantoptions[0].default_route },
+
+ { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp,
+ "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp },
+ { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+ "disable proxyarp option", OPT_A2CLR,
+ &ipcp_wantoptions[0].proxy_arp },
+ { "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+ "disable proxyarp option", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_wantoptions[0].proxy_arp },
+
+ { "usepeerdns", o_bool, &usepeerdns,
+ "Ask peer for DNS address(es)", 1 },
+
+ { "netmask", o_special, (void *)setnetmask,
+ "set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str },
+
+ { "IP addresses", o_wild, (void *) &setipaddr,
+ "set local and remote IP addresses",
+ OPT_NOARG | OPT_A2PRINTER, (void *) &printipaddr },
+
+ { NULL }
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init __P((int));
+static void ipcp_open __P((int));
+static void ipcp_close __P((int, char *));
+static void ipcp_lowerup __P((int));
+static void ipcp_lowerdown __P((int));
+static void ipcp_input __P((int, u_char *, int));
+static void ipcp_protrej __P((int));
+static int ipcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+static void ip_check_options __P((void));
+static int ip_demand_conf __P((int));
+static int ip_active_pkt __P((u_char *, int));
+static void create_resolv __P((u_int32_t, u_int32_t));
+
+struct protent ipcp_protent = {
+ PPP_IPCP,
+ ipcp_init,
+ ipcp_input,
+ ipcp_protrej,
+ ipcp_lowerup,
+ ipcp_lowerdown,
+ ipcp_open,
+ ipcp_close,
+ ipcp_printpkt,
+ NULL,
+ 1,
+ "IPCP",
+ "IP",
+ ipcp_option_list,
+ ip_check_options,
+ ip_demand_conf,
+ ip_active_pkt
+};
+
+static void ipcp_clear_addrs __P((int, u_int32_t, u_int32_t));
+static void ipcp_script __P((char *)); /* Run an up/down script */
+static void ipcp_script_done __P((void *));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */
+#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR 6 /* new-style single address option */
+#define CILEN_ADDRS 10 /* old-style dual address option */
+
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+ s_down,
+ s_up,
+} ipcp_script_state;
+static pid_t ipcp_script_pid;
+
+/*
+ * Make a string representation of a network IP address.
+ */
+char *
+ip_ntoa(ipaddr)
+u_int32_t ipaddr;
+{
+ static char b[64];
+
+ slprintf(b, sizeof(b), "%I", ipaddr);
+ return b;
+}
+
+/*
+ * Option parsing.
+ */
+
+/*
+ * setvjslots - set maximum number of connection slots for VJ compression
+ */
+static int
+setvjslots(argv)
+ char **argv;
+{
+ int value;
+
+ if (!int_option(*argv, &value))
+ return 0;
+ if (value < 2 || value > 16) {
+ option_error("vj-max-slots value must be between 2 and 16");
+ return 0;
+ }
+ ipcp_wantoptions [0].maxslotindex =
+ ipcp_allowoptions[0].maxslotindex = value - 1;
+ slprintf(vj_value, sizeof(vj_value), "%d", value);
+ return 1;
+}
+
+/*
+ * setdnsaddr - set the dns address(es)
+ */
+static int
+setdnsaddr(argv)
+ char **argv;
+{
+ u_int32_t dns;
+ struct hostent *hp;
+
+ dns = inet_addr(*argv);
+ if (dns == (u_int32_t) -1) {
+ if ((hp = gethostbyname(*argv)) == NULL) {
+ option_error("invalid address parameter '%s' for ms-dns option",
+ *argv);
+ return 0;
+ }
+ dns = *(u_int32_t *)hp->h_addr;
+ }
+
+ /* We take the last 2 values given, the 2nd-last as the primary
+ and the last as the secondary. If only one is given it
+ becomes both primary and secondary. */
+ if (ipcp_allowoptions[0].dnsaddr[1] == 0)
+ ipcp_allowoptions[0].dnsaddr[0] = dns;
+ else
+ ipcp_allowoptions[0].dnsaddr[0] = ipcp_allowoptions[0].dnsaddr[1];
+
+ /* always set the secondary address value. */
+ ipcp_allowoptions[0].dnsaddr[1] = dns;
+
+ return (1);
+}
+
+/*
+ * setwinsaddr - set the wins address(es)
+ * This is primrarly used with the Samba package under UNIX or for pointing
+ * the caller to the existing WINS server on a Windows NT platform.
+ */
+static int
+setwinsaddr(argv)
+ char **argv;
+{
+ u_int32_t wins;
+ struct hostent *hp;
+
+ wins = inet_addr(*argv);
+ if (wins == (u_int32_t) -1) {
+ if ((hp = gethostbyname(*argv)) == NULL) {
+ option_error("invalid address parameter '%s' for ms-wins option",
+ *argv);
+ return 0;
+ }
+ wins = *(u_int32_t *)hp->h_addr;
+ }
+
+ /* We take the last 2 values given, the 2nd-last as the primary
+ and the last as the secondary. If only one is given it
+ becomes both primary and secondary. */
+ if (ipcp_allowoptions[0].winsaddr[1] == 0)
+ ipcp_allowoptions[0].winsaddr[0] = wins;
+ else
+ ipcp_allowoptions[0].winsaddr[0] = ipcp_allowoptions[0].winsaddr[1];
+
+ /* always set the secondary address value. */
+ ipcp_allowoptions[0].winsaddr[1] = wins;
+
+ return (1);
+}
+
+/*
+ * setipaddr - Set the IP address
+ * If doit is 0, the call is to check whether this option is
+ * potentially an IP address specification.
+ */
+static int
+setipaddr(arg, argv, doit)
+ char *arg;
+ char **argv;
+ int doit;
+{
+ struct hostent *hp;
+ char *colon;
+ u_int32_t local, remote;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+ static int prio_local = 0, prio_remote = 0;
+
+ /*
+ * IP address pair separated by ":".
+ */
+ if ((colon = strchr(arg, ':')) == NULL)
+ return 0;
+ if (!doit)
+ return 1;
+
+ /*
+ * If colon first character, then no local addr.
+ */
+ if (colon != arg && option_priority >= prio_local) {
+ *colon = '\0';
+ if ((local = inet_addr(arg)) == (u_int32_t) -1) {
+ if ((hp = gethostbyname(arg)) == NULL) {
+ option_error("unknown host: %s", arg);
+ return 0;
+ }
+ local = *(u_int32_t *)hp->h_addr;
+ }
+ if (bad_ip_adrs(local)) {
+ option_error("bad local IP address %s", ip_ntoa(local));
+ return 0;
+ }
+ if (local != 0)
+ wo->ouraddr = local;
+ *colon = ':';
+ prio_local = option_priority;
+ }
+
+ /*
+ * If colon last character, then no remote addr.
+ */
+ if (*++colon != '\0' && option_priority >= prio_remote) {
+ if ((remote = inet_addr(colon)) == (u_int32_t) -1) {
+ if ((hp = gethostbyname(colon)) == NULL) {
+ option_error("unknown host: %s", colon);
+ return 0;
+ }
+ remote = *(u_int32_t *)hp->h_addr;
+ if (remote_name[0] == 0)
+ strlcpy(remote_name, colon, sizeof(remote_name));
+ }
+ if (bad_ip_adrs(remote)) {
+ option_error("bad remote IP address %s", ip_ntoa(remote));
+ return 0;
+ }
+ if (remote != 0)
+ wo->hisaddr = remote;
+ prio_remote = option_priority;
+ }
+
+ return 1;
+}
+
+static void
+printipaddr(opt, printer, arg)
+ option_t *opt;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ if (wo->ouraddr != 0)
+ printer(arg, "%I", wo->ouraddr);
+ printer(arg, ":");
+ if (wo->hisaddr != 0)
+ printer(arg, "%I", wo->hisaddr);
+}
+
+/*
+ * setnetmask - set the netmask to be used on the interface.
+ */
+static int
+setnetmask(argv)
+ char **argv;
+{
+ u_int32_t mask;
+ int n;
+ char *p;
+
+ /*
+ * Unfortunately, if we use inet_addr, we can't tell whether
+ * a result of all 1s is an error or a valid 255.255.255.255.
+ */
+ p = *argv;
+ n = parse_dotted_ip(p, &mask);
+
+ mask = htonl(mask);
+
+ if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
+ option_error("invalid netmask value '%s'", *argv);
+ return 0;
+ }
+
+ netmask = mask;
+ slprintf(netmask_str, sizeof(netmask_str), "%I", mask);
+
+ return (1);
+}
+
+int
+parse_dotted_ip(p, vp)
+ char *p;
+ u_int32_t *vp;
+{
+ int n;
+ u_int32_t v, b;
+ char *endp, *p0 = p;
+
+ v = 0;
+ for (n = 3;; --n) {
+ b = strtoul(p, &endp, 0);
+ if (endp == p)
+ return 0;
+ if (b > 255) {
+ if (n < 3)
+ return 0;
+ /* accept e.g. 0xffffff00 */
+ *vp = b;
+ return endp - p0;
+ }
+ v |= b << (n * 8);
+ p = endp;
+ if (n == 0)
+ break;
+ if (*p != '.')
+ return 0;
+ ++p;
+ }
+ *vp = v;
+ return p - p0;
+}
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void
+ipcp_init(unit)
+ int unit;
+{
+ fsm *f = &ipcp_fsm[unit];
+ ipcp_options *wo = &ipcp_wantoptions[unit];
+ ipcp_options *ao = &ipcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPCP;
+ f->callbacks = &ipcp_callbacks;
+ fsm_init(&ipcp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->neg_addr = 1;
+ wo->neg_vj = 1;
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_STATES - 1; /* really max index */
+ wo->cflag = 1;
+
+
+ /* max slots and slot-id compression are currently hardwired in */
+ /* ppp_if.c to 16 and 1, this needs to be changed (among other */
+ /* things) gmc */
+
+ ao->neg_addr = 1;
+ ao->neg_vj = 1;
+ ao->maxslotindex = MAX_STATES - 1;
+ ao->cflag = 1;
+
+ /*
+ * XXX These control whether the user may use the proxyarp
+ * and defaultroute options.
+ */
+ ao->proxy_arp = 1;
+ ao->default_route = 1;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void
+ipcp_open(unit)
+ int unit;
+{
+ fsm_open(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void
+ipcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void
+ipcp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void
+ipcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void
+ipcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipcp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void
+ipcp_resetci(f)
+ fsm *f;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+
+ wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+ if (wo->ouraddr == 0)
+ wo->accept_local = 1;
+ if (wo->hisaddr == 0)
+ wo->accept_remote = 1;
+ wo->req_dns1 = usepeerdns; /* Request DNS addresses from the peer */
+ wo->req_dns2 = usepeerdns;
+ *go = *wo;
+ if (!ask_for_local)
+ go->ouraddr = 0;
+ if (ip_choose_hook)
+ ip_choose_hook(&wo->hisaddr);
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static int
+ipcp_cilen(f)
+ fsm *f;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+
+#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0)
+
+ /*
+ * First see if we want to change our options to the old
+ * forms because we have received old forms from the peer.
+ */
+ if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
+ /* use the old style of address negotiation */
+ go->neg_addr = 1;
+ go->old_addrs = 1;
+ }
+ if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+ /* try an older style of VJ negotiation */
+ /* use the old style only if the peer did */
+ if (ho->neg_vj && ho->old_vj) {
+ go->neg_vj = 1;
+ go->old_vj = 1;
+ go->vj_protocol = ho->vj_protocol;
+ }
+ }
+
+ return (LENCIADDR(go->neg_addr, go->old_addrs) +
+ LENCIVJ(go->neg_vj, go->old_vj) +
+ LENCIDNS(go->req_dns1) +
+ LENCIDNS(go->req_dns2)) ;
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void
+ipcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ if (!old) { \
+ PUTCHAR(maxslotindex, ucp); \
+ PUTCHAR(cflag, ucp); \
+ } \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ if (len >= addrlen) { \
+ u_int32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(addrlen, ucp); \
+ l = ntohl(val1); \
+ PUTLONG(l, ucp); \
+ if (old) { \
+ l = ntohl(val2); \
+ PUTLONG(l, ucp); \
+ } \
+ len -= addrlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIDNS(opt, neg, addr) \
+ if (neg) { \
+ if (len >= CILEN_ADDR) { \
+ u_int32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_ADDR, ucp); \
+ l = ntohl(addr); \
+ PUTLONG(l, ucp); \
+ len -= CILEN_ADDR; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ * Called by fsm_rconfack, Receive Configure ACK.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ u_int32_t cilong;
+ u_char cimaxslotindex, cicflag;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if ((len -= vjlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslotindex) \
+ goto bad; \
+ GETCHAR(cicflag, p); \
+ if (cicflag != cflag) \
+ goto bad; \
+ } \
+ }
+
+#define ACKCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ u_int32_t l; \
+ if ((len -= addrlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != addrlen || \
+ citype != opt) \
+ goto bad; \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val1 != cilong) \
+ goto bad; \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val2 != cilong) \
+ goto bad; \
+ } \
+ }
+
+#define ACKCIDNS(opt, neg, addr) \
+ if (neg) { \
+ u_int32_t l; \
+ if ((len -= CILEN_ADDR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_ADDR || citype != opt) \
+ goto bad; \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (addr != cilong) \
+ goto bad; \
+ }
+
+ ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPCPDEBUG(("ipcp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the OPENED state.
+ * Calback from fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, cicflag;
+ u_char citype, cilen, *next;
+ u_short cishort;
+ u_int32_t ciaddr1, ciaddr2, l, cidnsaddr;
+ ipcp_options no; /* options we've seen Naks for */
+ ipcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIADDR(opt, neg, old, code) \
+ if (go->neg && \
+ len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciaddr1 = htonl(l); \
+ if (old) { \
+ GETLONG(l, p); \
+ ciaddr2 = htonl(l); \
+ no.old_addrs = 1; \
+ } else \
+ ciaddr2 = 0; \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIDNS(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cidnsaddr = htonl(l); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} address, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
+ if (go->accept_local && ciaddr1) { /* Do we know our address? */
+ try.ouraddr = ciaddr1;
+ }
+ if (go->accept_remote && ciaddr2) { /* Does he know his? */
+ try.hisaddr = ciaddr2;
+ }
+ );
+
+ /*
+ * Accept the peer's value of maxslotindex provided that it
+ * is less than what we asked for. Turn off slot-ID compression
+ * if the peer wants. Send old-style compress-type option if
+ * the peer wants.
+ */
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ if (cilen == CILEN_VJ) {
+ GETCHAR(cimaxslotindex, p);
+ GETCHAR(cicflag, p);
+ if (cishort == IPCP_VJ_COMP) {
+ try.old_vj = 0;
+ if (cimaxslotindex < go->maxslotindex)
+ try.maxslotindex = cimaxslotindex;
+ if (!cicflag)
+ try.cflag = 0;
+ } else {
+ try.neg_vj = 0;
+ }
+ } else {
+ if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+ try.old_vj = 1;
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+
+ NAKCIDNS(CI_MS_DNS1, req_dns1,
+ try.dnsaddr[0] = cidnsaddr;
+ );
+
+ NAKCIDNS(CI_MS_DNS2, req_dns2,
+ try.dnsaddr[1] = cidnsaddr;
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about IP addresses, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 )
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+ case CI_ADDRS:
+ if ((go->neg_addr && go->old_addrs) || no.old_addrs
+ || cilen != CILEN_ADDRS)
+ goto bad;
+ try.neg_addr = 1;
+ try.old_addrs = 1;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ GETLONG(l, p);
+ ciaddr2 = htonl(l);
+ if (ciaddr2 && go->accept_remote)
+ try.hisaddr = ciaddr2;
+ no.old_addrs = 1;
+ break;
+ case CI_ADDR:
+ if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR)
+ goto bad;
+ try.old_addrs = 0;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ if (try.ouraddr != 0)
+ try.neg_addr = 1;
+ no.neg_addr = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ * If there are any remaining options, we ignore them.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPCPDEBUG(("ipcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ * Callback from fsm_rconfnakrej.
+ */
+static int
+ipcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, ciflag, cilen;
+ u_short cishort;
+ u_int32_t cilong;
+ ipcp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIADDR(opt, neg, old, val1, val2) \
+ if (go->neg && \
+ len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ u_int32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val1) \
+ goto bad; \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val2) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+ if (go->neg && \
+ p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslot) \
+ goto bad; \
+ GETCHAR(ciflag, p); \
+ if (ciflag != cflag) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIDNS(opt, neg, dnsaddr) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ u_int32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != dnsaddr) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+
+ REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
+
+ REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ IPCPDEBUG(("ipcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ * Callback from fsm_rconfreq, Receive Configure Request
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipcp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *ao = &ipcp_allowoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u_int32_t tl, ciaddr1, ciaddr2;/* Parsed address values */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+ u_char maxslotindex, cflag;
+ int d;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPCPDEBUG(("ipcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_ADDRS:
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDRS) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * If neither we nor he knows his address, reject the option.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ /*
+ * If he doesn't know our address, or if we both have our address
+ * but disagree about it, then NAK it with our idea.
+ */
+ GETLONG(tl, p); /* Parse desination address (ours) */
+ ciaddr2 = htonl(tl);
+ if (ciaddr2 != wo->ouraddr) {
+ if (ciaddr2 == 0 || !wo->accept_local) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->ouraddr);
+ PUTLONG(tl, p);
+ }
+ } else {
+ go->ouraddr = ciaddr2; /* accept peer's idea */
+ }
+ }
+
+ ho->neg_addr = 1;
+ ho->old_addrs = 1;
+ ho->hisaddr = ciaddr1;
+ ho->ouraddr = ciaddr2;
+ break;
+
+ case CI_ADDR:
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * Don't ACK an address of 0.0.0.0 - reject it instead.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ ho->neg_addr = 1;
+ ho->hisaddr = ciaddr1;
+ break;
+
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ /* Microsoft primary or secondary DNS request */
+ d = citype == CI_MS_DNS2;
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->dnsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->dnsaddr[d]) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(ao->dnsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ /* Microsoft primary or secondary WINS request */
+ d = citype == CI_MS_WINS2;
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->winsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->winsaddr[d]) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(ao->winsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_COMPRESSTYPE:
+ if (!ao->neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ if (!(cishort == IPCP_VJ_COMP ||
+ (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ if (cilen == CILEN_VJ) {
+ GETCHAR(maxslotindex, p);
+ if (maxslotindex > ao->maxslotindex) {
+ orc = CONFNAK;
+ if (!reject_if_disagree){
+ DECPTR(1, p);
+ PUTCHAR(ao->maxslotindex, p);
+ }
+ }
+ GETCHAR(cflag, p);
+ if (cflag && !ao->cflag) {
+ orc = CONFNAK;
+ if (!reject_if_disagree){
+ DECPTR(1, p);
+ PUTCHAR(wo->cflag, p);
+ }
+ }
+ ho->maxslotindex = maxslotindex;
+ ho->cflag = cflag;
+ } else {
+ ho->old_vj = 1;
+ ho->maxslotindex = MAX_STATES - 1;
+ ho->cflag = 1;
+ }
+ break;
+
+ default:
+ orc = CONFREJ;
+ break;
+ }
+endswitch:
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ else {
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with a CI_ADDR option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_addr &&
+ wo->req_addr && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_addr = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_ADDR, ucp);
+ PUTCHAR(CILEN_ADDR, ucp);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPCPDEBUG(("ipcp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options()
+{
+ struct hostent *hp;
+ u_int32_t local;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * Default our local IP address based on our hostname.
+ * If local IP address already given, don't bother.
+ */
+ if (wo->ouraddr == 0 && !disable_defaultip) {
+ /*
+ * Look up our hostname (possibly with domain name appended)
+ * and take the first IP address as our local IP address.
+ * If there isn't an IP address for our hostname, too bad.
+ */
+ wo->accept_local = 1; /* don't insist on this default value */
+ if ((hp = gethostbyname(hostname)) != NULL) {
+ local = *(u_int32_t *)hp->h_addr;
+ if (local != 0 && !bad_ip_adrs(local))
+ wo->ouraddr = local;
+ }
+ }
+ ask_for_local = wo->ouraddr != 0 || !disable_defaultip;
+}
+
+
+/*
+ * ip_demand_conf - configure the interface as though
+ * IPCP were up, for use with dial-on-demand.
+ */
+static int
+ip_demand_conf(u)
+ int u;
+{
+ ipcp_options *wo = &ipcp_wantoptions[u];
+
+ if (wo->hisaddr == 0) {
+ /* make up an arbitrary address for the peer */
+ wo->hisaddr = htonl(0x0a707070 + ifunit);
+ wo->accept_remote = 1;
+ }
+ if (wo->ouraddr == 0) {
+ /* make up an arbitrary address for us */
+ wo->ouraddr = htonl(0x0a404040 + ifunit);
+ wo->accept_local = 1;
+ ask_for_local = 0; /* don't tell the peer this address */
+ }
+ if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
+ return 0;
+ if (!sifup(u))
+ return 0;
+ if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
+ return 0;
+ if (wo->default_route)
+ if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr))
+ default_route_set[u] = 1;
+ if (wo->proxy_arp)
+ if (sifproxyarp(u, wo->hisaddr))
+ proxy_arp_set[u] = 1;
+
+ notice("local IP address %I", wo->ouraddr);
+ notice("remote IP address %I", wo->hisaddr);
+
+ return 1;
+}
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(f)
+ fsm *f;
+{
+ u_int32_t mask;
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ IPCPDEBUG(("ipcp: up"));
+
+ /*
+ * We must have a non-zero IP address for both ends of the link.
+ */
+ if (!ho->neg_addr)
+ ho->hisaddr = wo->hisaddr;
+
+ if (go->ouraddr == 0) {
+ error("Could not determine local IP address");
+ ipcp_close(f->unit, "Could not determine local IP address");
+ return;
+ }
+ if (ho->hisaddr == 0) {
+ ho->hisaddr = htonl(0x0a404040 + ifunit);
+ warn("Could not determine remote IP address: defaulting to %I",
+ ho->hisaddr);
+ }
+ script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
+ script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
+
+ if (usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
+ script_setenv("USEPEERDNS", "1", 0);
+ if (go->dnsaddr[0])
+ script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
+ if (go->dnsaddr[1])
+ script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
+ create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
+ }
+
+ /*
+ * Check that the peer is allowed to use the IP address it wants.
+ */
+ if (!auth_ip_addr(f->unit, ho->hisaddr)) {
+ error("Peer is not authorized to use remote address %I", ho->hisaddr);
+ ipcp_close(f->unit, "Unauthorized remote IP address");
+ return;
+ }
+
+ /* set tcp compression */
+ sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
+
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IP packets.
+ */
+ if (demand) {
+ if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
+ ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr);
+ if (go->ouraddr != wo->ouraddr) {
+ warn("Local IP address changed to %I", go->ouraddr);
+ script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
+ wo->ouraddr = go->ouraddr;
+ } else
+ script_unsetenv("OLDIPLOCAL");
+ if (ho->hisaddr != wo->hisaddr) {
+ warn("Remote IP address changed to %I", ho->hisaddr);
+ script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
+ wo->hisaddr = ho->hisaddr;
+ } else
+ script_unsetenv("OLDIPREMOTE");
+
+ /* Set the interface to the new addresses */
+ mask = GetMask(go->ouraddr);
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ if (debug)
+ warn("Interface configuration failed");
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route)
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+ if (ipcp_wantoptions[f->unit].proxy_arp)
+ if (sifproxyarp(f->unit, ho->hisaddr))
+ proxy_arp_set[f->unit] = 1;
+
+ }
+ demand_rexmit(PPP_IP);
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ } else {
+ /*
+ * Set IP addresses and (if specified) netmask.
+ */
+ mask = GetMask(go->ouraddr);
+
+#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ if (debug)
+ warn("Interface configuration failed");
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+
+ /* bring the interface up for IP */
+ if (!sifup(f->unit)) {
+ if (debug)
+ warn("Interface failed to come up");
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+#if (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ if (debug)
+ warn("Interface configuration failed");
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route)
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+ if (ipcp_wantoptions[f->unit].proxy_arp)
+ if (sifproxyarp(f->unit, ho->hisaddr))
+ proxy_arp_set[f->unit] = 1;
+
+ ipcp_wantoptions[0].ouraddr = go->ouraddr;
+
+ notice("local IP address %I", go->ouraddr);
+ notice("remote IP address %I", ho->hisaddr);
+ if (go->dnsaddr[0])
+ notice("primary DNS address %I", go->dnsaddr[0]);
+ if (go->dnsaddr[1])
+ notice("secondary DNS address %I", go->dnsaddr[1]);
+ }
+
+ np_up(f->unit, PPP_IP);
+ ipcp_is_up = 1;
+
+ if (ip_up_hook)
+ ip_up_hook();
+
+ /*
+ * Execute the ip-up script, like this:
+ * /etc/ppp/ip-up interface tty speed local-IP remote-IP
+ */
+ if (ipcp_script_state == s_down && ipcp_script_pid == 0) {
+ ipcp_script_state = s_up;
+ ipcp_script(_PATH_IPUP);
+ }
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipcp_down(f)
+ fsm *f;
+{
+ IPCPDEBUG(("ipcp: down"));
+ /* XXX a bit IPv4-centric here, we only need to get the stats
+ * before the interface is marked down. */
+ update_link_stats(f->unit);
+ if (ip_down_hook)
+ ip_down_hook();
+ if (ipcp_is_up) {
+ ipcp_is_up = 0;
+ np_down(f->unit, PPP_IP);
+ }
+ sifvjcomp(f->unit, 0, 0, 0);
+
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE);
+ } else {
+ sifnpmode(f->unit, PPP_IP, NPMODE_DROP);
+ sifdown(f->unit);
+ ipcp_clear_addrs(f->unit, ipcp_gotoptions[f->unit].ouraddr,
+ ipcp_hisoptions[f->unit].hisaddr);
+ }
+
+ /* Execute the ip-down script */
+ if (ipcp_script_state == s_up && ipcp_script_pid == 0) {
+ ipcp_script_state = s_down;
+ ipcp_script(_PATH_IPDOWN);
+ }
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes,
+ * proxy arp entries, etc.
+ */
+static void
+ipcp_clear_addrs(unit, ouraddr, hisaddr)
+ int unit;
+ u_int32_t ouraddr; /* local address */
+ u_int32_t hisaddr; /* remote address */
+{
+ if (proxy_arp_set[unit]) {
+ cifproxyarp(unit, hisaddr);
+ proxy_arp_set[unit] = 0;
+ }
+ if (default_route_set[unit]) {
+ cifdefaultroute(unit, ouraddr, hisaddr);
+ default_route_set[unit] = 0;
+ }
+ cifaddr(unit, ouraddr, hisaddr);
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipcp_finished(f)
+ fsm *f;
+{
+ np_finished(f->unit, PPP_IP);
+}
+
+
+/*
+ * ipcp_script_done - called when the ip-up or ip-down script
+ * has finished.
+ */
+static void
+ipcp_script_done(arg)
+ void *arg;
+{
+ ipcp_script_pid = 0;
+ switch (ipcp_script_state) {
+ case s_up:
+ if (ipcp_fsm[0].state != OPENED) {
+ ipcp_script_state = s_down;
+ ipcp_script(_PATH_IPDOWN);
+ }
+ break;
+ case s_down:
+ if (ipcp_fsm[0].state == OPENED) {
+ ipcp_script_state = s_up;
+ ipcp_script(_PATH_IPUP);
+ }
+ break;
+ }
+}
+
+
+/*
+ * ipcp_script - Execute a script with arguments
+ * interface-name tty-name speed local-IP remote-IP.
+ */
+static void
+ipcp_script(script)
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char *argv[8];
+
+ slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+ slprintf(strlocal, sizeof(strlocal), "%I", ipcp_gotoptions[0].ouraddr);
+ slprintf(strremote, sizeof(strremote), "%I", ipcp_hisoptions[0].hisaddr);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strlocal;
+ argv[5] = strremote;
+ argv[6] = ipparam;
+ argv[7] = NULL;
+ ipcp_script_pid = run_program(script, argv, 0, ipcp_script_done, NULL);
+}
+
+/*
+ * create_resolv - create the replacement resolv.conf file
+ */
+static void
+create_resolv(peerdns1, peerdns2)
+ u_int32_t peerdns1, peerdns2;
+{
+ FILE *f;
+
+ f = fopen(_PATH_RESOLV, "w");
+ if (f == NULL) {
+ error("Failed to create %s: %m", _PATH_RESOLV);
+ return;
+ }
+
+ if (peerdns1)
+ fprintf(f, "nameserver %s\n", ip_ntoa(peerdns1));
+
+ if (peerdns2)
+ fprintf(f, "nameserver %s\n", ip_ntoa(peerdns2));
+
+ if (ferror(f))
+ error("Write failed to %s: %m", _PATH_RESOLV);
+
+ fclose(f);
+}
+
+/*
+ * ipcp_printpkt - print the contents of an IPCP packet.
+ */
+static char *ipcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ipcp_codenames) / sizeof(char *))
+ printer(arg, " %s", ipcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_ADDRS:
+ if (olen == CILEN_ADDRS) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "addrs %I", htonl(cilong));
+ GETLONG(cilong, p);
+ printer(arg, " %I", htonl(cilong));
+ }
+ break;
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress ");
+ switch (cishort) {
+ case IPCP_VJ_COMP:
+ printer(arg, "VJ");
+ break;
+ case IPCP_VJ_COMP_OLD:
+ printer(arg, "old-VJ");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_ADDR:
+ if (olen == CILEN_ADDR) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "addr %I", htonl(cilong));
+ }
+ break;
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "ms-dns%d %I", code - CI_MS_DNS1 + 1,
+ htonl(cilong));
+ break;
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "ms-wins %I", htonl(cilong));
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/*
+ * ip_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP_HDRLEN 20 /* bytes */
+#define IP_OFFMASK 0x1fff
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define net_short(x) (((x)[0] << 8) + (x)[1])
+#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF)
+#define get_ipoff(x) net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x) (((unsigned char *)(x))[9])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ip_active_pkt(pkt, len)
+ u_char *pkt;
+ int len;
+{
+ u_char *tcp;
+ int hlen;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP_HDRLEN)
+ return 0;
+ if ((get_ipoff(pkt) & IP_OFFMASK) != 0)
+ return 0;
+ if (get_ipproto(pkt) != IPPROTO_TCP)
+ return 1;
+ hlen = get_iphl(pkt) * 4;
+ if (len < hlen + TCP_HDRLEN)
+ return 0;
+ tcp = pkt + hlen;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4)
+ return 0;
+ return 1;
+}
diff --git a/mdk-stage1/ppp/pppd/ipcp.h b/mdk-stage1/ppp/pppd/ipcp.h
new file mode 100644
index 000000000..8c5aca861
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/ipcp.h
@@ -0,0 +1,73 @@
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Options.
+ */
+#define CI_ADDRS 1 /* IP Addresses */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+#define CI_ADDR 3
+
+#define CI_MS_DNS1 129 /* Primary DNS value */
+#define CI_MS_WINS1 130 /* Primary WINS value */
+#define CI_MS_DNS2 131 /* Secondary DNS value */
+#define CI_MS_WINS2 132 /* Secondary WINS value */
+
+#define MAX_STATES 16 /* from slcompress.h */
+
+#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */
+#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */
+#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */
+ /* maxslot and slot number compression) */
+
+#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/
+#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */
+ /* compression option*/
+
+typedef struct ipcp_options {
+ bool neg_addr; /* Negotiate IP Address? */
+ bool old_addrs; /* Use old (IP-Addresses) option? */
+ bool req_addr; /* Ask peer to send IP address? */
+ bool default_route; /* Assign default route through interface? */
+ bool proxy_arp; /* Make proxy ARP entry for peer? */
+ bool neg_vj; /* Van Jacobson Compression? */
+ bool old_vj; /* use old (short) form of VJ option? */
+ bool accept_local; /* accept peer's value for ouraddr */
+ bool accept_remote; /* accept peer's value for hisaddr */
+ bool req_dns1; /* Ask peer to send primary DNS address? */
+ bool req_dns2; /* Ask peer to send secondary DNS address? */
+ int vj_protocol; /* protocol value to use in VJ option */
+ int maxslotindex; /* values for RFC1332 VJ compression neg. */
+ bool cflag;
+ u_int32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */
+ u_int32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */
+ u_int32_t winsaddr[2]; /* Primary and secondary MS WINS entries */
+} ipcp_options;
+
+extern fsm ipcp_fsm[];
+extern ipcp_options ipcp_wantoptions[];
+extern ipcp_options ipcp_gotoptions[];
+extern ipcp_options ipcp_allowoptions[];
+extern ipcp_options ipcp_hisoptions[];
+
+char *ip_ntoa __P((u_int32_t));
+
+extern struct protent ipcp_protent;
diff --git a/mdk-stage1/ppp/pppd/ipv6cp.c b/mdk-stage1/ppp/pppd/ipv6cp.c
new file mode 100644
index 000000000..54ff7d7d8
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/ipv6cp.c
@@ -0,0 +1,1512 @@
+/*
+ ipv6cp.c - PPP IPV6 Control Protocol.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms. The name of the author may not be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+#define RCSID "$Id$"
+
+/*
+ * TODO:
+ *
+ * Proxy Neighbour Discovery.
+ *
+ * Better defines for selecting the ordering of
+ * interface up / set address. (currently checks for __linux__,
+ * since SVR4 && (SNI || __USLC__) didn't work properly)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "magic.h"
+#include "pathnames.h"
+
+static const char rcsid[] = RCSID;
+
+/* global vars */
+ipv6cp_options ipv6cp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipv6cp_options ipv6cp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipv6cp_options ipv6cp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipv6cp_options ipv6cp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+int no_ifaceid_neg = 0;
+
+/* local vars */
+static int ipv6cp_is_up;
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipv6cp_resetci __P((fsm *)); /* Reset our CI */
+static int ipv6cp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipv6cp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipv6cp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipv6cp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipv6cp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipv6cp_up __P((fsm *)); /* We're UP */
+static void ipv6cp_down __P((fsm *)); /* We're DOWN */
+static void ipv6cp_finished __P((fsm *)); /* Don't need lower layer */
+
+fsm ipv6cp_fsm[NUM_PPP]; /* IPV6CP fsm structure */
+
+static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
+ ipv6cp_resetci, /* Reset our Configuration Information */
+ ipv6cp_cilen, /* Length of our Configuration Information */
+ ipv6cp_addci, /* Add our Configuration Information */
+ ipv6cp_ackci, /* ACK our Configuration Information */
+ ipv6cp_nakci, /* NAK our Configuration Information */
+ ipv6cp_rejci, /* Reject our Configuration Information */
+ ipv6cp_reqci, /* Request peer's Configuration Information */
+ ipv6cp_up, /* Called when fsm reaches OPENED state */
+ ipv6cp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipv6cp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPV6CP" /* String name of protocol */
+};
+
+/*
+ * Command-line options.
+ */
+static int setifaceid __P((char **arg));
+static void printifaceid __P((option_t *,
+ void (*)(void *, char *, ...), void *));
+
+static option_t ipv6cp_option_list[] = {
+ { "ipv6", o_special, (void *)setifaceid,
+ "Set interface identifiers for IPV6",
+ OPT_A2PRINTER, (void *)printifaceid },
+
+ { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Enable IPv6 and IPv6CP", OPT_PRIO | 1 },
+ { "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Disable IPv6 and IPv6CP", OPT_PRIOSUB },
+ { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS },
+
+ { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local,
+ "Accept peer's interface identifier for us", 1 },
+
+ { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
+ "Use (default) IPv4 address as interface identifier", 1 },
+
+#if defined(SOL2)
+ { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
+ "Use uniquely-available persistent value for link local address", 1 },
+#endif /* defined(SOL2) */
+
+ { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
+ "Set timeout for IPv6CP", OPT_PRIO },
+ { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
+ "Set max #xmits for term-reqs", OPT_PRIO },
+ { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
+ "Set max #xmits for conf-reqs", OPT_PRIO },
+ { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
+ "Set max #conf-naks for IPv6CP", OPT_PRIO },
+
+ { NULL }
+};
+
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipv6cp_init __P((int));
+static void ipv6cp_open __P((int));
+static void ipv6cp_close __P((int, char *));
+static void ipv6cp_lowerup __P((int));
+static void ipv6cp_lowerdown __P((int));
+static void ipv6cp_input __P((int, u_char *, int));
+static void ipv6cp_protrej __P((int));
+static int ipv6cp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+static void ipv6_check_options __P((void));
+static int ipv6_demand_conf __P((int));
+static int ipv6_active_pkt __P((u_char *, int));
+
+struct protent ipv6cp_protent = {
+ PPP_IPV6CP,
+ ipv6cp_init,
+ ipv6cp_input,
+ ipv6cp_protrej,
+ ipv6cp_lowerup,
+ ipv6cp_lowerdown,
+ ipv6cp_open,
+ ipv6cp_close,
+ ipv6cp_printpkt,
+ NULL,
+ 0,
+ "IPV6CP",
+ "IPV6",
+ ipv6cp_option_list,
+ ipv6_check_options,
+ ipv6_demand_conf,
+ ipv6_active_pkt
+};
+
+static void ipv6cp_clear_addrs __P((int, eui64_t, eui64_t));
+static void ipv6cp_script __P((char *));
+static void ipv6cp_script_done __P((void *));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* length for RFC2023 compress opt. */
+#define CILEN_IFACEID 10 /* RFC2472, interface identifier */
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+ s_down,
+ s_up,
+} ipv6cp_script_state;
+static pid_t ipv6cp_script_pid;
+
+/*
+ * setifaceid - set the interface identifiers manually
+ */
+static int
+setifaceid(argv)
+ char **argv;
+{
+ char *comma, *arg, c;
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+ struct in6_addr addr;
+ static int prio_local, prio_remote;
+
+#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
+ (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
+
+ arg = *argv;
+ if ((comma = strchr(arg, ',')) == NULL)
+ comma = arg + strlen(arg);
+
+ /*
+ * If comma first character, then no local identifier
+ */
+ if (comma != arg) {
+ c = *comma;
+ *comma = '\0';
+
+ if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (local): %s", arg);
+ return 0;
+ }
+
+ if (option_priority >= prio_local) {
+ eui64_copy(addr.s6_addr32[2], wo->ourid);
+ wo->opt_local = 1;
+ prio_local = option_priority;
+ }
+ *comma = c;
+ }
+
+ /*
+ * If comma last character, the no remote identifier
+ */
+ if (*comma != 0 && *++comma != '\0') {
+ if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (remote): %s", comma);
+ return 0;
+ }
+ if (option_priority >= prio_remote) {
+ eui64_copy(addr.s6_addr32[2], wo->hisid);
+ wo->opt_remote = 1;
+ prio_remote = option_priority;
+ }
+ }
+
+ if (override_value("+ipv6", option_priority, option_source))
+ ipv6cp_protent.enabled_flag = 1;
+ return 1;
+}
+
+static void
+printifaceid(opt, printer, arg)
+ option_t *opt;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+ if (wo->opt_local)
+ printer(arg, "%s", llv6_ntoa(wo->ourid));
+ printer(arg, ",");
+ if (wo->opt_remote)
+ printer(arg, "%s", llv6_ntoa(wo->hisid));
+}
+
+/*
+ * Make a string representation of a network address.
+ */
+char *
+llv6_ntoa(ifaceid)
+ eui64_t ifaceid;
+{
+ static char b[64];
+
+ sprintf(b, "fe80::%s", eui64_ntoa(ifaceid));
+ return b;
+}
+
+
+/*
+ * ipv6cp_init - Initialize IPV6CP.
+ */
+static void
+ipv6cp_init(unit)
+ int unit;
+{
+ fsm *f = &ipv6cp_fsm[unit];
+ ipv6cp_options *wo = &ipv6cp_wantoptions[unit];
+ ipv6cp_options *ao = &ipv6cp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPV6CP;
+ f->callbacks = &ipv6cp_callbacks;
+ fsm_init(&ipv6cp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->accept_local = 1;
+ wo->neg_ifaceid = 1;
+ ao->neg_ifaceid = 1;
+
+#ifdef IPV6CP_COMP
+ wo->neg_vj = 1;
+ ao->neg_vj = 1;
+ wo->vj_protocol = IPV6CP_COMP;
+#endif
+
+}
+
+
+/*
+ * ipv6cp_open - IPV6CP is allowed to come up.
+ */
+static void
+ipv6cp_open(unit)
+ int unit;
+{
+ fsm_open(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_close - Take IPV6CP down.
+ */
+static void
+ipv6cp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipv6cp_fsm[unit], reason);
+}
+
+
+/*
+ * ipv6cp_lowerup - The lower layer is up.
+ */
+static void
+ipv6cp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_lowerdown - The lower layer is down.
+ */
+static void
+ipv6cp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_input - Input IPV6CP packet.
+ */
+static void
+ipv6cp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipv6cp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipv6cp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_resetci - Reset our CI.
+ */
+static void
+ipv6cp_resetci(f)
+ fsm *f;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+ wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid;
+
+ if (!wo->opt_local) {
+ eui64_magic_nz(wo->ourid);
+ }
+
+ *go = *wo;
+ eui64_zero(go->hisid); /* last proposed interface identifier */
+}
+
+
+/*
+ * ipv6cp_cilen - Return length of our CI.
+ */
+static int
+ipv6cp_cilen(f)
+ fsm *f;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+#define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0)
+#define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0)
+
+ return (LENCIIFACEID(go->neg_ifaceid) +
+ LENCIVJ(go->neg_vj));
+}
+
+
+/*
+ * ipv6cp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipv6cp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if (len >= idlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(idlen, ucp); \
+ eui64_put(val1, ucp); \
+ len -= idlen; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipv6cp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipv6cp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ eui64_t ifaceid;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ if ((len -= vjlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+
+#define ACKCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if ((len -= idlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != idlen || \
+ citype != opt) \
+ goto bad; \
+ eui64_get(ifaceid, p); \
+ if (! eui64_equals(val1, ifaceid)) \
+ goto bad; \
+ }
+
+ ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipv6cp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPV6CP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipv6cp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char citype, cilen, *next;
+ u_short cishort;
+ eui64_t ifaceid;
+ ipv6cp_options no; /* options we've seen Naks for */
+ ipv6cp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIIFACEID(opt, neg, code) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} interface identifier, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
+ if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try.ourid = ifaceid;
+ IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
+ }
+ );
+
+#ifdef IPV6CP_COMP
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ {
+ if (cishort == IPV6CP_COMP) {
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+#else
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ {
+ try.neg_vj = 0;
+ }
+ );
+#endif
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about interface identifier, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 )
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+ case CI_IFACEID:
+ if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
+ goto bad;
+ try.neg_ifaceid = 1;
+ eui64_get(ifaceid, p);
+ if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try.ourid = ifaceid;
+ }
+ no.neg_ifaceid = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_rejci - Reject some of our CIs.
+ */
+static int
+ipv6cp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char cilen;
+ u_short cishort;
+ eui64_t ifaceid;
+ ipv6cp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIIFACEID(opt, neg, val1) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ /* Check rejected value. */ \
+ if (! eui64_equals(ifaceid, val1)) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val) \
+ if (go->neg && \
+ p[1] == CILEN_COMPRESS && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+ REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipv6cp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+ ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
+ ipv6cp_options *ao = &ipv6cp_allowoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ eui64_t ifaceid; /* Parsed interface identifier */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_IFACEID:
+ IPV6CPDEBUG(("ipv6cp: received interface identifier "));
+
+ if (!ao->neg_ifaceid ||
+ cilen != CILEN_IFACEID) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no interface identifier, or if we both have same
+ * identifier then NAK it with new idea.
+ * In particular, if we don't know his identifier, but he does,
+ * then accept it.
+ */
+ eui64_get(ifaceid, p);
+ IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
+ if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ if (!eui64_iszero(wo->hisid) &&
+ !eui64_equals(ifaceid, wo->hisid) &&
+ eui64_iszero(go->hisid)) {
+
+ orc = CONFNAK;
+ ifaceid = wo->hisid;
+ go->hisid = ifaceid;
+ DECPTR(sizeof(ifaceid), p);
+ eui64_put(ifaceid, p);
+ } else
+ if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) {
+ orc = CONFNAK;
+ if (eui64_iszero(go->hisid)) /* first time, try option */
+ ifaceid = wo->hisid;
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->ourid)) /* bad luck */
+ eui64_magic(ifaceid);
+ go->hisid = ifaceid;
+ DECPTR(sizeof(ifaceid), p);
+ eui64_put(ifaceid, p);
+ }
+
+ ho->neg_ifaceid = 1;
+ ho->hisid = ifaceid;
+ break;
+
+ case CI_COMPRESSTYPE:
+ IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
+ if (!ao->neg_vj ||
+ (cilen != CILEN_COMPRESS)) {
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+ IPV6CPDEBUG(("(%d)", cishort));
+
+#ifdef IPV6CP_COMP
+ if (!(cishort == IPV6CP_COMP)) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ break;
+#else
+ orc = CONFREJ;
+ break;
+#endif
+
+ default:
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ IPV6CPDEBUG((" (%s)\n", CODENAME(orc)));
+
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ else {
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their identifier and they didn't send their identifier, then we
+ * send a NAK with a CI_IFACEID option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_ifaceid &&
+ wo->req_ifaceid && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_ifaceid = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_IFACEID, ucp);
+ PUTCHAR(CILEN_IFACEID, ucp);
+ eui64_put(wo->hisid, ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * ipv6_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ipv6_check_options()
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+ if (!ipv6cp_protent.enabled_flag)
+ return;
+
+#if defined(SOL2)
+ /*
+ * Persistent link-local id is only used when user has not explicitly
+ * configure/hard-code the id
+ */
+ if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
+
+ /*
+ * On systems where there are no Ethernet interfaces used, there
+ * may be other ways to obtain a persistent id. Right now, it
+ * will fall back to using magic [see eui64_magic] below when
+ * an EUI-48 from MAC address can't be obtained. Other possibilities
+ * include obtaining EEPROM serial numbers, or some other unique
+ * yet persistent number. On Sparc platforms, this is possible,
+ * but too bad there's no standards yet for x86 machines.
+ */
+ if (ether_to_eui64(&wo->ourid)) {
+ wo->opt_local = 1;
+ }
+ }
+#endif
+
+ if (!wo->opt_local) { /* init interface identifier */
+ if (wo->use_ip && eui64_iszero(wo->ourid)) {
+ eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr));
+ if (!eui64_iszero(wo->ourid))
+ wo->opt_local = 1;
+ }
+
+ while (eui64_iszero(wo->ourid))
+ eui64_magic(wo->ourid);
+ }
+
+ if (!wo->opt_remote) {
+ if (wo->use_ip && eui64_iszero(wo->hisid)) {
+ eui64_setlo32(wo->hisid, ntohl(ipcp_wantoptions[0].hisaddr));
+ if (!eui64_iszero(wo->hisid))
+ wo->opt_remote = 1;
+ }
+ }
+
+ if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
+ option_error("local/remote LL address required for demand-dialling\n");
+ exit(1);
+ }
+}
+
+
+/*
+ * ipv6_demand_conf - configure the interface as though
+ * IPV6CP were up, for use with dial-on-demand.
+ */
+static int
+ipv6_demand_conf(u)
+ int u;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[u];
+
+#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+#if defined(SOL2)
+ if (!sif6up(u))
+ return 0;
+#else
+ if (!sifup(u))
+ return 0;
+#endif /* defined(SOL2) */
+#endif
+ if (!sif6addr(u, wo->ourid, wo->hisid))
+ return 0;
+#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifup(u))
+ return 0;
+#endif
+ if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
+ return 0;
+
+ notice("ipv6_demand_conf");
+ notice("local LL address %s", llv6_ntoa(wo->ourid));
+ notice("remote LL address %s", llv6_ntoa(wo->hisid));
+
+ return 1;
+}
+
+
+/*
+ * ipv6cp_up - IPV6CP has come UP.
+ *
+ * Configure the IPv6 network interface appropriately and bring it up.
+ */
+static void
+ipv6cp_up(f)
+ fsm *f;
+{
+ ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+
+ IPV6CPDEBUG(("ipv6cp: up"));
+
+ /*
+ * We must have a non-zero LL address for both ends of the link.
+ */
+ if (!ho->neg_ifaceid)
+ ho->hisid = wo->hisid;
+
+ if(!no_ifaceid_neg) {
+ if (eui64_iszero(ho->hisid)) {
+ error("Could not determine remote LL address");
+ ipv6cp_close(f->unit, "Could not determine remote LL address");
+ return;
+ }
+ if (eui64_iszero(go->ourid)) {
+ error("Could not determine local LL address");
+ ipv6cp_close(f->unit, "Could not determine local LL address");
+ return;
+ }
+ if (eui64_equals(go->ourid, ho->hisid)) {
+ error("local and remote LL addresses are equal");
+ ipv6cp_close(f->unit, "local and remote LL addresses are equal");
+ return;
+ }
+ }
+ script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
+ script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
+
+#ifdef IPV6CP_COMP
+ /* set tcp compression */
+ sif6comp(f->unit, ho->neg_vj);
+#endif
+
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IPv6 packets.
+ */
+ if (demand) {
+ if (! eui64_equals(go->ourid, wo->ourid) ||
+ ! eui64_equals(ho->hisid, wo->hisid)) {
+ if (! eui64_equals(go->ourid, wo->ourid))
+ warn("Local LL address changed to %s",
+ llv6_ntoa(go->ourid));
+ if (! eui64_equals(ho->hisid, wo->hisid))
+ warn("Remote LL address changed to %s",
+ llv6_ntoa(ho->hisid));
+ ipv6cp_clear_addrs(f->unit, go->ourid, ho->hisid);
+
+ /* Set the interface to the new addresses */
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ }
+ demand_rexmit(PPP_IPV6);
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
+
+ } else {
+ /*
+ * Set LL addresses
+ */
+#if !defined(__linux__) && !defined(SOL2) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+
+ /* bring the interface up for IPv6 */
+#if defined(SOL2)
+ if (!sif6up(f->unit)) {
+ if (debug)
+ warn("sifup failed (IPV6)");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#else
+ if (!sifup(f->unit)) {
+ if (debug)
+ warn("sifup failed (IPV6)");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif /* defined(SOL2) */
+
+#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
+
+ notice("local LL address %s", llv6_ntoa(go->ourid));
+ notice("remote LL address %s", llv6_ntoa(ho->hisid));
+ }
+
+ np_up(f->unit, PPP_IPV6);
+ ipv6cp_is_up = 1;
+
+ /*
+ * Execute the ipv6-up script, like this:
+ * /etc/ppp/ipv6-up interface tty speed local-LL remote-LL
+ */
+ if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_up;
+ ipv6cp_script(_PATH_IPV6UP);
+ }
+}
+
+
+/*
+ * ipv6cp_down - IPV6CP has gone DOWN.
+ *
+ * Take the IPv6 network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipv6cp_down(f)
+ fsm *f;
+{
+ IPV6CPDEBUG(("ipv6cp: down"));
+ update_link_stats(f->unit);
+ if (ipv6cp_is_up) {
+ ipv6cp_is_up = 0;
+ np_down(f->unit, PPP_IPV6);
+ }
+#ifdef IPV6CP_COMP
+ sif6comp(f->unit, 0);
+#endif
+
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE);
+ } else {
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_DROP);
+#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
+#if defined(SOL2)
+ sif6down(f->unit);
+#else
+ sifdown(f->unit);
+#endif /* defined(SOL2) */
+#endif
+ ipv6cp_clear_addrs(f->unit,
+ ipv6cp_gotoptions[f->unit].ourid,
+ ipv6cp_hisoptions[f->unit].hisid);
+#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
+ sifdown(f->unit);
+#endif
+ }
+
+ /* Execute the ipv6-down script */
+ if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_down;
+ ipv6cp_script(_PATH_IPV6DOWN);
+ }
+}
+
+
+/*
+ * ipv6cp_clear_addrs() - clear the interface addresses, routes,
+ * proxy neighbour discovery entries, etc.
+ */
+static void
+ipv6cp_clear_addrs(unit, ourid, hisid)
+ int unit;
+ eui64_t ourid;
+ eui64_t hisid;
+{
+ cif6addr(unit, ourid, hisid);
+}
+
+
+/*
+ * ipv6cp_finished - possibly shut down the lower layers.
+ */
+static void
+ipv6cp_finished(f)
+ fsm *f;
+{
+ np_finished(f->unit, PPP_IPV6);
+}
+
+
+/*
+ * ipv6cp_script_done - called when the ipv6-up or ipv6-down script
+ * has finished.
+ */
+static void
+ipv6cp_script_done(arg)
+ void *arg;
+{
+ ipv6cp_script_pid = 0;
+ switch (ipv6cp_script_state) {
+ case s_up:
+ if (ipv6cp_fsm[0].state != OPENED) {
+ ipv6cp_script_state = s_down;
+ ipv6cp_script(_PATH_IPV6DOWN);
+ }
+ break;
+ case s_down:
+ if (ipv6cp_fsm[0].state == OPENED) {
+ ipv6cp_script_state = s_up;
+ ipv6cp_script(_PATH_IPV6UP);
+ }
+ break;
+ }
+}
+
+
+/*
+ * ipv6cp_script - Execute a script with arguments
+ * interface-name tty-name speed local-LL remote-LL.
+ */
+static void
+ipv6cp_script(script)
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char *argv[8];
+
+ sprintf(strspeed, "%d", baud_rate);
+ strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid));
+ strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid));
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strlocal;
+ argv[5] = strremote;
+ argv[6] = ipparam;
+ argv[7] = NULL;
+
+ ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, NULL);
+}
+
+/*
+ * ipv6cp_printpkt - print the contents of an IPV6CP packet.
+ */
+static char *ipv6cp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipv6cp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ eui64_t ifaceid;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ipv6cp_codenames) / sizeof(char *))
+ printer(arg, " %s", ipv6cp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress ");
+ printer(arg, "0x%x", cishort);
+ }
+ break;
+ case CI_IFACEID:
+ if (olen == CILEN_IFACEID) {
+ p += 2;
+ eui64_get(ifaceid, p);
+ printer(arg, "addr %s", llv6_ntoa(ifaceid));
+ }
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/*
+ * ipv6_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP6_HDRLEN 40 /* bytes */
+#define IP6_NHDR_FRAG 44 /* fragment IPv6 header */
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define get_ip6nh(x) (((unsigned char *)(x))[6])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ipv6_active_pkt(pkt, len)
+ u_char *pkt;
+ int len;
+{
+ u_char *tcp;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP6_HDRLEN)
+ return 0;
+ if (get_ip6nh(pkt) == IP6_NHDR_FRAG)
+ return 0;
+ if (get_ip6nh(pkt) != IPPROTO_TCP)
+ return 1;
+ if (len < IP6_HDRLEN + TCP_HDRLEN)
+ return 0;
+ tcp = pkt + IP6_HDRLEN;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4)
+ return 0;
+ return 1;
+}
diff --git a/mdk-stage1/ppp/pppd/ipv6cp.h b/mdk-stage1/ppp/pppd/ipv6cp.h
new file mode 100644
index 000000000..60d366eb3
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/ipv6cp.h
@@ -0,0 +1,126 @@
+/*
+ ipv6cp.h - PPP IPV6 Control Protocol.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms. The name of the author may not be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Options.
+ */
+#define CI_IFACEID 1 /* Interface Identifier */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+
+/* No compression types yet defined.
+ *#define IPV6CP_COMP 0x004f
+ */
+typedef struct ipv6cp_options {
+ int neg_ifaceid; /* Negotiate interface identifier? */
+ int req_ifaceid; /* Ask peer to send interface identifier? */
+ int accept_local; /* accept peer's value for iface id? */
+ int opt_local; /* ourtoken set by option */
+ int opt_remote; /* histoken set by option */
+ int use_ip; /* use IP as interface identifier */
+#if defined(SOL2)
+ int use_persistent; /* use uniquely persistent value for address */
+#endif /* defined(SOL2) */
+ int neg_vj; /* Van Jacobson Compression? */
+ u_short vj_protocol; /* protocol value to use in VJ option */
+ eui64_t ourid, hisid; /* Interface identifiers */
+} ipv6cp_options;
+
+extern fsm ipv6cp_fsm[];
+extern ipv6cp_options ipv6cp_wantoptions[];
+extern ipv6cp_options ipv6cp_gotoptions[];
+extern ipv6cp_options ipv6cp_allowoptions[];
+extern ipv6cp_options ipv6cp_hisoptions[];
+
+extern struct protent ipv6cp_protent;
diff --git a/mdk-stage1/ppp/pppd/ipxcp.c b/mdk-stage1/ppp/pppd/ipxcp.c
new file mode 100644
index 000000000..f9a12b934
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/ipxcp.c
@@ -0,0 +1,1570 @@
+/*
+ * ipxcp.c - PPP IPX Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef IPX_CHANGE
+
+#define RCSID "$Id$"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipxcp.h"
+#include "pathnames.h"
+#include "magic.h"
+
+static const char rcsid[] = RCSID;
+
+/* global vars */
+ipxcp_options ipxcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipxcp_options ipxcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipxcp_options ipxcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipxcp_options ipxcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+#define wo (&ipxcp_wantoptions[0])
+#define ao (&ipxcp_allowoptions[0])
+#define go (&ipxcp_gotoptions[0])
+#define ho (&ipxcp_hisoptions[0])
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipxcp_resetci __P((fsm *)); /* Reset our CI */
+static int ipxcp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipxcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipxcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipxcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipxcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipxcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipxcp_up __P((fsm *)); /* We're UP */
+static void ipxcp_down __P((fsm *)); /* We're DOWN */
+static void ipxcp_finished __P((fsm *)); /* Don't need lower layer */
+static void ipxcp_script __P((fsm *, char *)); /* Run an up/down script */
+
+fsm ipxcp_fsm[NUM_PPP]; /* IPXCP fsm structure */
+
+static fsm_callbacks ipxcp_callbacks = { /* IPXCP callback routines */
+ ipxcp_resetci, /* Reset our Configuration Information */
+ ipxcp_cilen, /* Length of our Configuration Information */
+ ipxcp_addci, /* Add our Configuration Information */
+ ipxcp_ackci, /* ACK our Configuration Information */
+ ipxcp_nakci, /* NAK our Configuration Information */
+ ipxcp_rejci, /* Reject our Configuration Information */
+ ipxcp_reqci, /* Request peer's Configuration Information */
+ ipxcp_up, /* Called when fsm reaches OPENED state */
+ ipxcp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipxcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPXCP" /* String name of protocol */
+};
+
+/*
+ * Command-line options.
+ */
+static int setipxnode __P((char **));
+static void printipxnode __P((option_t *,
+ void (*)(void *, char *, ...), void *));
+static int setipxname __P((char **));
+
+static option_t ipxcp_option_list[] = {
+ { "ipx", o_bool, &ipxcp_protent.enabled_flag,
+ "Enable IPXCP (and IPX)", OPT_PRIO | 1 },
+ { "+ipx", o_bool, &ipxcp_protent.enabled_flag,
+ "Enable IPXCP (and IPX)", OPT_PRIOSUB | OPT_ALIAS | 1 },
+ { "noipx", o_bool, &ipxcp_protent.enabled_flag,
+ "Disable IPXCP (and IPX)", OPT_PRIOSUB },
+ { "-ipx", o_bool, &ipxcp_protent.enabled_flag,
+ "Disable IPXCP (and IPX)", OPT_PRIOSUB | OPT_ALIAS },
+
+ { "ipx-network", o_uint32, &ipxcp_wantoptions[0].our_network,
+ "Set our IPX network number", OPT_PRIO, &ipxcp_wantoptions[0].neg_nn },
+
+ { "ipxcp-accept-network", o_bool, &ipxcp_wantoptions[0].accept_network,
+ "Accept peer IPX network number", 1,
+ &ipxcp_allowoptions[0].accept_network },
+
+ { "ipx-node", o_special, (void *)setipxnode,
+ "Set IPX node number", OPT_A2PRINTER, (void *)printipxnode },
+
+ { "ipxcp-accept-local", o_bool, &ipxcp_wantoptions[0].accept_local,
+ "Accept our IPX address", 1,
+ &ipxcp_allowoptions[0].accept_local },
+
+ { "ipxcp-accept-remote", o_bool, &ipxcp_wantoptions[0].accept_remote,
+ "Accept peer's IPX address", 1,
+ &ipxcp_allowoptions[0].accept_remote },
+
+ { "ipx-routing", o_int, &ipxcp_wantoptions[0].router,
+ "Set IPX routing proto number", OPT_PRIO,
+ &ipxcp_wantoptions[0].neg_router },
+
+ { "ipx-router-name", o_special, setipxname,
+ "Set IPX router name", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC,
+ &ipxcp_wantoptions[0].name },
+
+ { "ipxcp-restart", o_int, &ipxcp_fsm[0].timeouttime,
+ "Set timeout for IPXCP", OPT_PRIO },
+ { "ipxcp-max-terminate", o_int, &ipxcp_fsm[0].maxtermtransmits,
+ "Set max #xmits for IPXCP term-reqs", OPT_PRIO },
+ { "ipxcp-max-configure", o_int, &ipxcp_fsm[0].maxconfreqtransmits,
+ "Set max #xmits for IPXCP conf-reqs", OPT_PRIO },
+ { "ipxcp-max-failure", o_int, &ipxcp_fsm[0].maxnakloops,
+ "Set max #conf-naks for IPXCP", OPT_PRIO },
+
+ { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+
+static void ipxcp_init __P((int));
+static void ipxcp_open __P((int));
+static void ipxcp_close __P((int, char *));
+static void ipxcp_lowerup __P((int));
+static void ipxcp_lowerdown __P((int));
+static void ipxcp_input __P((int, u_char *, int));
+static void ipxcp_protrej __P((int));
+static int ipxcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+
+struct protent ipxcp_protent = {
+ PPP_IPXCP,
+ ipxcp_init,
+ ipxcp_input,
+ ipxcp_protrej,
+ ipxcp_lowerup,
+ ipxcp_lowerdown,
+ ipxcp_open,
+ ipxcp_close,
+ ipxcp_printpkt,
+ NULL,
+ 0,
+ "IPXCP",
+ "IPX",
+ ipxcp_option_list,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*
+ * Lengths of configuration options.
+ */
+
+#define CILEN_VOID 2
+#define CILEN_COMPLETE 2 /* length of complete option */
+#define CILEN_NETN 6 /* network number length option */
+#define CILEN_NODEN 8 /* node number length option */
+#define CILEN_PROTOCOL 4 /* Minimum length of routing protocol */
+#define CILEN_NAME 3 /* Minimum length of router name */
+#define CILEN_COMPRESS 4 /* Minimum length of compression protocol */
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+static int ipxcp_is_up;
+
+static char *ipx_ntoa __P((u_int32_t));
+
+/* Used in printing the node number */
+#define NODE(base) base[0], base[1], base[2], base[3], base[4], base[5]
+
+/* Used to generate the proper bit mask */
+#define BIT(num) (1 << (num))
+
+/*
+ * Convert from internal to external notation
+ */
+
+static short int
+to_external(internal)
+short int internal;
+{
+ short int external;
+
+ if (internal & BIT(IPX_NONE) )
+ external = IPX_NONE;
+ else
+ external = RIP_SAP;
+
+ return external;
+}
+
+/*
+ * Make a string representation of a network IP address.
+ */
+
+static char *
+ipx_ntoa(ipxaddr)
+u_int32_t ipxaddr;
+{
+ static char b[64];
+ slprintf(b, sizeof(b), "%x", ipxaddr);
+ return b;
+}
+
+
+static u_char *
+setipxnodevalue(src,dst)
+u_char *src, *dst;
+{
+ int indx;
+ int item;
+
+ for (;;) {
+ if (!isxdigit (*src))
+ break;
+
+ for (indx = 0; indx < 5; ++indx) {
+ dst[indx] <<= 4;
+ dst[indx] |= (dst[indx + 1] >> 4) & 0x0F;
+ }
+
+ item = toupper (*src) - '0';
+ if (item > 9)
+ item -= 7;
+
+ dst[5] = (dst[5] << 4) | item;
+ ++src;
+ }
+ return src;
+}
+
+static int ipx_prio_our, ipx_prio_his;
+
+static int
+setipxnode(argv)
+ char **argv;
+{
+ char *end;
+ int have_his = 0;
+ u_char our_node[6];
+ u_char his_node[6];
+
+ memset (our_node, 0, 6);
+ memset (his_node, 0, 6);
+
+ end = setipxnodevalue (*argv, our_node);
+ if (*end == ':') {
+ have_his = 1;
+ end = setipxnodevalue (++end, his_node);
+ }
+
+ if (*end == '\0') {
+ ipxcp_wantoptions[0].neg_node = 1;
+ if (option_priority >= ipx_prio_our) {
+ memcpy(&ipxcp_wantoptions[0].our_node[0], our_node, 6);
+ ipx_prio_our = option_priority;
+ }
+ if (have_his && option_priority >= ipx_prio_his) {
+ memcpy(&ipxcp_wantoptions[0].his_node[0], his_node, 6);
+ ipx_prio_his = option_priority;
+ }
+ return 1;
+ }
+
+ option_error("invalid parameter '%s' for ipx-node option", *argv);
+ return 0;
+}
+
+static void
+printipxnode(opt, printer, arg)
+ option_t *opt;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ unsigned char *p;
+
+ p = ipxcp_wantoptions[0].our_node;
+ if (ipx_prio_our)
+ printer(arg, "%.2x%.2x%.2x%.2x%.2x%.2x",
+ p[0], p[1], p[2], p[3], p[4], p[5]);
+ printer(arg, ":");
+ p = ipxcp_wantoptions[0].his_node;
+ if (ipx_prio_his)
+ printer(arg, "%.2x%.2x%.2x%.2x%.2x%.2x",
+ p[0], p[1], p[2], p[3], p[4], p[5]);
+}
+
+static int
+setipxname (argv)
+ char **argv;
+{
+ char *dest = ipxcp_wantoptions[0].name;
+ char *src = *argv;
+ int count;
+ char ch;
+
+ ipxcp_wantoptions[0].neg_name = 1;
+ ipxcp_allowoptions[0].neg_name = 1;
+ memset (dest, '\0', sizeof (ipxcp_wantoptions[0].name));
+
+ count = 0;
+ while (*src) {
+ ch = *src++;
+ if (! isalnum (ch) && ch != '_') {
+ option_error("IPX router name must be alphanumeric or _");
+ return 0;
+ }
+
+ if (count >= sizeof (ipxcp_wantoptions[0].name) - 1) {
+ option_error("IPX router name is limited to %d characters",
+ sizeof (ipxcp_wantoptions[0].name) - 1);
+ return 0;
+ }
+
+ dest[count++] = toupper (ch);
+ }
+ dest[count] = 0;
+
+ return 1;
+}
+
+/*
+ * ipxcp_init - Initialize IPXCP.
+ */
+static void
+ipxcp_init(unit)
+ int unit;
+{
+ fsm *f = &ipxcp_fsm[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPXCP;
+ f->callbacks = &ipxcp_callbacks;
+ fsm_init(&ipxcp_fsm[unit]);
+
+ memset (wo->name, 0, sizeof (wo->name));
+ memset (wo->our_node, 0, sizeof (wo->our_node));
+ memset (wo->his_node, 0, sizeof (wo->his_node));
+
+ wo->neg_nn = 1;
+ wo->neg_complete = 1;
+ wo->network = 0;
+
+ ao->neg_node = 1;
+ ao->neg_nn = 1;
+ ao->neg_name = 1;
+ ao->neg_complete = 1;
+ ao->neg_router = 1;
+
+ ao->accept_local = 0;
+ ao->accept_remote = 0;
+ ao->accept_network = 0;
+
+ wo->tried_rip = 0;
+ wo->tried_nlsp = 0;
+}
+
+/*
+ * Copy the node number
+ */
+
+static void
+copy_node (src, dst)
+u_char *src, *dst;
+{
+ memcpy (dst, src, sizeof (ipxcp_wantoptions[0].our_node));
+}
+
+/*
+ * Compare node numbers
+ */
+
+static int
+compare_node (src, dst)
+u_char *src, *dst;
+{
+ return memcmp (dst, src, sizeof (ipxcp_wantoptions[0].our_node)) == 0;
+}
+
+/*
+ * Is the node number zero?
+ */
+
+static int
+zero_node (node)
+u_char *node;
+{
+ int indx;
+ for (indx = 0; indx < sizeof (ipxcp_wantoptions[0].our_node); ++indx)
+ if (node [indx] != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Increment the node number
+ */
+
+static void
+inc_node (node)
+u_char *node;
+{
+ u_char *outp;
+ u_int32_t magic_num;
+
+ outp = node;
+ magic_num = magic();
+ *outp++ = '\0';
+ *outp++ = '\0';
+ PUTLONG (magic_num, outp);
+}
+
+/*
+ * ipxcp_open - IPXCP is allowed to come up.
+ */
+static void
+ipxcp_open(unit)
+ int unit;
+{
+ fsm_open(&ipxcp_fsm[unit]);
+}
+
+/*
+ * ipxcp_close - Take IPXCP down.
+ */
+static void
+ipxcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipxcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipxcp_lowerup - The lower layer is up.
+ */
+static void
+ipxcp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_lowerdown - The lower layer is down.
+ */
+static void
+ipxcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_input - Input IPXCP packet.
+ */
+static void
+ipxcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipxcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipxcp_protrej - A Protocol-Reject was received for IPXCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipxcp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_resetci - Reset our CI.
+ */
+static void
+ipxcp_resetci(f)
+ fsm *f;
+{
+ wo->req_node = wo->neg_node && ao->neg_node;
+ wo->req_nn = wo->neg_nn && ao->neg_nn;
+
+ if (wo->our_network == 0) {
+ wo->neg_node = 1;
+ ao->accept_network = 1;
+ }
+/*
+ * If our node number is zero then change it.
+ */
+ if (zero_node (wo->our_node)) {
+ inc_node (wo->our_node);
+ ao->accept_local = 1;
+ wo->neg_node = 1;
+ }
+/*
+ * If his node number is zero then change it.
+ */
+ if (zero_node (wo->his_node)) {
+ inc_node (wo->his_node);
+ ao->accept_remote = 1;
+ }
+/*
+ * If no routing agent was specified then we do RIP/SAP according to the
+ * RFC documents. If you have specified something then OK. Otherwise, we
+ * do RIP/SAP.
+ */
+ if (ao->router == 0) {
+ ao->router |= BIT(RIP_SAP);
+ wo->router |= BIT(RIP_SAP);
+ }
+
+ /* Always specify a routing protocol unless it was REJected. */
+ wo->neg_router = 1;
+/*
+ * Start with these default values
+ */
+ *go = *wo;
+}
+
+/*
+ * ipxcp_cilen - Return length of our CI.
+ */
+
+static int
+ipxcp_cilen(f)
+ fsm *f;
+{
+ int len;
+
+ len = go->neg_nn ? CILEN_NETN : 0;
+ len += go->neg_node ? CILEN_NODEN : 0;
+ len += go->neg_name ? CILEN_NAME + strlen (go->name) - 1 : 0;
+
+ /* RFC says that defaults should not be included. */
+ if (go->neg_router && to_external(go->router) != RIP_SAP)
+ len += CILEN_PROTOCOL;
+
+ return (len);
+}
+
+
+/*
+ * ipxcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipxcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+/*
+ * Add the options to the record.
+ */
+ if (go->neg_nn) {
+ PUTCHAR (IPX_NETWORK_NUMBER, ucp);
+ PUTCHAR (CILEN_NETN, ucp);
+ PUTLONG (go->our_network, ucp);
+ }
+
+ if (go->neg_node) {
+ int indx;
+ PUTCHAR (IPX_NODE_NUMBER, ucp);
+ PUTCHAR (CILEN_NODEN, ucp);
+ for (indx = 0; indx < sizeof (go->our_node); ++indx)
+ PUTCHAR (go->our_node[indx], ucp);
+ }
+
+ if (go->neg_name) {
+ int cilen = strlen (go->name);
+ int indx;
+ PUTCHAR (IPX_ROUTER_NAME, ucp);
+ PUTCHAR (CILEN_NAME + cilen - 1, ucp);
+ for (indx = 0; indx < cilen; ++indx)
+ PUTCHAR (go->name [indx], ucp);
+ }
+
+ if (go->neg_router) {
+ short external = to_external (go->router);
+ if (external != RIP_SAP) {
+ PUTCHAR (IPX_ROUTER_PROTOCOL, ucp);
+ PUTCHAR (CILEN_PROTOCOL, ucp);
+ PUTSHORT (external, ucp);
+ }
+ }
+}
+
+/*
+ * ipxcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipxcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ u_short cilen, citype, cishort;
+ u_char cichar;
+ u_int32_t cilong;
+
+#define ACKCIVOID(opt, neg) \
+ if (neg) { \
+ if ((len -= CILEN_VOID) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || \
+ citype != opt) \
+ break; \
+ }
+
+#define ACKCICOMPLETE(opt,neg) ACKCIVOID(opt, neg)
+
+#define ACKCICHARS(opt, neg, val, cnt) \
+ if (neg) { \
+ int indx, count = cnt; \
+ len -= (count + 2); \
+ if (len < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != (count + 2) || \
+ citype != opt) \
+ break; \
+ for (indx = 0; indx < count; ++indx) {\
+ GETCHAR(cichar, p); \
+ if (cichar != ((u_char *) &val)[indx]) \
+ break; \
+ }\
+ if (indx != count) \
+ break; \
+ }
+
+#define ACKCINODE(opt,neg,val) ACKCICHARS(opt,neg,val,sizeof(val))
+#define ACKCINAME(opt,neg,val) ACKCICHARS(opt,neg,val,strlen(val))
+
+#define ACKCINETWORK(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_NETN) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_NETN || \
+ citype != opt) \
+ break; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ break; \
+ }
+
+#define ACKCIPROTO(opt, neg, val) \
+ if (neg) { \
+ if (len < 2) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_PROTOCOL || citype != opt) \
+ break; \
+ len -= cilen; \
+ if (len < 0) \
+ break; \
+ GETSHORT(cishort, p); \
+ if (cishort != to_external (val) || cishort == RIP_SAP) \
+ break; \
+ }
+/*
+ * Process the ACK frame in the order in which the frame was assembled
+ */
+ do {
+ ACKCINETWORK (IPX_NETWORK_NUMBER, go->neg_nn, go->our_network);
+ ACKCINODE (IPX_NODE_NUMBER, go->neg_node, go->our_node);
+ ACKCINAME (IPX_ROUTER_NAME, go->neg_name, go->name);
+ if (len > 0)
+ ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router);
+/*
+ * This is the end of the record.
+ */
+ if (len == 0)
+ return (1);
+ } while (0);
+/*
+ * The frame is invalid
+ */
+ IPXCPDEBUG(("ipxcp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipxcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPXCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+
+static int
+ipxcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ u_char citype, cilen, *next;
+ u_short s;
+ u_int32_t l;
+ ipxcp_options no; /* options we've seen Naks for */
+ ipxcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ while (len > CILEN_VOID) {
+ GETCHAR (citype, p);
+ GETCHAR (cilen, p);
+ len -= cilen;
+ if (len < 0)
+ goto bad;
+ next = &p [cilen - CILEN_VOID];
+
+ switch (citype) {
+ case IPX_NETWORK_NUMBER:
+ if (!go->neg_nn || no.neg_nn || (cilen != CILEN_NETN))
+ goto bad;
+ no.neg_nn = 1;
+
+ GETLONG(l, p);
+ if (l && ao->accept_network)
+ try.our_network = l;
+ break;
+
+ case IPX_NODE_NUMBER:
+ if (!go->neg_node || no.neg_node || (cilen != CILEN_NODEN))
+ goto bad;
+ no.neg_node = 1;
+
+ if (!zero_node (p) && ao->accept_local &&
+ ! compare_node (p, ho->his_node))
+ copy_node (p, try.our_node);
+ break;
+
+ /* This has never been sent. Ignore the NAK frame */
+ case IPX_COMPRESSION_PROTOCOL:
+ goto bad;
+
+ case IPX_ROUTER_PROTOCOL:
+ if (!go->neg_router || (cilen < CILEN_PROTOCOL))
+ goto bad;
+
+ GETSHORT (s, p);
+ if (s > 15) /* This is just bad, but ignore for now. */
+ break;
+
+ s = BIT(s);
+ if (no.router & s) /* duplicate NAKs are always bad */
+ goto bad;
+
+ if (no.router == 0) /* Reset on first NAK only */
+ try.router = 0;
+
+ no.router |= s;
+ try.router |= s;
+ try.neg_router = 1;
+ break;
+
+ /* These, according to the RFC, must never be NAKed. */
+ case IPX_ROUTER_NAME:
+ case IPX_COMPLETE:
+ goto bad;
+
+ /* These are for options which we have not seen. */
+ default:
+ break;
+ }
+ p = next;
+ }
+
+ /*
+ * Do not permit the peer to force a router protocol which we do not
+ * support. However, default to the condition that will accept "NONE".
+ */
+ try.router &= (ao->router | BIT(IPX_NONE));
+ if (try.router == 0 && ao->router != 0)
+ try.router = BIT(IPX_NONE);
+
+ if (try.router != 0)
+ try.neg_router = 1;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ * If there are any options left, we ignore them.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPXCPDEBUG(("ipxcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+/*
+ * ipxcp_rejci - Reject some of our CIs.
+ */
+static int
+ipxcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ u_short cilen, citype, cishort;
+ u_char cichar;
+ u_int32_t cilong;
+ ipxcp_options try; /* options to request next time */
+
+#define REJCINETWORK(opt, neg, val) \
+ if (neg && p[0] == opt) { \
+ if ((len -= CILEN_NETN) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_NETN || \
+ citype != opt) \
+ break; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ break; \
+ neg = 0; \
+ }
+
+#define REJCICHARS(opt, neg, val, cnt) \
+ if (neg && p[0] == opt) { \
+ int indx, count = cnt; \
+ len -= (count + 2); \
+ if (len < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != (count + 2) || \
+ citype != opt) \
+ break; \
+ for (indx = 0; indx < count; ++indx) {\
+ GETCHAR(cichar, p); \
+ if (cichar != ((u_char *) &val)[indx]) \
+ break; \
+ }\
+ if (indx != count) \
+ break; \
+ neg = 0; \
+ }
+
+#define REJCINODE(opt,neg,val) REJCICHARS(opt,neg,val,sizeof(val))
+#define REJCINAME(opt,neg,val) REJCICHARS(opt,neg,val,strlen(val))
+
+#define REJCIVOID(opt, neg) \
+ if (neg && p[0] == opt) { \
+ if ((len -= CILEN_VOID) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || citype != opt) \
+ break; \
+ neg = 0; \
+ }
+
+/* a reject for RIP/SAP is invalid since we don't send it and you can't
+ reject something which is not sent. (You can NAK, but you can't REJ.) */
+#define REJCIPROTO(opt, neg, val, bit) \
+ if (neg && p[0] == opt) { \
+ if ((len -= CILEN_PROTOCOL) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_PROTOCOL) \
+ break; \
+ GETSHORT(cishort, p); \
+ if (cishort != to_external (val) || cishort == RIP_SAP) \
+ break; \
+ neg = 0; \
+ }
+/*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+ try = *go;
+
+ do {
+ REJCINETWORK (IPX_NETWORK_NUMBER, try.neg_nn, try.our_network);
+ REJCINODE (IPX_NODE_NUMBER, try.neg_node, try.our_node);
+ REJCINAME (IPX_ROUTER_NAME, try.neg_name, try.name);
+ REJCIPROTO (IPX_ROUTER_PROTOCOL, try.neg_router, try.router, 0);
+/*
+ * This is the end of the record.
+ */
+ if (len == 0) {
+ if (f->state != OPENED)
+ *go = try;
+ return (1);
+ }
+ } while (0);
+/*
+ * The frame is invalid at this point.
+ */
+ IPXCPDEBUG(("ipxcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+/*
+ * ipxcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipxcp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u_int32_t cinetwork; /* Parsed address values */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPXCPDEBUG(("ipxcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+/*
+ * The network number must match. Choose the larger of the two.
+ */
+ case IPX_NETWORK_NUMBER:
+ /* if we wont negotiate the network number or the length is wrong
+ then reject the option */
+ if ( !ao->neg_nn || cilen != CILEN_NETN ) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cinetwork, p);
+
+ /* If the network numbers match then acknowledge them. */
+ if (cinetwork != 0) {
+ ho->his_network = cinetwork;
+ ho->neg_nn = 1;
+ if (wo->our_network == cinetwork)
+ break;
+/*
+ * If the network number is not given or we don't accept their change or
+ * the network number is too small then NAK it.
+ */
+ if (! ao->accept_network || cinetwork < wo->our_network) {
+ DECPTR (sizeof (u_int32_t), p);
+ PUTLONG (wo->our_network, p);
+ orc = CONFNAK;
+ }
+ break;
+ }
+/*
+ * The peer sent '0' for the network. Give it ours if we have one.
+ */
+ if (go->our_network != 0) {
+ DECPTR (sizeof (u_int32_t), p);
+ PUTLONG (wo->our_network, p);
+ orc = CONFNAK;
+/*
+ * We don't have one. Reject the value.
+ */
+ } else
+ orc = CONFREJ;
+
+ break;
+/*
+ * The node number is required
+ */
+ case IPX_NODE_NUMBER:
+ /* if we wont negotiate the node number or the length is wrong
+ then reject the option */
+ if ( cilen != CILEN_NODEN ) {
+ orc = CONFREJ;
+ break;
+ }
+
+ copy_node (p, ho->his_node);
+ ho->neg_node = 1;
+/*
+ * If the remote does not have a number and we do then NAK it with the value
+ * which we have for it. (We never have a default value of zero.)
+ */
+ if (zero_node (ho->his_node)) {
+ orc = CONFNAK;
+ copy_node (wo->his_node, p);
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+ }
+/*
+ * If you have given me the expected network node number then I'll accept
+ * it now.
+ */
+ if (compare_node (wo->his_node, ho->his_node)) {
+ orc = CONFACK;
+ ho->neg_node = 1;
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+ }
+/*
+ * If his node number is the same as ours then ask him to try the next
+ * value.
+ */
+ if (compare_node (ho->his_node, go->our_node)) {
+ inc_node (ho->his_node);
+ orc = CONFNAK;
+ copy_node (ho->his_node, p);
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+ }
+/*
+ * If we don't accept a new value then NAK it.
+ */
+ if (! ao->accept_remote) {
+ copy_node (wo->his_node, p);
+ INCPTR (sizeof (wo->his_node), p);
+ orc = CONFNAK;
+ break;
+ }
+ orc = CONFACK;
+ ho->neg_node = 1;
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+/*
+ * Compression is not desired at this time. It is always rejected.
+ */
+ case IPX_COMPRESSION_PROTOCOL:
+ orc = CONFREJ;
+ break;
+/*
+ * The routing protocol is a bitmask of various types. Any combination
+ * of the values RIP_SAP and NLSP are permissible. 'IPX_NONE' for no
+ * routing protocol must be specified only once.
+ */
+ case IPX_ROUTER_PROTOCOL:
+ if ( !ao->neg_router || cilen < CILEN_PROTOCOL ) {
+ orc = CONFREJ;
+ break;
+ }
+
+ GETSHORT (cishort, p);
+
+ if (wo->neg_router == 0) {
+ wo->neg_router = 1;
+ wo->router = BIT(IPX_NONE);
+ }
+
+ if ((cishort == IPX_NONE && ho->router != 0) ||
+ (ho->router & BIT(IPX_NONE))) {
+ orc = CONFREJ;
+ break;
+ }
+
+ cishort = BIT(cishort);
+ if (ho->router & cishort) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->router |= cishort;
+ ho->neg_router = 1;
+
+ /* Finally do not allow a router protocol which we do not
+ support. */
+
+ if ((cishort & (ao->router | BIT(IPX_NONE))) == 0) {
+ int protocol;
+
+ if (cishort == BIT(NLSP) &&
+ (ao->router & BIT(RIP_SAP)) &&
+ !wo->tried_rip) {
+ protocol = RIP_SAP;
+ wo->tried_rip = 1;
+ } else
+ protocol = IPX_NONE;
+
+ DECPTR (sizeof (u_int16_t), p);
+ PUTSHORT (protocol, p);
+ orc = CONFNAK;
+ }
+ break;
+/*
+ * The router name is advisorary. Just accept it if it is not too large.
+ */
+ case IPX_ROUTER_NAME:
+ if (cilen >= CILEN_NAME) {
+ int name_size = cilen - CILEN_NAME;
+ if (name_size > sizeof (ho->name))
+ name_size = sizeof (ho->name) - 1;
+ memset (ho->name, 0, sizeof (ho->name));
+ memcpy (ho->name, p, name_size);
+ ho->name [name_size] = '\0';
+ ho->neg_name = 1;
+ orc = CONFACK;
+ break;
+ }
+ orc = CONFREJ;
+ break;
+/*
+ * This is advisorary.
+ */
+ case IPX_COMPLETE:
+ if (cilen != CILEN_COMPLETE)
+ orc = CONFREJ;
+ else {
+ ho->neg_complete = 1;
+ orc = CONFACK;
+ }
+ break;
+/*
+ * All other entries are not known at this time.
+ */
+ default:
+ orc = CONFREJ;
+ break;
+ }
+endswitch:
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with a IPX_NODE_NUMBER option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+
+ if (rc != CONFREJ && !ho->neg_node &&
+ wo->req_nn && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ wo->req_nn = 0; /* don't ask again */
+ ucp = inp; /* reset pointer */
+ }
+
+ if (zero_node (wo->his_node))
+ inc_node (wo->his_node);
+
+ PUTCHAR (IPX_NODE_NUMBER, ucp);
+ PUTCHAR (CILEN_NODEN, ucp);
+ copy_node (wo->his_node, ucp);
+ INCPTR (sizeof (wo->his_node), ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPXCPDEBUG(("ipxcp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+/*
+ * ipxcp_up - IPXCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+
+static void
+ipxcp_up(f)
+ fsm *f;
+{
+ int unit = f->unit;
+
+ IPXCPDEBUG(("ipxcp: up"));
+
+ /* The default router protocol is RIP/SAP. */
+ if (ho->router == 0)
+ ho->router = BIT(RIP_SAP);
+
+ if (go->router == 0)
+ go->router = BIT(RIP_SAP);
+
+ /* Fetch the network number */
+ if (!ho->neg_nn)
+ ho->his_network = wo->his_network;
+
+ if (!ho->neg_node)
+ copy_node (wo->his_node, ho->his_node);
+
+ if (!wo->neg_node && !go->neg_node)
+ copy_node (wo->our_node, go->our_node);
+
+ if (zero_node (go->our_node)) {
+ static char errmsg[] = "Could not determine local IPX node address";
+ if (debug)
+ error(errmsg);
+ ipxcp_close(f->unit, errmsg);
+ return;
+ }
+
+ go->network = go->our_network;
+ if (ho->his_network != 0 && ho->his_network > go->network)
+ go->network = ho->his_network;
+
+ if (go->network == 0) {
+ static char errmsg[] = "Can not determine network number";
+ if (debug)
+ error(errmsg);
+ ipxcp_close (unit, errmsg);
+ return;
+ }
+
+ /* bring the interface up */
+ if (!sifup(unit)) {
+ if (debug)
+ warn("sifup failed (IPX)");
+ ipxcp_close(unit, "Interface configuration failed");
+ return;
+ }
+ ipxcp_is_up = 1;
+
+ /* set the network number for IPX */
+ if (!sipxfaddr(unit, go->network, go->our_node)) {
+ if (debug)
+ warn("sipxfaddr failed");
+ ipxcp_close(unit, "Interface configuration failed");
+ return;
+ }
+
+ np_up(f->unit, PPP_IPX);
+
+ /*
+ * Execute the ipx-up script, like this:
+ * /etc/ppp/ipx-up interface tty speed local-IPX remote-IPX
+ */
+
+ ipxcp_script (f, _PATH_IPXUP);
+}
+
+/*
+ * ipxcp_down - IPXCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+
+static void
+ipxcp_down(f)
+ fsm *f;
+{
+ IPXCPDEBUG(("ipxcp: down"));
+
+ if (!ipxcp_is_up)
+ return;
+ ipxcp_is_up = 0;
+ np_down(f->unit, PPP_IPX);
+ cipxfaddr(f->unit);
+ sifnpmode(f->unit, PPP_IPX, NPMODE_DROP);
+ sifdown(f->unit);
+ ipxcp_script (f, _PATH_IPXDOWN);
+}
+
+
+/*
+ * ipxcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipxcp_finished(f)
+ fsm *f;
+{
+ np_finished(f->unit, PPP_IPX);
+}
+
+
+/*
+ * ipxcp_script - Execute a script with arguments
+ * interface-name tty-name speed local-IPX remote-IPX networks.
+ */
+static void
+ipxcp_script(f, script)
+ fsm *f;
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char strnetwork[32], strpid[32];
+ char *argv[14], strproto_lcl[32], strproto_rmt[32];
+
+ slprintf(strpid, sizeof(strpid), "%d", getpid());
+ slprintf(strspeed, sizeof(strspeed),"%d", baud_rate);
+
+ strproto_lcl[0] = '\0';
+ if (go->neg_router && ((go->router & BIT(IPX_NONE)) == 0)) {
+ if (go->router & BIT(RIP_SAP))
+ strlcpy (strproto_lcl, "RIP ", sizeof(strproto_lcl));
+ if (go->router & BIT(NLSP))
+ strlcat (strproto_lcl, "NLSP ", sizeof(strproto_lcl));
+ }
+
+ if (strproto_lcl[0] == '\0')
+ strlcpy (strproto_lcl, "NONE ", sizeof(strproto_lcl));
+
+ strproto_lcl[strlen (strproto_lcl)-1] = '\0';
+
+ strproto_rmt[0] = '\0';
+ if (ho->neg_router && ((ho->router & BIT(IPX_NONE)) == 0)) {
+ if (ho->router & BIT(RIP_SAP))
+ strlcpy (strproto_rmt, "RIP ", sizeof(strproto_rmt));
+ if (ho->router & BIT(NLSP))
+ strlcat (strproto_rmt, "NLSP ", sizeof(strproto_rmt));
+ }
+
+ if (strproto_rmt[0] == '\0')
+ strlcpy (strproto_rmt, "NONE ", sizeof(strproto_rmt));
+
+ strproto_rmt[strlen (strproto_rmt)-1] = '\0';
+
+ strlcpy (strnetwork, ipx_ntoa (go->network), sizeof(strnetwork));
+
+ slprintf (strlocal, sizeof(strlocal), "%0.6B", go->our_node);
+
+ slprintf (strremote, sizeof(strremote), "%0.6B", ho->his_node);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strnetwork;
+ argv[5] = strlocal;
+ argv[6] = strremote;
+ argv[7] = strproto_lcl;
+ argv[8] = strproto_rmt;
+ argv[9] = go->name;
+ argv[10] = ho->name;
+ argv[11] = ipparam;
+ argv[12] = strpid;
+ argv[13] = NULL;
+ run_program(script, argv, 0, NULL, NULL);
+}
+
+/*
+ * ipxcp_printpkt - print the contents of an IPXCP packet.
+ */
+static char *ipxcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipxcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ipxcp_codenames) / sizeof(char *))
+ printer(arg, " %s", ipxcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < CILEN_VOID || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case IPX_NETWORK_NUMBER:
+ if (olen == CILEN_NETN) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer (arg, "network %s", ipx_ntoa (cilong));
+ }
+ break;
+ case IPX_NODE_NUMBER:
+ if (olen == CILEN_NODEN) {
+ p += 2;
+ printer (arg, "node ");
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, "%.2x", (int) (unsigned int) (unsigned char) code);
+ }
+ }
+ break;
+ case IPX_COMPRESSION_PROTOCOL:
+ if (olen == CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT (cishort, p);
+ printer (arg, "compression %d", (int) cishort);
+ }
+ break;
+ case IPX_ROUTER_PROTOCOL:
+ if (olen == CILEN_PROTOCOL) {
+ p += 2;
+ GETSHORT (cishort, p);
+ printer (arg, "router proto %d", (int) cishort);
+ }
+ break;
+ case IPX_ROUTER_NAME:
+ if (olen >= CILEN_NAME) {
+ p += 2;
+ printer (arg, "router name \"");
+ while (p < optend) {
+ GETCHAR(code, p);
+ if (code >= 0x20 && code <= 0x7E)
+ printer (arg, "%c", (int) (unsigned int) (unsigned char) code);
+ else
+ printer (arg, " \\%.2x", (int) (unsigned int) (unsigned char) code);
+ }
+ printer (arg, "\"");
+ }
+ break;
+ case IPX_COMPLETE:
+ if (olen == CILEN_COMPLETE) {
+ p += 2;
+ printer (arg, "complete");
+ }
+ break;
+ default:
+ break;
+ }
+
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
+ }
+
+ return p - pstart;
+}
+#endif /* ifdef IPX_CHANGE */
diff --git a/mdk-stage1/ppp/pppd/ipxcp.h b/mdk-stage1/ppp/pppd/ipxcp.h
new file mode 100644
index 000000000..47f680d70
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/ipxcp.h
@@ -0,0 +1,71 @@
+/*
+ * ipxcp.h - IPX Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Options.
+ */
+#define IPX_NETWORK_NUMBER 1 /* IPX Network Number */
+#define IPX_NODE_NUMBER 2
+#define IPX_COMPRESSION_PROTOCOL 3
+#define IPX_ROUTER_PROTOCOL 4
+#define IPX_ROUTER_NAME 5
+#define IPX_COMPLETE 6
+
+/* Values for the router protocol */
+#define IPX_NONE 0
+#define RIP_SAP 2
+#define NLSP 4
+
+typedef struct ipxcp_options {
+ bool neg_node; /* Negotiate IPX node number? */
+ bool req_node; /* Ask peer to send IPX node number? */
+
+ bool neg_nn; /* Negotiate IPX network number? */
+ bool req_nn; /* Ask peer to send IPX network number */
+
+ bool neg_name; /* Negotiate IPX router name */
+ bool neg_complete; /* Negotiate completion */
+ bool neg_router; /* Negotiate IPX router number */
+
+ bool accept_local; /* accept peer's value for ournode */
+ bool accept_remote; /* accept peer's value for hisnode */
+ bool accept_network; /* accept network number */
+
+ bool tried_nlsp; /* I have suggested NLSP already */
+ bool tried_rip; /* I have suggested RIP/SAP already */
+
+ u_int32_t his_network; /* base network number */
+ u_int32_t our_network; /* our value for network number */
+ u_int32_t network; /* the final network number */
+
+ u_char his_node[6]; /* peer's node number */
+ u_char our_node[6]; /* our node number */
+ u_char name [48]; /* name of the router */
+ int router; /* routing protocol */
+} ipxcp_options;
+
+extern fsm ipxcp_fsm[];
+extern ipxcp_options ipxcp_wantoptions[];
+extern ipxcp_options ipxcp_gotoptions[];
+extern ipxcp_options ipxcp_allowoptions[];
+extern ipxcp_options ipxcp_hisoptions[];
+
+extern struct protent ipxcp_protent;
diff --git a/mdk-stage1/ppp/pppd/lcp.c b/mdk-stage1/ppp/pppd/lcp.c
new file mode 100644
index 000000000..41c58cad5
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/lcp.c
@@ -0,0 +1,2224 @@
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "chap.h"
+#include "magic.h"
+
+static const char rcsid[] = RCSID;
+
+/*
+ * When the link comes up we want to be able to wait for a short while,
+ * or until seeing some input from the peer, before starting to send
+ * configure-requests. We do this by delaying the fsm_lowerup call.
+ */
+/* steal a bit in fsm flags word */
+#define DELAYED_UP 0x100
+
+static void lcp_delayed_up __P((void *));
+
+/*
+ * LCP-related command-line options.
+ */
+int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
+int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
+bool lax_recv = 0; /* accept control chars in asyncmap */
+bool noendpoint = 0; /* don't send/accept endpoint discriminator */
+
+static int noopt __P((char **));
+
+#ifdef HAVE_MULTILINK
+static int setendpoint __P((char **));
+static void printendpoint __P((option_t *, void (*)(void *, char *, ...),
+ void *));
+#endif /* HAVE_MULTILINK */
+
+static option_t lcp_option_list[] = {
+ /* LCP options */
+ { "-all", o_special_noarg, (void *)noopt,
+ "Don't request/allow any LCP options" },
+
+ { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression,
+ "Disable address/control compression",
+ OPT_A2CLR, &lcp_allowoptions[0].neg_accompression },
+ { "-ac", o_bool, &lcp_wantoptions[0].neg_accompression,
+ "Disable address/control compression",
+ OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_accompression },
+
+ { "asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap,
+ "Set asyncmap (for received packets)",
+ OPT_OR, &lcp_wantoptions[0].neg_asyncmap },
+ { "-as", o_uint32, &lcp_wantoptions[0].asyncmap,
+ "Set asyncmap (for received packets)",
+ OPT_ALIAS | OPT_OR, &lcp_wantoptions[0].neg_asyncmap },
+ { "default-asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap,
+ "Disable asyncmap negotiation",
+ OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR,
+ &lcp_allowoptions[0].neg_asyncmap },
+ { "-am", o_uint32, &lcp_wantoptions[0].asyncmap,
+ "Disable asyncmap negotiation",
+ OPT_ALIAS | OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR,
+ &lcp_allowoptions[0].neg_asyncmap },
+
+ { "nomagic", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+ "Disable magic number negotiation (looped-back line detection)",
+ OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber },
+ { "-mn", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+ "Disable magic number negotiation (looped-back line detection)",
+ OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber },
+
+ { "mru", o_int, &lcp_wantoptions[0].mru,
+ "Set MRU (maximum received packet size) for negotiation",
+ OPT_PRIO, &lcp_wantoptions[0].neg_mru },
+ { "default-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+ "Disable MRU negotiation (use default 1500)",
+ OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru },
+ { "-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+ "Disable MRU negotiation (use default 1500)",
+ OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru },
+
+ { "mtu", o_int, &lcp_allowoptions[0].mru,
+ "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU },
+
+ { "nopcomp", o_bool, &lcp_wantoptions[0].neg_pcompression,
+ "Disable protocol field compression",
+ OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression },
+ { "-pc", o_bool, &lcp_wantoptions[0].neg_pcompression,
+ "Disable protocol field compression",
+ OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression },
+
+ { "passive", o_bool, &lcp_wantoptions[0].passive,
+ "Set passive mode", 1 },
+ { "-p", o_bool, &lcp_wantoptions[0].passive,
+ "Set passive mode", OPT_ALIAS | 1 },
+
+ { "silent", o_bool, &lcp_wantoptions[0].silent,
+ "Set silent mode", 1 },
+
+ { "lcp-echo-failure", o_int, &lcp_echo_fails,
+ "Set number of consecutive echo failures to indicate link failure",
+ OPT_PRIO },
+ { "lcp-echo-interval", o_int, &lcp_echo_interval,
+ "Set time in seconds between LCP echo requests", OPT_PRIO },
+ { "lcp-restart", o_int, &lcp_fsm[0].timeouttime,
+ "Set time in seconds between LCP retransmissions", OPT_PRIO },
+ { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits,
+ "Set maximum number of LCP terminate-request transmissions", OPT_PRIO },
+ { "lcp-max-configure", o_int, &lcp_fsm[0].maxconfreqtransmits,
+ "Set maximum number of LCP configure-request transmissions", OPT_PRIO },
+ { "lcp-max-failure", o_int, &lcp_fsm[0].maxnakloops,
+ "Set limit on number of LCP configure-naks", OPT_PRIO },
+
+ { "receive-all", o_bool, &lax_recv,
+ "Accept all received control characters", 1 },
+
+#ifdef HAVE_MULTILINK
+ { "mrru", o_int, &lcp_wantoptions[0].mrru,
+ "Maximum received packet size for multilink bundle",
+ OPT_PRIO, &lcp_wantoptions[0].neg_mrru },
+
+ { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+ "Use short sequence numbers in multilink headers",
+ OPT_PRIO | 1, &lcp_allowoptions[0].neg_ssnhf },
+ { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+ "Don't use short sequence numbers in multilink headers",
+ OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_ssnhf },
+
+ { "endpoint", o_special, (void *) setendpoint,
+ "Endpoint discriminator for multilink",
+ OPT_PRIO | OPT_A2PRINTER, (void *) printendpoint },
+#endif /* HAVE_MULTILINK */
+
+ { "noendpoint", o_bool, &noendpoint,
+ "Don't send or accept multilink endpoint discriminator", 1 },
+
+ {NULL}
+};
+
+/* global vars */
+fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/
+lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+static int lcp_echos_pending = 0; /* Number of outstanding echo msgs */
+static int lcp_echo_number = 0; /* ID number of next echo frame */
+static int lcp_echo_timer_running = 0; /* set if a timer is running */
+
+static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void lcp_resetci __P((fsm *)); /* Reset our CI */
+static int lcp_cilen __P((fsm *)); /* Return length of our CI */
+static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */
+static int lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int lcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */
+static void lcp_up __P((fsm *)); /* We're UP */
+static void lcp_down __P((fsm *)); /* We're DOWN */
+static void lcp_starting __P((fsm *)); /* We need lower layer up */
+static void lcp_finished __P((fsm *)); /* We need lower layer down */
+static int lcp_extcode __P((fsm *, int, int, u_char *, int));
+static void lcp_rprotrej __P((fsm *, u_char *, int));
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup __P((int));
+static void lcp_echo_lowerdown __P((int));
+static void LcpEchoTimeout __P((void *));
+static void lcp_received_echo_reply __P((fsm *, int, u_char *, int));
+static void LcpSendEchoRequest __P((fsm *));
+static void LcpLinkFailure __P((fsm *));
+static void LcpEchoCheck __P((fsm *));
+
+static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
+ lcp_resetci, /* Reset our Configuration Information */
+ lcp_cilen, /* Length of our Configuration Information */
+ lcp_addci, /* Add our Configuration Information */
+ lcp_ackci, /* ACK our Configuration Information */
+ lcp_nakci, /* NAK our Configuration Information */
+ lcp_rejci, /* Reject our Configuration Information */
+ lcp_reqci, /* Request peer's Configuration Information */
+ lcp_up, /* Called when fsm reaches OPENED state */
+ lcp_down, /* Called when fsm leaves OPENED state */
+ lcp_starting, /* Called when we want the lower layer up */
+ lcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ lcp_extcode, /* Called to handle LCP-specific codes */
+ "LCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ * Some of these are called directly.
+ */
+
+static void lcp_init __P((int));
+static void lcp_input __P((int, u_char *, int));
+static void lcp_protrej __P((int));
+static int lcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+
+struct protent lcp_protent = {
+ PPP_LCP,
+ lcp_init,
+ lcp_input,
+ lcp_protrej,
+ lcp_lowerup,
+ lcp_lowerdown,
+ lcp_open,
+ lcp_close,
+ lcp_printpkt,
+ NULL,
+ 1,
+ "LCP",
+ NULL,
+ lcp_option_list,
+ NULL,
+ NULL,
+ NULL
+};
+
+int lcp_loopbackfail = DEFLOOPBACKFAIL;
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID 2
+#define CILEN_CHAR 3
+#define CILEN_SHORT 4 /* CILEN_VOID + 2 */
+#define CILEN_CHAP 5 /* CILEN_VOID + 2 + 1 */
+#define CILEN_LONG 6 /* CILEN_VOID + 4 */
+#define CILEN_LQR 8 /* CILEN_VOID + 2 + 4 */
+#define CILEN_CBCP 3
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+/*
+ * noopt - Disable all options (why?).
+ */
+static int
+noopt(argv)
+ char **argv;
+{
+ BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
+
+ return (1);
+}
+
+#ifdef HAVE_MULTILINK
+static int
+setendpoint(argv)
+ char **argv;
+{
+ if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) {
+ lcp_wantoptions[0].neg_endpoint = 1;
+ return 1;
+ }
+ option_error("Can't parse '%s' as an endpoint discriminator", *argv);
+ return 0;
+}
+
+static void
+printendpoint(opt, printer, arg)
+ option_t *opt;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ printer(arg, "%s", epdisc_to_str(&lcp_wantoptions[0].endpoint));
+}
+#endif /* HAVE_MULTILINK */
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+static void
+lcp_init(unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_LCP;
+ f->callbacks = &lcp_callbacks;
+
+ fsm_init(f);
+
+ BZERO(wo, sizeof(*wo));
+ wo->neg_mru = 1;
+ wo->mru = DEFMRU;
+ wo->neg_asyncmap = 1;
+ wo->chap_mdtype = CHAP_DIGEST_MD5;
+ wo->neg_magicnumber = 1;
+ wo->neg_pcompression = 1;
+ wo->neg_accompression = 1;
+
+ BZERO(ao, sizeof(*ao));
+ ao->neg_mru = 1;
+ ao->mru = MAXMRU;
+ ao->neg_asyncmap = 1;
+ ao->neg_chap = 1;
+ ao->chap_mdtype = CHAP_DIGEST_MD5;
+ ao->neg_upap = 1;
+ ao->neg_magicnumber = 1;
+ ao->neg_pcompression = 1;
+ ao->neg_accompression = 1;
+#ifdef CBCP_SUPPORT
+ ao->neg_cbcp = 1;
+#endif
+ ao->neg_endpoint = 1;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void
+lcp_open(unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ f->flags &= ~(OPT_PASSIVE | OPT_SILENT);
+ if (wo->passive)
+ f->flags |= OPT_PASSIVE;
+ if (wo->silent)
+ f->flags |= OPT_SILENT;
+ fsm_open(f);
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void
+lcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (phase != PHASE_DEAD)
+ new_phase(PHASE_TERMINATE);
+ if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
+ /*
+ * This action is not strictly according to the FSM in RFC1548,
+ * but it does mean that the program terminates if you do a
+ * lcp_close() in passive/silent mode when a connection hasn't
+ * been established.
+ */
+ f->state = CLOSED;
+ lcp_finished(f);
+
+ } else
+ fsm_close(&lcp_fsm[unit], reason);
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void
+lcp_lowerup(unit)
+ int unit;
+{
+ lcp_options *wo = &lcp_wantoptions[unit];
+ fsm *f = &lcp_fsm[unit];
+
+ /*
+ * Don't use A/C or protocol compression on transmission,
+ * but accept A/C and protocol compressed packets
+ * if we are going to ask for A/C and protocol compression.
+ */
+ ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0);
+ ppp_recv_config(unit, PPP_MRU, (lax_recv? 0: 0xffffffff),
+ wo->neg_pcompression, wo->neg_accompression);
+ peer_mru[unit] = PPP_MRU;
+
+ if (listen_time != 0) {
+ f->flags |= DELAYED_UP;
+ timeout(lcp_delayed_up, f, 0, listen_time * 1000);
+ } else
+ fsm_lowerup(f);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void
+lcp_lowerdown(unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (f->flags & DELAYED_UP)
+ f->flags &= ~DELAYED_UP;
+ else
+ fsm_lowerdown(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_delayed_up - Bring the lower layer up now.
+ */
+static void
+lcp_delayed_up(arg)
+ void *arg;
+{
+ fsm *f = arg;
+
+ if (f->flags & DELAYED_UP) {
+ f->flags &= ~DELAYED_UP;
+ fsm_lowerup(f);
+ }
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void
+lcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (f->flags & DELAYED_UP) {
+ f->flags &= ~DELAYED_UP;
+ fsm_lowerup(f);
+ }
+ fsm_input(f, p, len);
+}
+
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int
+lcp_extcode(f, code, id, inp, len)
+ fsm *f;
+ int code, id;
+ u_char *inp;
+ int len;
+{
+ u_char *magp;
+
+ switch( code ){
+ case PROTREJ:
+ lcp_rprotrej(f, inp, len);
+ break;
+
+ case ECHOREQ:
+ if (f->state != OPENED)
+ break;
+ magp = inp;
+ PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
+ fsm_sdata(f, ECHOREP, id, inp, len);
+ break;
+
+ case ECHOREP:
+ lcp_received_echo_reply(f, id, inp, len);
+ break;
+
+ case DISCREQ:
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * lcp_rprotrej - Receive an Protocol-Reject.
+ *
+ * Figure out which protocol is rejected and inform it.
+ */
+static void
+lcp_rprotrej(f, inp, len)
+ fsm *f;
+ u_char *inp;
+ int len;
+{
+ int i;
+ struct protent *protp;
+ u_short prot;
+
+ if (len < 2) {
+ LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
+ return;
+ }
+
+ GETSHORT(prot, inp);
+
+ /*
+ * Protocol-Reject packets received in any state other than the LCP
+ * OPENED state SHOULD be silently discarded.
+ */
+ if( f->state != OPENED ){
+ LCPDEBUG(("Protocol-Reject discarded: LCP in state %d", f->state));
+ return;
+ }
+
+ /*
+ * Upcall the proper Protocol-Reject routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol == prot && protp->enabled_flag) {
+ (*protp->protrej)(f->unit);
+ return;
+ }
+
+ warn("Protocol-Reject for unsupported protocol 0x%x", prot);
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+/*ARGSUSED*/
+static void
+lcp_protrej(unit)
+ int unit;
+{
+ /*
+ * Can't reject LCP!
+ */
+ error("Received Protocol-Reject for LCP!");
+ fsm_protreject(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void
+lcp_sprotrej(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ /*
+ * Send back the protocol and the information field of the
+ * rejected packet. We only get here if LCP is in the OPENED state.
+ */
+ p += 2;
+ len -= 2;
+
+ fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id,
+ p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void
+lcp_resetci(f)
+ fsm *f;
+{
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+
+ wo->magicnumber = magic();
+ wo->numloops = 0;
+ *go = *wo;
+ if (!multilink) {
+ go->neg_mrru = 0;
+ go->neg_ssnhf = 0;
+ go->neg_endpoint = 0;
+ }
+ if (noendpoint)
+ ao->neg_endpoint = 0;
+ peer_mru[f->unit] = PPP_MRU;
+ auth_reset(f->unit);
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int
+lcp_cilen(f)
+ fsm *f;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0)
+#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0)
+#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0)
+#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0)
+#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0)
+#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
+ /*
+ * NB: we only ask for one of CHAP and UPAP, even if we will
+ * accept either.
+ */
+ return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) +
+ LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) +
+ LENCICHAP(go->neg_chap) +
+ LENCISHORT(!go->neg_chap && go->neg_upap) +
+ LENCILQR(go->neg_lqr) +
+ LENCICBCP(go->neg_cbcp) +
+ LENCILONG(go->neg_magicnumber) +
+ LENCIVOID(go->neg_pcompression) +
+ LENCIVOID(go->neg_accompression) +
+ LENCISHORT(go->neg_mrru) +
+ LENCIVOID(go->neg_ssnhf) +
+ (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void
+lcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_VOID, ucp); \
+ }
+#define ADDCISHORT(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_SHORT, ucp); \
+ PUTSHORT(val, ucp); \
+ }
+#define ADDCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAP, ucp); \
+ PUTSHORT(val, ucp); \
+ PUTCHAR(digest, ucp); \
+ }
+#define ADDCILONG(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LONG, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCILQR(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LQR, ucp); \
+ PUTSHORT(PPP_LQR, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCICHAR(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR, ucp); \
+ PUTCHAR(val, ucp); \
+ }
+#define ADDCIENDP(opt, neg, class, val, len) \
+ if (neg) { \
+ int i; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR + len, ucp); \
+ PUTCHAR(class, ucp); \
+ for (i = 0; i < len; ++i) \
+ PUTCHAR(val[i], ucp); \
+ }
+
+ ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
+ ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+ go->asyncmap);
+ ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+ ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+ ADDCIVOID(CI_SSNHF, go->neg_ssnhf);
+ ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+ go->endpoint.value, go->endpoint.length);
+
+ if (ucp - start_ucp != *lenp) {
+ /* this should never happen, because peer_mtu should be 1500 */
+ error("Bug in lcp_addci: wrong length");
+ }
+}
+
+
+/*
+ * lcp_ackci - Ack our CIs.
+ * This should not modify any state if the Ack is bad.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+lcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cilen, citype, cichar;
+ u_short cishort;
+ u_int32_t cilong;
+
+ /*
+ * CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define ACKCIVOID(opt, neg) \
+ if (neg) { \
+ if ((len -= CILEN_VOID) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || \
+ citype != opt) \
+ goto bad; \
+ }
+#define ACKCISHORT(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_SHORT) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_SHORT || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+#define ACKCICHAR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_CHAR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAR || \
+ citype != opt) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != val) \
+ goto bad; \
+ }
+#define ACKCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ if ((len -= CILEN_CHAP) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAP || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != digest) \
+ goto bad; \
+ }
+#define ACKCILONG(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LONG) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LONG || \
+ citype != opt) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+#define ACKCILQR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LQR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LQR || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != PPP_LQR) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+#define ACKCIENDP(opt, neg, class, val, vlen) \
+ if (neg) { \
+ int i; \
+ if ((len -= CILEN_CHAR + vlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAR + vlen || \
+ citype != opt) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != class) \
+ goto bad; \
+ for (i = 0; i < vlen; ++i) { \
+ GETCHAR(cichar, p); \
+ if (cichar != val[i]) \
+ goto bad; \
+ } \
+ }
+
+ ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
+ ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+ go->asyncmap);
+ ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+ ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+ ACKCIVOID(CI_SSNHF, go->neg_ssnhf);
+ ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+ go->endpoint.value, go->endpoint.length);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+bad:
+ LCPDEBUG(("lcp_acki: received bad Ack!"));
+ return (0);
+}
+
+
+/*
+ * lcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+lcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ u_char citype, cichar, *next;
+ u_short cishort;
+ u_int32_t cilong;
+ lcp_options no; /* options we've seen Naks for */
+ lcp_options try; /* options to request next time */
+ int looped_back = 0;
+ int cilen;
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIVOID(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ no.neg = 1; \
+ try.neg = 0; \
+ }
+#define NAKCICHAP(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCICHAR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[1] == CILEN_CHAR && \
+ p[0] == opt) { \
+ len -= CILEN_CHAR; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCISHORT(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILONG(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILQR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCIENDP(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[0] == opt && \
+ p[1] >= CILEN_CHAR && \
+ p[1] <= len) { \
+ len -= p[1]; \
+ INCPTR(p[1], p); \
+ no.neg = 1; \
+ try.neg = 0; \
+ }
+
+ /*
+ * We don't care if they want to send us smaller packets than
+ * we want. Therefore, accept any MRU less than what we asked for,
+ * but then ignore the new value when setting the MRU in the kernel.
+ * If they send us a bigger MRU than what we asked, accept it, up to
+ * the limit of the default MRU we'd get if we didn't negotiate.
+ */
+ if (go->neg_mru && go->mru != DEFMRU) {
+ NAKCISHORT(CI_MRU, neg_mru,
+ if (cishort <= wo->mru || cishort <= DEFMRU)
+ try.mru = cishort;
+ );
+ }
+
+ /*
+ * Add any characters they want to our (receive-side) asyncmap.
+ */
+ if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) {
+ NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
+ try.asyncmap = go->asyncmap | cilong;
+ );
+ }
+
+ /*
+ * If they've nak'd our authentication-protocol, check whether
+ * they are proposing a different protocol, or a different
+ * hash algorithm for CHAP.
+ */
+ if ((go->neg_chap || go->neg_upap)
+ && len >= CILEN_SHORT
+ && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
+ cilen = p[1];
+ len -= cilen;
+ no.neg_chap = go->neg_chap;
+ no.neg_upap = go->neg_upap;
+ INCPTR(2, p);
+ GETSHORT(cishort, p);
+ if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+ /*
+ * If we were asking for CHAP, they obviously don't want to do it.
+ * If we weren't asking for CHAP, then we were asking for PAP,
+ * in which case this Nak is bad.
+ */
+ if (!go->neg_chap)
+ goto bad;
+ try.neg_chap = 0;
+
+ } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
+ GETCHAR(cichar, p);
+ if (go->neg_chap) {
+ /*
+ * We were asking for CHAP/MD5; they must want a different
+ * algorithm. If they can't do MD5, we can ask for M$-CHAP
+ * if we support it, otherwise we'll have to stop
+ * asking for CHAP.
+ */
+ if (cichar != go->chap_mdtype) {
+#ifdef CHAPMS
+ if (cichar == CHAP_MICROSOFT)
+ go->chap_mdtype = CHAP_MICROSOFT;
+ else
+#endif /* CHAPMS */
+ try.neg_chap = 0;
+ }
+ } else {
+ /*
+ * Stop asking for PAP if we were asking for it.
+ */
+ try.neg_upap = 0;
+ }
+
+ } else {
+ /*
+ * We don't recognize what they're suggesting.
+ * Stop asking for what we were asking for.
+ */
+ if (go->neg_chap)
+ try.neg_chap = 0;
+ else
+ try.neg_upap = 0;
+ p += cilen - CILEN_SHORT;
+ }
+ }
+
+ /*
+ * If they can't cope with our link quality protocol, we'll have
+ * to stop asking for LQR. We haven't got any other protocol.
+ * If they Nak the reporting period, take their value XXX ?
+ */
+ NAKCILQR(CI_QUALITY, neg_lqr,
+ if (cishort != PPP_LQR)
+ try.neg_lqr = 0;
+ else
+ try.lqr_period = cilong;
+ );
+
+ /*
+ * Only implementing CBCP...not the rest of the callback options
+ */
+ NAKCICHAR(CI_CALLBACK, neg_cbcp,
+ try.neg_cbcp = 0;
+ );
+
+ /*
+ * Check for a looped-back line.
+ */
+ NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
+ try.magicnumber = magic();
+ looped_back = 1;
+ );
+
+ /*
+ * Peer shouldn't send Nak for protocol compression or
+ * address/control compression requests; they should send
+ * a Reject instead. If they send a Nak, treat it as a Reject.
+ */
+ NAKCIVOID(CI_PCOMPRESSION, neg_pcompression);
+ NAKCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+ /*
+ * Nak for MRRU option - accept their value if it is smaller
+ * than the one we want.
+ */
+ if (go->neg_mrru) {
+ NAKCISHORT(CI_MRRU, neg_mrru,
+ if (cishort <= wo->mrru)
+ try.mrru = cishort;
+ );
+ }
+
+ /*
+ * Nak for short sequence numbers shouldn't be sent, treat it
+ * like a reject.
+ */
+ NAKCIVOID(CI_SSNHF, neg_ssnhf);
+
+ /*
+ * Nak of the endpoint discriminator option is not permitted,
+ * treat it like a reject.
+ */
+ NAKCIENDP(CI_EPDISC, neg_endpoint);
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If we see an option that we requested, or one we've already seen
+ * in this packet, then this packet is bad.
+ * If we wanted to respond by starting to negotiate on the requested
+ * option(s), we could, but we don't, because except for the
+ * authentication type and quality protocol, if we are not negotiating
+ * an option, it is because we were told not to.
+ * For the authentication type, the Nak from the peer means
+ * `let me authenticate myself with you' which is a bit pointless.
+ * For the quality protocol, the Nak means `ask me to send you quality
+ * reports', but if we didn't ask for them, we don't want them.
+ * An option we don't recognize represents the peer asking to
+ * negotiate some option we don't support, so ignore it.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if (cilen < CILEN_VOID || (len -= cilen) < 0)
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_MRU:
+ if ((go->neg_mru && go->mru != DEFMRU)
+ || no.neg_mru || cilen != CILEN_SHORT)
+ goto bad;
+ GETSHORT(cishort, p);
+ if (cishort < DEFMRU) {
+ try.neg_mru = 1;
+ try.mru = cishort;
+ }
+ break;
+ case CI_ASYNCMAP:
+ if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF)
+ || no.neg_asyncmap || cilen != CILEN_LONG)
+ goto bad;
+ break;
+ case CI_AUTHTYPE:
+ if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap)
+ goto bad;
+ break;
+ case CI_MAGICNUMBER:
+ if (go->neg_magicnumber || no.neg_magicnumber ||
+ cilen != CILEN_LONG)
+ goto bad;
+ break;
+ case CI_PCOMPRESSION:
+ if (go->neg_pcompression || no.neg_pcompression
+ || cilen != CILEN_VOID)
+ goto bad;
+ break;
+ case CI_ACCOMPRESSION:
+ if (go->neg_accompression || no.neg_accompression
+ || cilen != CILEN_VOID)
+ goto bad;
+ break;
+ case CI_QUALITY:
+ if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
+ goto bad;
+ break;
+ case CI_MRRU:
+ if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT)
+ goto bad;
+ break;
+ case CI_SSNHF:
+ if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID)
+ goto bad;
+ try.neg_ssnhf = 1;
+ break;
+ case CI_EPDISC:
+ if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR)
+ goto bad;
+ break;
+ }
+ p = next;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ * If there are any options left we ignore them.
+ */
+ if (f->state != OPENED) {
+ if (looped_back) {
+ if (++try.numloops >= lcp_loopbackfail) {
+ notice("Serial line is looped back.");
+ lcp_close(f->unit, "Loopback detected");
+ status = EXIT_LOOPBACK;
+ }
+ } else
+ try.numloops = 0;
+ *go = try;
+ }
+
+ return 1;
+
+bad:
+ LCPDEBUG(("lcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * lcp_rejci - Peer has Rejected some of our CIs.
+ * This should not modify any state if the Reject is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Reject was bad.
+ * 1 - Reject was good.
+ */
+static int
+lcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cichar;
+ u_short cishort;
+ u_int32_t cilong;
+ lcp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIVOID(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ try.neg = 0; \
+ }
+#define REJCISHORT(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCICHAP(opt, neg, val, digest) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cishort != val || cichar != digest) \
+ goto bad; \
+ try.neg = 0; \
+ try.neg_upap = 0; \
+ }
+#define REJCILONG(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cilong != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCILQR(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cishort != PPP_LQR || cilong != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCICBCP(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_CBCP && \
+ p[1] == CILEN_CBCP && \
+ p[0] == opt) { \
+ len -= CILEN_CBCP; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cichar != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCIENDP(opt, neg, class, val, vlen) \
+ if (go->neg && \
+ len >= CILEN_CHAR + vlen && \
+ p[0] == opt && \
+ p[1] == CILEN_CHAR + vlen) { \
+ int i; \
+ len -= CILEN_CHAR + vlen; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ if (cichar != class) \
+ goto bad; \
+ for (i = 0; i < vlen; ++i) { \
+ GETCHAR(cichar, p); \
+ if (cichar != val[i]) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+ REJCISHORT(CI_MRU, neg_mru, go->mru);
+ REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+ REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype);
+ if (!go->neg_chap) {
+ REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+ }
+ REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+ REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
+ REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+ REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+ REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+ REJCISHORT(CI_MRRU, neg_mrru, go->mrru);
+ REJCIVOID(CI_SSNHF, neg_ssnhf);
+ REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class,
+ go->endpoint.value, go->endpoint.length);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ LCPDEBUG(("lcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * lcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+lcp_reqci(f, inp, lenp, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *lenp; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ int cilen, citype, cichar; /* Parsed len, type, char value */
+ u_short cishort; /* Parsed short value */
+ u_int32_t cilong; /* Parse long value */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *rejp; /* Pointer to next char in reject frame */
+ u_char *nakp; /* Pointer to next char in Nak frame */
+ int l = *lenp; /* Length left */
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ nakp = nak_buffer;
+ rejp = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ LCPDEBUG(("lcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ citype = 0;
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_MRU:
+ if (!ao->neg_mru || /* Allow option? */
+ cilen != CILEN_SHORT) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETSHORT(cishort, p); /* Parse MRU */
+
+ /*
+ * He must be able to receive at least our minimum.
+ * No need to check a maximum. If he sends a large number,
+ * we'll just ignore it.
+ */
+ if (cishort < MINMRU) {
+ orc = CONFNAK; /* Nak CI */
+ PUTCHAR(CI_MRU, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(MINMRU, nakp); /* Give him a hint */
+ break;
+ }
+ ho->neg_mru = 1; /* Remember he sent MRU */
+ ho->mru = cishort; /* And remember value */
+ break;
+
+ case CI_ASYNCMAP:
+ if (!ao->neg_asyncmap ||
+ cilen != CILEN_LONG) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+
+ /*
+ * Asyncmap must have set at least the bits
+ * which are set in lcp_allowoptions[unit].asyncmap.
+ */
+ if ((ao->asyncmap & ~cilong) != 0) {
+ orc = CONFNAK;
+ PUTCHAR(CI_ASYNCMAP, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(ao->asyncmap | cilong, nakp);
+ break;
+ }
+ ho->neg_asyncmap = 1;
+ ho->asyncmap = cilong;
+ break;
+
+ case CI_AUTHTYPE:
+ if (cilen < CILEN_SHORT ||
+ !(ao->neg_upap || ao->neg_chap)) {
+ /*
+ * Reject the option if we're not willing to authenticate.
+ */
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ /*
+ * Authtype must be PAP or CHAP.
+ *
+ * Note: if both ao->neg_upap and ao->neg_chap are set,
+ * and the peer sends a Configure-Request with two
+ * authenticate-protocol requests, one for CHAP and one
+ * for UPAP, then we will reject the second request.
+ * Whether we end up doing CHAP or UPAP depends then on
+ * the ordering of the CIs in the peer's Configure-Request.
+ */
+
+ if (cishort == PPP_PAP) {
+ if (ho->neg_chap || /* we've already accepted CHAP */
+ cilen != CILEN_SHORT) {
+ LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_upap) { /* we don't want to do PAP */
+ orc = CONFNAK; /* NAK it and suggest CHAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ /* XXX if we can do CHAP_MICROSOFT as well, we should
+ probably put in another option saying so */
+ break;
+ }
+ ho->neg_upap = 1;
+ break;
+ }
+ if (cishort == PPP_CHAP) {
+ if (ho->neg_upap || /* we've already accepted PAP */
+ cilen != CILEN_CHAP) {
+ LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_chap) { /* we don't want to do CHAP */
+ orc = CONFNAK; /* NAK it and suggest PAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ break;
+ }
+ GETCHAR(cichar, p); /* get digest type*/
+ if (cichar != CHAP_DIGEST_MD5
+#ifdef CHAPMS
+ && cichar != CHAP_MICROSOFT
+#endif
+ ) {
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ break;
+ }
+ ho->chap_mdtype = cichar; /* save md type */
+ ho->neg_chap = 1;
+ break;
+ }
+
+ /*
+ * We don't recognize the protocol they're asking for.
+ * Nak it with something we're willing to do.
+ * (At this point we know ao->neg_upap || ao->neg_chap.)
+ */
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ if (ao->neg_chap) {
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ } else {
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ }
+ break;
+
+ case CI_QUALITY:
+ if (!ao->neg_lqr ||
+ cilen != CILEN_LQR) {
+ orc = CONFREJ;
+ break;
+ }
+
+ GETSHORT(cishort, p);
+ GETLONG(cilong, p);
+
+ /*
+ * Check the protocol and the reporting period.
+ * XXX When should we Nak this, and what with?
+ */
+ if (cishort != PPP_LQR) {
+ orc = CONFNAK;
+ PUTCHAR(CI_QUALITY, nakp);
+ PUTCHAR(CILEN_LQR, nakp);
+ PUTSHORT(PPP_LQR, nakp);
+ PUTLONG(ao->lqr_period, nakp);
+ break;
+ }
+ break;
+
+ case CI_MAGICNUMBER:
+ if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
+ cilen != CILEN_LONG) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+
+ /*
+ * He must have a different magic number.
+ */
+ if (go->neg_magicnumber &&
+ cilong == go->magicnumber) {
+ cilong = magic(); /* Don't put magic() inside macro! */
+ orc = CONFNAK;
+ PUTCHAR(CI_MAGICNUMBER, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(cilong, nakp);
+ break;
+ }
+ ho->neg_magicnumber = 1;
+ ho->magicnumber = cilong;
+ break;
+
+
+ case CI_PCOMPRESSION:
+ if (!ao->neg_pcompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_pcompression = 1;
+ break;
+
+ case CI_ACCOMPRESSION:
+ if (!ao->neg_accompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_accompression = 1;
+ break;
+
+ case CI_MRRU:
+ if (!ao->neg_mrru || !multilink ||
+ cilen != CILEN_SHORT) {
+ orc = CONFREJ;
+ break;
+ }
+
+ GETSHORT(cishort, p);
+ /* possibly should insist on a minimum/maximum MRRU here */
+ ho->neg_mrru = 1;
+ ho->mrru = cishort;
+ break;
+
+ case CI_SSNHF:
+ if (!ao->neg_ssnhf || !multilink ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_ssnhf = 1;
+ break;
+
+ case CI_EPDISC:
+ if (!ao->neg_endpoint ||
+ cilen < CILEN_CHAR ||
+ cilen > CILEN_CHAR + MAX_ENDP_LEN) {
+ orc = CONFREJ;
+ break;
+ }
+ GETCHAR(cichar, p);
+ cilen -= CILEN_CHAR;
+ ho->neg_endpoint = 1;
+ ho->endpoint.class = cichar;
+ ho->endpoint.length = cilen;
+ BCOPY(p, ho->endpoint.value, cilen);
+ INCPTR(cilen, p);
+ break;
+
+ default:
+ LCPDEBUG(("lcp_reqci: rcvd unknown option %d", citype));
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree /* Getting fed up with sending NAKs? */
+ && citype != CI_MAGICNUMBER) {
+ orc = CONFREJ; /* Get tough if so */
+ } else {
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ rc = CONFNAK;
+ }
+ }
+ if (orc == CONFREJ) { /* Reject this CI */
+ rc = CONFREJ;
+ if (cip != rejp) /* Need to move rejected CI? */
+ BCOPY(cip, rejp, cilen); /* Move it */
+ INCPTR(cilen, rejp); /* Update output pointer */
+ }
+ }
+
+ /*
+ * If we wanted to send additional NAKs (for unsent CIs), the
+ * code would go here. The extra NAKs would go at *nakp.
+ * At present there are no cases where we want to ask the
+ * peer to negotiate an option.
+ */
+
+ switch (rc) {
+ case CONFACK:
+ *lenp = next - inp;
+ break;
+ case CONFNAK:
+ /*
+ * Copy the Nak'd options from the nak_buffer to the caller's buffer.
+ */
+ *lenp = nakp - nak_buffer;
+ BCOPY(nak_buffer, inp, *lenp);
+ break;
+ case CONFREJ:
+ *lenp = rejp - inp;
+ break;
+ }
+
+ LCPDEBUG(("lcp_reqci: returning CONF%s.", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ */
+static void
+lcp_up(f)
+ fsm *f;
+{
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+ int mtu;
+
+ if (!go->neg_magicnumber)
+ go->magicnumber = 0;
+ if (!ho->neg_magicnumber)
+ ho->magicnumber = 0;
+
+ /*
+ * Set our MTU to the smaller of the MTU we wanted and
+ * the MRU our peer wanted. If we negotiated an MRU,
+ * set our MRU to the larger of value we wanted and
+ * the value we got in the negotiation.
+ * Note on the MTU: the link MTU can be the MRU the peer wanted,
+ * the interface MTU is set to the lower of that and the
+ * MTU we want to use.
+ */
+ mtu = ho->neg_mru? ho->mru: PPP_MRU;
+#ifdef HAVE_MULTILINK
+ if (!(multilink && go->neg_mrru && ho->neg_mrru))
+#endif /* HAVE_MULTILINK */
+ netif_set_mtu(f->unit, MIN(mtu, ao->mru));
+ ppp_send_config(f->unit, mtu,
+ (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
+ ho->neg_pcompression, ho->neg_accompression);
+ ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU),
+ (lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff),
+ go->neg_pcompression, go->neg_accompression);
+
+ if (ho->neg_mru)
+ peer_mru[f->unit] = ho->mru;
+
+ lcp_echo_lowerup(f->unit); /* Enable echo messages */
+
+ link_established(f->unit);
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void
+lcp_down(f)
+ fsm *f;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+ lcp_echo_lowerdown(f->unit);
+
+ link_down(f->unit);
+
+ ppp_send_config(f->unit, PPP_MRU, 0xffffffff, 0, 0);
+ ppp_recv_config(f->unit, PPP_MRU,
+ (go->neg_asyncmap? go->asyncmap: 0xffffffff),
+ go->neg_pcompression, go->neg_accompression);
+ peer_mru[f->unit] = PPP_MRU;
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void
+lcp_starting(f)
+ fsm *f;
+{
+ link_required(f->unit);
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void
+lcp_finished(f)
+ fsm *f;
+{
+ link_terminated(f->unit);
+}
+
+
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+static char *lcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej", "ProtRej",
+ "EchoReq", "EchoRep", "DiscReq"
+};
+
+static int
+lcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len, olen, i;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *))
+ printer(arg, " %s", lcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_MRU:
+ if (olen == CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "mru %d", cishort);
+ }
+ break;
+ case CI_ASYNCMAP:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "asyncmap 0x%x", cilong);
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "auth ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_PAP:
+ printer(arg, "pap");
+ break;
+ case PPP_CHAP:
+ printer(arg, "chap");
+ if (p < optend) {
+ switch (*p) {
+ case CHAP_DIGEST_MD5:
+ printer(arg, " MD5");
+ ++p;
+ break;
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ printer(arg, " m$oft");
+ ++p;
+ break;
+#endif
+ }
+ }
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_QUALITY:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "quality ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_LQR:
+ printer(arg, "lqr");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_CALLBACK:
+ if (olen >= CILEN_CHAR) {
+ p += 2;
+ printer(arg, "callback ");
+ GETCHAR(cishort, p);
+ switch (cishort) {
+ case CBCP_OPT:
+ printer(arg, "CBCP");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_MAGICNUMBER:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "magic 0x%x", cilong);
+ }
+ break;
+ case CI_PCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "pcomp");
+ }
+ break;
+ case CI_ACCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "accomp");
+ }
+ break;
+ case CI_MRRU:
+ if (olen == CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "mrru %d", cishort);
+ }
+ break;
+ case CI_SSNHF:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "ssnhf");
+ }
+ break;
+ case CI_EPDISC:
+#ifdef HAVE_MULTILINK
+ if (olen >= CILEN_CHAR) {
+ struct epdisc epd;
+ p += 2;
+ GETCHAR(epd.class, p);
+ epd.length = olen - CILEN_CHAR;
+ if (epd.length > MAX_ENDP_LEN)
+ epd.length = MAX_ENDP_LEN;
+ if (epd.length > 0) {
+ BCOPY(p, epd.value, epd.length);
+ p += epd.length;
+ }
+ printer(arg, "endpoint [%s]", epdisc_to_str(&epd));
+ }
+#else
+ printer(arg, "endpoint");
+#endif
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+
+ case ECHOREQ:
+ case ECHOREP:
+ case DISCREQ:
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " magic=0x%x", cilong);
+ p += 4;
+ len -= 4;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (i = 0; i < len && i < 32; ++i) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ if (i < len) {
+ printer(arg, " ...");
+ p += len - i;
+ }
+
+ return p - pstart;
+}
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+
+static
+void LcpLinkFailure (f)
+ fsm *f;
+{
+ if (f->state == OPENED) {
+ info("No response to %d echo-requests", lcp_echos_pending);
+ notice("Serial link appears to be disconnected.");
+ lcp_close(f->unit, "Peer not responding");
+ status = EXIT_PEER_DEAD;
+ }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+
+static void
+LcpEchoCheck (f)
+ fsm *f;
+{
+ LcpSendEchoRequest (f);
+ if (f->state != OPENED)
+ return;
+
+ /*
+ * Start the timer for the next interval.
+ */
+ if (lcp_echo_timer_running)
+ warn("assertion lcp_echo_timer_running==0 failed");
+ TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval);
+ lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+
+static void
+LcpEchoTimeout (arg)
+ void *arg;
+{
+ if (lcp_echo_timer_running != 0) {
+ lcp_echo_timer_running = 0;
+ LcpEchoCheck ((fsm *) arg);
+ }
+}
+
+/*
+ * LcpEchoReply - LCP has received a reply to the echo
+ */
+
+static void
+lcp_received_echo_reply (f, id, inp, len)
+ fsm *f;
+ int id;
+ u_char *inp;
+ int len;
+{
+ u_int32_t magic;
+
+ /* Check the magic number - don't count replies from ourselves. */
+ if (len < 4) {
+ dbglog("lcp: received short Echo-Reply, length %d", len);
+ return;
+ }
+ GETLONG(magic, inp);
+ if (lcp_gotoptions[f->unit].neg_magicnumber
+ && magic == lcp_gotoptions[f->unit].magicnumber) {
+ warn("appear to have received our own echo-reply!");
+ return;
+ }
+
+ /* Reset the number of outstanding echo frames */
+ lcp_echos_pending = 0;
+}
+
+/*
+ * LcpSendEchoRequest - Send an echo request frame to the peer
+ */
+
+static void
+LcpSendEchoRequest (f)
+ fsm *f;
+{
+ u_int32_t lcp_magic;
+ u_char pkt[4], *pktp;
+
+ /*
+ * Detect the failure of the peer at this point.
+ */
+ if (lcp_echo_fails != 0) {
+ if (lcp_echos_pending >= lcp_echo_fails) {
+ LcpLinkFailure(f);
+ lcp_echos_pending = 0;
+ }
+ }
+
+ /*
+ * Make and send the echo request frame.
+ */
+ if (f->state == OPENED) {
+ lcp_magic = lcp_gotoptions[f->unit].magicnumber;
+ pktp = pkt;
+ PUTLONG(lcp_magic, pktp);
+ fsm_sdata(f, ECHOREQ, lcp_echo_number++ & 0xFF, pkt, pktp - pkt);
+ ++lcp_echos_pending;
+ }
+}
+
+/*
+ * lcp_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerup (unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ /* Clear the parameters for generating echo frames */
+ lcp_echos_pending = 0;
+ lcp_echo_number = 0;
+ lcp_echo_timer_running = 0;
+
+ /* If a timeout interval is specified then start the timer */
+ if (lcp_echo_interval != 0)
+ LcpEchoCheck (f);
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerdown (unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ lcp_echo_timer_running = 0;
+ }
+}
diff --git a/mdk-stage1/ppp/pppd/lcp.h b/mdk-stage1/ppp/pppd/lcp.h
new file mode 100644
index 000000000..b87a9295e
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/lcp.h
@@ -0,0 +1,95 @@
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Options.
+ */
+#define CI_MRU 1 /* Maximum Receive Unit */
+#define CI_ASYNCMAP 2 /* Async Control Character Map */
+#define CI_AUTHTYPE 3 /* Authentication Type */
+#define CI_QUALITY 4 /* Quality Protocol */
+#define CI_MAGICNUMBER 5 /* Magic Number */
+#define CI_PCOMPRESSION 7 /* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */
+#define CI_CALLBACK 13 /* callback */
+#define CI_MRRU 17 /* max reconstructed receive unit; multilink */
+#define CI_SSNHF 18 /* short sequence numbers for multilink */
+#define CI_EPDISC 19 /* endpoint discriminator */
+
+/*
+ * LCP-specific packet types.
+ */
+#define PROTREJ 8 /* Protocol Reject */
+#define ECHOREQ 9 /* Echo Request */
+#define ECHOREP 10 /* Echo Reply */
+#define DISCREQ 11 /* Discard Request */
+#define CBCP_OPT 6 /* Use callback control protocol */
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+ bool passive; /* Don't die if we don't get a response */
+ bool silent; /* Wait for the other end to start first */
+ bool restart; /* Restart vs. exit after close */
+ bool neg_mru; /* Negotiate the MRU? */
+ bool neg_asyncmap; /* Negotiate the async map? */
+ bool neg_upap; /* Ask for UPAP authentication? */
+ bool neg_chap; /* Ask for CHAP authentication? */
+ bool neg_magicnumber; /* Ask for magic number? */
+ bool neg_pcompression; /* HDLC Protocol Field Compression? */
+ bool neg_accompression; /* HDLC Address/Control Field Compression? */
+ bool neg_lqr; /* Negotiate use of Link Quality Reports */
+ bool neg_cbcp; /* Negotiate use of CBCP */
+ bool neg_mrru; /* negotiate multilink MRRU */
+ bool neg_ssnhf; /* negotiate short sequence numbers */
+ bool neg_endpoint; /* negotiate endpoint discriminator */
+ int mru; /* Value of MRU */
+ int mrru; /* Value of MRRU, and multilink enable */
+ u_char chap_mdtype; /* which MD type (hashing algorithm) */
+ u_int32_t asyncmap; /* Value of async map */
+ u_int32_t magicnumber;
+ int numloops; /* Number of loops during magic number neg. */
+ u_int32_t lqr_period; /* Reporting period for LQR 1/100ths second */
+ struct epdisc endpoint; /* endpoint discriminator */
+} lcp_options;
+
+extern fsm lcp_fsm[];
+extern lcp_options lcp_wantoptions[];
+extern lcp_options lcp_gotoptions[];
+extern lcp_options lcp_allowoptions[];
+extern lcp_options lcp_hisoptions[];
+
+#define DEFMRU 1500 /* Try for this */
+#define MINMRU 128 /* No MRUs below this */
+#define MAXMRU 16384 /* Normally limit MRU to this */
+
+void lcp_open __P((int));
+void lcp_close __P((int, char *));
+void lcp_lowerup __P((int));
+void lcp_lowerdown __P((int));
+void lcp_sprotrej __P((int, u_char *, int)); /* send protocol reject */
+
+extern struct protent lcp_protent;
+
+/* Default number of times we receive our magic number from the peer
+ before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL 10
diff --git a/mdk-stage1/ppp/pppd/magic.c b/mdk-stage1/ppp/pppd/magic.c
new file mode 100644
index 000000000..764692a16
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/magic.c
@@ -0,0 +1,87 @@
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "pppd.h"
+#include "magic.h"
+
+static const char rcsid[] = RCSID;
+
+extern long mrand48 __P((void));
+extern void srand48 __P((long));
+
+/*
+ * magic_init - Initialize the magic number generator.
+ *
+ * Attempts to compute a random number seed which will not repeat.
+ * The current method uses the current hostid, current process ID
+ * and current time, currently.
+ */
+void
+magic_init()
+{
+ long seed;
+ struct timeval t;
+
+ gettimeofday(&t, NULL);
+ seed = get_host_seed() ^ t.tv_sec ^ t.tv_usec ^ getpid();
+ srand48(seed);
+}
+
+/*
+ * magic - Returns the next magic number.
+ */
+u_int32_t
+magic()
+{
+ return (u_int32_t) mrand48();
+}
+
+#ifdef NO_DRAND48
+/*
+ * Substitute procedures for those systems which don't have
+ * drand48 et al.
+ */
+
+double
+drand48()
+{
+ return (double)random() / (double)0x7fffffffL; /* 2**31-1 */
+}
+
+long
+mrand48()
+{
+ return random();
+}
+
+void
+srand48(seedval)
+long seedval;
+{
+ srandom((int)seedval);
+}
+
+#endif
diff --git a/mdk-stage1/ppp/pppd/magic.h b/mdk-stage1/ppp/pppd/magic.h
new file mode 100644
index 000000000..1344626a3
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/magic.h
@@ -0,0 +1,23 @@
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+void magic_init __P((void)); /* Initialize the magic number generator */
+u_int32_t magic __P((void)); /* Returns the next magic number */
diff --git a/mdk-stage1/ppp/pppd/main.c b/mdk-stage1/ppp/pppd/main.c
new file mode 100644
index 000000000..8789d3b1d
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/main.c
@@ -0,0 +1,1831 @@
+/*
+ * main.c - Point-to-Point Protocol main module
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "magic.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#ifdef INET6
+#include "ipv6cp.h"
+#endif
+#include "upap.h"
+#include "chap.h"
+#include "ccp.h"
+#include "pathnames.h"
+#include "tdb.h"
+
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#endif /* IPX_CHANGE */
+#ifdef AT_CHANGE
+#include "atcp.h"
+#endif
+
+static const char rcsid[] = RCSID;
+
+/* interface vars */
+char ifname[32]; /* Interface name */
+int ifunit; /* Interface unit number */
+
+struct channel *the_channel;
+
+char *progname; /* Name of this program */
+char hostname[MAXNAMELEN]; /* Our hostname */
+static char pidfilename[MAXPATHLEN]; /* name of pid file */
+static char linkpidfile[MAXPATHLEN]; /* name of linkname pid file */
+char ppp_devnam[MAXPATHLEN]; /* name of PPP tty (maybe ttypx) */
+uid_t uid; /* Our real user-id */
+struct notifier *pidchange = NULL;
+struct notifier *phasechange = NULL;
+struct notifier *exitnotify = NULL;
+struct notifier *sigreceived = NULL;
+
+int hungup; /* terminal has been hung up */
+int privileged; /* we're running as real uid root */
+int need_holdoff; /* need holdoff period before restarting */
+int detached; /* have detached from terminal */
+volatile int status; /* exit status for pppd */
+int unsuccess; /* # unsuccessful connection attempts */
+int do_callback; /* != 0 if we should do callback next */
+int doing_callback; /* != 0 if we are doing callback */
+TDB_CONTEXT *pppdb; /* database for storing status etc. */
+char db_key[32];
+
+int (*holdoff_hook) __P((void)) = NULL;
+int (*new_phase_hook) __P((int)) = NULL;
+
+static int conn_running; /* we have a [dis]connector running */
+static int devfd; /* fd of underlying device */
+static int fd_ppp = -1; /* fd for talking PPP */
+static int fd_loop; /* fd for getting demand-dial packets */
+
+int phase; /* where the link is at */
+int kill_link;
+int open_ccp_flag;
+int listen_time;
+int got_sigusr2;
+int got_sigterm;
+int got_sighup;
+
+static int waiting;
+static sigjmp_buf sigjmp;
+
+char **script_env; /* Env. variable values for scripts */
+int s_env_nalloc; /* # words avail at script_env */
+
+u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */
+u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
+
+static int n_children; /* # child processes still running */
+static int got_sigchld; /* set if we have received a SIGCHLD */
+
+int privopen; /* don't lock, open device as root */
+
+char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
+
+GIDSET_TYPE groups[NGROUPS_MAX];/* groups the user is in */
+int ngroups; /* How many groups valid in groups */
+
+static struct timeval start_time; /* Time when link was started. */
+
+struct pppd_stats link_stats;
+int link_connect_time;
+int link_stats_valid;
+
+/*
+ * We maintain a list of child process pids and
+ * functions to call when they exit.
+ */
+struct subprocess {
+ pid_t pid;
+ char *prog;
+ void (*done) __P((void *));
+ void *arg;
+ struct subprocess *next;
+};
+
+static struct subprocess *children;
+
+/* Prototypes for procedures local to this file. */
+
+static void setup_signals __P((void));
+static void create_pidfile __P((void));
+static void create_linkpidfile __P((void));
+static void cleanup __P((void));
+static void get_input __P((void));
+static void calltimeout __P((void));
+static struct timeval *timeleft __P((struct timeval *));
+static void kill_my_pg __P((int));
+static void hup __P((int));
+static void term __P((int));
+static void chld __P((int));
+static void toggle_debug __P((int));
+static void open_ccp __P((int));
+static void bad_signal __P((int));
+static void holdoff_end __P((void *));
+static int reap_kids __P((int waitfor));
+static void update_db_entry __P((void));
+static void add_db_key __P((const char *));
+static void delete_db_key __P((const char *));
+static void cleanup_db __P((void));
+static void handle_events __P((void));
+
+extern char *ttyname __P((int));
+extern char *getlogin __P((void));
+int main __P((int, char *[]));
+
+#ifdef ultrix
+#undef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+#ifdef ULTRIX
+#define setlogmask(x)
+#endif
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+struct protent *protocols[] = {
+ &lcp_protent,
+ &pap_protent,
+ &chap_protent,
+#ifdef CBCP_SUPPORT
+ &cbcp_protent,
+#endif
+ &ipcp_protent,
+#ifdef INET6
+ &ipv6cp_protent,
+#endif
+ &ccp_protent,
+#ifdef IPX_CHANGE
+ &ipxcp_protent,
+#endif
+#ifdef AT_CHANGE
+ &atcp_protent,
+#endif
+ NULL
+};
+
+/*
+ * If PPP_DRV_NAME is not defined, use the default "ppp" as the device name.
+ */
+#if !defined(PPP_DRV_NAME)
+#define PPP_DRV_NAME "ppp"
+#endif /* !defined(PPP_DRV_NAME) */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, t;
+ char *p;
+ struct passwd *pw;
+ struct protent *protp;
+ char numbuf[16];
+
+ new_phase(PHASE_INITIALIZE);
+
+ /*
+ * Ensure that fds 0, 1, 2 are open, to /dev/null if nowhere else.
+ * This way we can close 0, 1, 2 in detach() without clobbering
+ * a fd that we are using.
+ */
+ if ((i = open("/dev/null", O_RDWR)) >= 0) {
+ while (0 <= i && i <= 2)
+ i = dup(i);
+ if (i >= 0)
+ close(i);
+ }
+
+ script_env = NULL;
+
+ /* Initialize syslog facilities */
+ reopen_log();
+
+ if (gethostname(hostname, MAXNAMELEN) < 0 ) {
+ option_error("Couldn't get hostname: %m");
+ exit(1);
+ }
+ hostname[MAXNAMELEN-1] = 0;
+
+ /* make sure we don't create world or group writable files. */
+ umask(umask(0777) | 022);
+
+ uid = getuid();
+ privileged = uid == 0;
+ slprintf(numbuf, sizeof(numbuf), "%d", uid);
+ script_setenv("ORIG_UID", numbuf, 0);
+
+ ngroups = getgroups(NGROUPS_MAX, groups);
+
+ /*
+ * Initialize magic number generator now so that protocols may
+ * use magic numbers in initialization.
+ */
+ magic_init();
+
+ /*
+ * Initialize each protocol.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ (*protp->init)(0);
+
+ /*
+ * Initialize the default channel.
+ */
+ tty_init();
+
+ progname = *argv;
+
+ /*
+ * Parse, in order, the system options file, the user's options file,
+ * and the command line arguments.
+ */
+ if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
+ || !options_from_user()
+ || !parse_args(argc-1, argv+1))
+ exit(EXIT_OPTION_ERROR);
+ devnam_fixed = 1; /* can no longer change device name */
+
+ /*
+ * Work out the device name, if it hasn't already been specified,
+ * and parse the tty's options file.
+ */
+ if (the_channel->process_extra_options)
+ (*the_channel->process_extra_options)();
+
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+
+ /*
+ * Check that we are running as root.
+ */
+ if (geteuid() != 0) {
+ option_error("must be root to run %s, since it is not setuid-root",
+ argv[0]);
+ exit(EXIT_NOT_ROOT);
+ }
+
+ if (!ppp_available()) {
+ option_error("%s", no_ppp_msg);
+ exit(EXIT_NO_KERNEL_SUPPORT);
+ }
+
+ /*
+ * Check that the options given are valid and consistent.
+ */
+ check_options();
+ if (!sys_check_options())
+ exit(EXIT_OPTION_ERROR);
+ auth_check_options();
+#ifdef HAVE_MULTILINK
+ mp_check_options();
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->check_options != NULL)
+ (*protp->check_options)();
+ if (the_channel->check_options)
+ (*the_channel->check_options)();
+
+
+ if (dump_options || dryrun) {
+ init_pr_log(NULL, LOG_INFO);
+ print_options(pr_log, NULL);
+ end_pr_log();
+ if (dryrun)
+ die(0);
+ }
+
+ /*
+ * Initialize system-dependent stuff.
+ */
+ sys_init();
+
+ pppdb = tdb_open(_PATH_PPPDB, 0, 0, O_RDWR|O_CREAT, 0644);
+ if (pppdb != NULL) {
+ slprintf(db_key, sizeof(db_key), "pppd%d", getpid());
+ update_db_entry();
+ } else {
+ warn("Warning: couldn't open ppp database %s", _PATH_PPPDB);
+ if (multilink) {
+ warn("Warning: disabling multilink");
+ multilink = 0;
+ }
+ }
+
+ /*
+ * Detach ourselves from the terminal, if required,
+ * and identify who is running us.
+ */
+ if (!nodetach && !updetach)
+ detach();
+ p = getlogin();
+ if (p == NULL) {
+ pw = getpwuid(uid);
+ if (pw != NULL && pw->pw_name != NULL)
+ p = pw->pw_name;
+ else
+ p = "(unknown)";
+ }
+ syslog(LOG_NOTICE, "pppd %s started by %s, uid %d", VERSION, p, uid);
+ script_setenv("PPPLOGNAME", p, 0);
+
+ if (devnam[0])
+ script_setenv("DEVICE", devnam, 1);
+ slprintf(numbuf, sizeof(numbuf), "%d", getpid());
+ script_setenv("PPPD_PID", numbuf, 1);
+
+ setup_signals();
+
+ waiting = 0;
+
+ create_linkpidfile();
+
+ /*
+ * If we're doing dial-on-demand, set up the interface now.
+ */
+ if (demand) {
+ /*
+ * Open the loopback channel and set it up to be the ppp interface.
+ */
+ tdb_writelock(pppdb);
+ fd_loop = open_ppp_loopback();
+ set_ifunit(1);
+ tdb_writeunlock(pppdb);
+
+ /*
+ * Configure the interface and mark it up, etc.
+ */
+ demand_conf();
+ }
+
+ do_callback = 0;
+ for (;;) {
+
+ listen_time = 0;
+ need_holdoff = 1;
+ devfd = -1;
+ status = EXIT_OK;
+ ++unsuccess;
+ doing_callback = do_callback;
+ do_callback = 0;
+
+ if (demand && !doing_callback) {
+ /*
+ * Don't do anything until we see some activity.
+ */
+ new_phase(PHASE_DORMANT);
+ demand_unblock();
+ add_fd(fd_loop);
+ for (;;) {
+ handle_events();
+ if (kill_link && !persist)
+ break;
+ if (get_loop_output())
+ break;
+ }
+ remove_fd(fd_loop);
+ if (kill_link && !persist)
+ break;
+
+ /*
+ * Now we want to bring up the link.
+ */
+ demand_block();
+ info("Starting link");
+ }
+
+ new_phase(PHASE_SERIALCONN);
+
+ devfd = the_channel->connect();
+ if (devfd < 0)
+ goto fail;
+
+ /* set up the serial device as a ppp interface */
+ tdb_writelock(pppdb);
+ fd_ppp = the_channel->establish_ppp(devfd);
+ if (fd_ppp < 0) {
+ tdb_writeunlock(pppdb);
+ status = EXIT_FATAL_ERROR;
+ goto disconnect;
+ }
+
+ if (!demand && ifunit >= 0)
+ set_ifunit(1);
+ tdb_writeunlock(pppdb);
+
+ /*
+ * 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);
+
+ add_fd(fd_ppp);
+ lcp_open(0); /* Start protocol */
+ status = EXIT_NEGOTIATION_FAILED;
+ new_phase(PHASE_ESTABLISH);
+ while (phase != PHASE_DEAD) {
+ handle_events();
+ get_input();
+ if (kill_link)
+ lcp_close(0, "User request");
+ if (open_ccp_flag) {
+ if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) {
+ ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
+ (*ccp_protent.open)(0);
+ }
+ }
+ }
+
+ /*
+ * Print connect time and statistics.
+ */
+ if (link_stats_valid) {
+ int t = (link_connect_time + 5) / 6; /* 1/10ths of minutes */
+ info("Connect time %d.%d minutes.", t/10, t%10);
+ info("Sent %u bytes, received %u bytes.",
+ link_stats.bytes_out, link_stats.bytes_in);
+ }
+
+ /*
+ * Delete pid file before disestablishing ppp. Otherwise it
+ * can happen that another pppd gets the same unit and then
+ * we delete its pid file.
+ */
+ if (!demand) {
+ if (pidfilename[0] != 0
+ && unlink(pidfilename) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", pidfilename);
+ pidfilename[0] = 0;
+ }
+
+ /*
+ * If we may want to bring the link up again, transfer
+ * the ppp unit back to the loopback. Set the
+ * real serial device back to its normal mode of operation.
+ */
+ remove_fd(fd_ppp);
+ clean_check();
+ the_channel->disestablish_ppp(devfd);
+ fd_ppp = -1;
+ if (!hungup)
+ lcp_lowerdown(0);
+ if (!demand)
+ script_unsetenv("IFNAME");
+
+ /*
+ * Run disconnector script, if requested.
+ * XXX we may not be able to do this if the line has hung up!
+ */
+ disconnect:
+ new_phase(PHASE_DISCONNECT);
+ the_channel->disconnect();
+
+ fail:
+ if (the_channel->cleanup)
+ (*the_channel->cleanup)();
+
+ if (!demand) {
+ if (pidfilename[0] != 0
+ && unlink(pidfilename) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", pidfilename);
+ pidfilename[0] = 0;
+ }
+
+ if (!persist || (maxfail > 0 && unsuccess >= maxfail))
+ break;
+
+ if (demand)
+ demand_discard();
+ t = need_holdoff? holdoff: 0;
+ if (holdoff_hook)
+ t = (*holdoff_hook)();
+ if (t > 0) {
+ new_phase(PHASE_HOLDOFF);
+ TIMEOUT(holdoff_end, NULL, t);
+ do {
+ handle_events();
+ if (kill_link)
+ new_phase(PHASE_DORMANT); /* allow signal to end holdoff */
+ } while (phase == PHASE_HOLDOFF);
+ if (!persist)
+ break;
+ }
+ }
+
+ /* Wait for scripts to finish */
+ /* XXX should have a timeout here */
+ while (n_children > 0) {
+ if (debug) {
+ struct subprocess *chp;
+ dbglog("Waiting for %d child processes...", n_children);
+ for (chp = children; chp != NULL; chp = chp->next)
+ dbglog(" script %s, pid %d", chp->prog, chp->pid);
+ }
+ if (reap_kids(1) < 0)
+ break;
+ }
+
+ die(status);
+ return 0;
+}
+
+/*
+ * handle_events - wait for something to happen and respond to it.
+ */
+static void
+handle_events()
+{
+ struct timeval timo;
+ sigset_t mask;
+
+ kill_link = open_ccp_flag = 0;
+ if (sigsetjmp(sigjmp, 1) == 0) {
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+ if (got_sighup || got_sigterm || got_sigusr2 || got_sigchld) {
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ } else {
+ waiting = 1;
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ wait_input(timeleft(&timo));
+ }
+ }
+ waiting = 0;
+ calltimeout();
+ if (got_sighup) {
+ kill_link = 1;
+ got_sighup = 0;
+ if (status != EXIT_HANGUP)
+ status = EXIT_USER_REQUEST;
+ }
+ if (got_sigterm) {
+ kill_link = 1;
+ persist = 0;
+ status = EXIT_USER_REQUEST;
+ got_sigterm = 0;
+ }
+ if (got_sigchld) {
+ reap_kids(0); /* Don't leave dead kids lying around */
+ got_sigchld = 0;
+ }
+ if (got_sigusr2) {
+ open_ccp_flag = 1;
+ got_sigusr2 = 0;
+ }
+}
+
+/*
+ * setup_signals - initialize signal handling.
+ */
+static void
+setup_signals()
+{
+ struct sigaction sa;
+ sigset_t mask;
+
+ /*
+ * Compute mask of all interesting signals and install signal handlers
+ * for each. Only one signal handler may be active at a time. Therefore,
+ * all other signals should be masked when any handler is executing.
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGHUP);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGCHLD);
+ sigaddset(&mask, SIGUSR2);
+
+#define SIGNAL(s, handler) do { \
+ sa.sa_handler = handler; \
+ if (sigaction(s, &sa, NULL) < 0) \
+ fatal("Couldn't establish signal handler (%d): %m", s); \
+ } while (0)
+
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+ SIGNAL(SIGHUP, hup); /* Hangup */
+ SIGNAL(SIGINT, term); /* Interrupt */
+ SIGNAL(SIGTERM, term); /* Terminate */
+ SIGNAL(SIGCHLD, chld);
+
+ SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */
+ SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */
+
+ /*
+ * Install a handler for other signals which would otherwise
+ * cause pppd to exit without cleaning up.
+ */
+ SIGNAL(SIGABRT, bad_signal);
+ SIGNAL(SIGALRM, bad_signal);
+ SIGNAL(SIGFPE, bad_signal);
+ SIGNAL(SIGILL, bad_signal);
+ SIGNAL(SIGPIPE, bad_signal);
+ SIGNAL(SIGQUIT, bad_signal);
+ SIGNAL(SIGSEGV, bad_signal);
+#ifdef SIGBUS
+ SIGNAL(SIGBUS, bad_signal);
+#endif
+#ifdef SIGEMT
+ SIGNAL(SIGEMT, bad_signal);
+#endif
+#ifdef SIGPOLL
+ SIGNAL(SIGPOLL, bad_signal);
+#endif
+#ifdef SIGPROF
+ SIGNAL(SIGPROF, bad_signal);
+#endif
+#ifdef SIGSYS
+ SIGNAL(SIGSYS, bad_signal);
+#endif
+#ifdef SIGTRAP
+ SIGNAL(SIGTRAP, bad_signal);
+#endif
+#ifdef SIGVTALRM
+ SIGNAL(SIGVTALRM, bad_signal);
+#endif
+#ifdef SIGXCPU
+ SIGNAL(SIGXCPU, bad_signal);
+#endif
+#ifdef SIGXFSZ
+ SIGNAL(SIGXFSZ, bad_signal);
+#endif
+
+ /*
+ * Apparently we can get a SIGPIPE when we call syslog, if
+ * syslogd has died and been restarted. Ignoring it seems
+ * be sufficient.
+ */
+ signal(SIGPIPE, SIG_IGN);
+}
+
+/*
+ * set_ifunit - do things we need to do once we know which ppp
+ * unit we are using.
+ */
+void
+set_ifunit(iskey)
+ int iskey;
+{
+ info("Using interface %s%d", PPP_DRV_NAME, ifunit);
+ slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
+ script_setenv("IFNAME", ifname, iskey);
+ if (iskey) {
+ create_pidfile(); /* write pid to file */
+ create_linkpidfile();
+ }
+}
+
+/*
+ * detach - detach us from the controlling terminal.
+ */
+void
+detach()
+{
+ int pid;
+ char numbuf[16];
+
+ if (detached)
+ return;
+ if ((pid = fork()) < 0) {
+ error("Couldn't detach (fork failed: %m)");
+ die(1); /* or just return? */
+ }
+ if (pid != 0) {
+ /* parent */
+ notify(pidchange, pid);
+ exit(0); /* parent dies */
+ }
+ setsid();
+ chdir("/");
+ close(0);
+ close(1);
+ close(2);
+ detached = 1;
+ if (log_default)
+ log_to_fd = -1;
+ /* update pid files if they have been written already */
+ if (pidfilename[0])
+ create_pidfile();
+ if (linkpidfile[0])
+ create_linkpidfile();
+ slprintf(numbuf, sizeof(numbuf), "%d", getpid());
+ script_setenv("PPPD_PID", numbuf, 1);
+}
+
+/*
+ * reopen_log - (re)open our connection to syslog.
+ */
+void
+reopen_log()
+{
+#ifdef ULTRIX
+ openlog("pppd", LOG_PID);
+#else
+ openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
+ setlogmask(LOG_UPTO(LOG_INFO));
+#endif
+}
+
+/*
+ * Create a file containing our process ID.
+ */
+static void
+create_pidfile()
+{
+ FILE *pidfile;
+
+ slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid",
+ _PATH_VARRUN, ifname);
+ if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", getpid());
+ (void) fclose(pidfile);
+ } else {
+ error("Failed to create pid file %s: %m", pidfilename);
+ pidfilename[0] = 0;
+ }
+}
+
+static void
+create_linkpidfile()
+{
+ FILE *pidfile;
+
+ if (linkname[0] == 0)
+ return;
+ script_setenv("LINKNAME", linkname, 1);
+ slprintf(linkpidfile, sizeof(linkpidfile), "%sppp-%s.pid",
+ _PATH_VARRUN, linkname);
+ if ((pidfile = fopen(linkpidfile, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", getpid());
+ if (ifname[0])
+ fprintf(pidfile, "%s\n", ifname);
+ (void) fclose(pidfile);
+ } else {
+ error("Failed to create pid file %s: %m", linkpidfile);
+ linkpidfile[0] = 0;
+ }
+}
+
+/*
+ * holdoff_end - called via a timeout when the holdoff period ends.
+ */
+static void
+holdoff_end(arg)
+ void *arg;
+{
+ new_phase(PHASE_DORMANT);
+}
+
+/* List of protocol names, to make our messages a little more informative. */
+struct protocol_list {
+ u_short proto;
+ const char *name;
+} protocol_list[] = {
+ { 0x21, "IP" },
+ { 0x23, "OSI Network Layer" },
+ { 0x25, "Xerox NS IDP" },
+ { 0x27, "DECnet Phase IV" },
+ { 0x29, "Appletalk" },
+ { 0x2b, "Novell IPX" },
+ { 0x2d, "VJ compressed TCP/IP" },
+ { 0x2f, "VJ uncompressed TCP/IP" },
+ { 0x31, "Bridging PDU" },
+ { 0x33, "Stream Protocol ST-II" },
+ { 0x35, "Banyan Vines" },
+ { 0x39, "AppleTalk EDDP" },
+ { 0x3b, "AppleTalk SmartBuffered" },
+ { 0x3d, "Multi-Link" },
+ { 0x3f, "NETBIOS Framing" },
+ { 0x41, "Cisco Systems" },
+ { 0x43, "Ascom Timeplex" },
+ { 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" },
+ { 0x47, "DCA Remote Lan" },
+ { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" },
+ { 0x4b, "SNA over 802.2" },
+ { 0x4d, "SNA" },
+ { 0x4f, "IP6 Header Compression" },
+ { 0x6f, "Stampede Bridging" },
+ { 0xfb, "single-link compression" },
+ { 0xfd, "1st choice compression" },
+ { 0x0201, "802.1d Hello Packets" },
+ { 0x0203, "IBM Source Routing BPDU" },
+ { 0x0205, "DEC LANBridge100 Spanning Tree" },
+ { 0x0231, "Luxcom" },
+ { 0x0233, "Sigma Network Systems" },
+ { 0x8021, "Internet Protocol Control Protocol" },
+ { 0x8023, "OSI Network Layer Control Protocol" },
+ { 0x8025, "Xerox NS IDP Control Protocol" },
+ { 0x8027, "DECnet Phase IV Control Protocol" },
+ { 0x8029, "Appletalk Control Protocol" },
+ { 0x802b, "Novell IPX Control Protocol" },
+ { 0x8031, "Bridging NCP" },
+ { 0x8033, "Stream Protocol Control Protocol" },
+ { 0x8035, "Banyan Vines Control Protocol" },
+ { 0x803d, "Multi-Link Control Protocol" },
+ { 0x803f, "NETBIOS Framing Control Protocol" },
+ { 0x8041, "Cisco Systems Control Protocol" },
+ { 0x8043, "Ascom Timeplex" },
+ { 0x8045, "Fujitsu LBLB Control Protocol" },
+ { 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" },
+ { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" },
+ { 0x804b, "SNA over 802.2 Control Protocol" },
+ { 0x804d, "SNA Control Protocol" },
+ { 0x804f, "IP6 Header Compression Control Protocol" },
+ { 0x006f, "Stampede Bridging Control Protocol" },
+ { 0x80fb, "Single Link Compression Control Protocol" },
+ { 0x80fd, "Compression Control Protocol" },
+ { 0xc021, "Link Control Protocol" },
+ { 0xc023, "Password Authentication Protocol" },
+ { 0xc025, "Link Quality Report" },
+ { 0xc027, "Shiva Password Authentication Protocol" },
+ { 0xc029, "CallBack Control Protocol (CBCP)" },
+ { 0xc081, "Container Control Protocol" },
+ { 0xc223, "Challenge Handshake Authentication Protocol" },
+ { 0xc281, "Proprietary Authentication Protocol" },
+ { 0, NULL },
+};
+
+/*
+ * protocol_name - find a name for a PPP protocol.
+ */
+const char *
+protocol_name(proto)
+ int proto;
+{
+ struct protocol_list *lp;
+
+ for (lp = protocol_list; lp->proto != 0; ++lp)
+ if (proto == lp->proto)
+ return lp->name;
+ return NULL;
+}
+
+/*
+ * get_input - called when incoming data is available.
+ */
+static void
+get_input()
+{
+ int len, i;
+ u_char *p;
+ u_short protocol;
+ struct protent *protp;
+
+ p = inpacket_buf; /* point to beginning of packet buffer */
+
+ len = read_packet(inpacket_buf);
+ if (len < 0)
+ return;
+
+ if (len == 0) {
+ notice("Modem hangup");
+ hungup = 1;
+ status = EXIT_HANGUP;
+ lcp_lowerdown(0); /* serial link is no longer available */
+ link_terminated(0);
+ return;
+ }
+
+ if (debug /*&& (debugflags & DBG_INPACKET)*/)
+ dbglog("rcvd %P", p, len);
+
+ if (len < PPP_HDRLEN) {
+ MAINDEBUG(("io(): Received short packet."));
+ return;
+ }
+
+ p += 2; /* Skip address and control */
+ GETSHORT(protocol, p);
+ len -= PPP_HDRLEN;
+
+ /*
+ * Toss all non-LCP packets unless LCP is OPEN.
+ */
+ if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
+ MAINDEBUG(("get_input: Received non-LCP packet when LCP not open."));
+ return;
+ }
+
+ /*
+ * Until we get past the authentication phase, toss all packets
+ * except LCP, LQR and authentication packets.
+ */
+ if (phase <= PHASE_AUTHENTICATE
+ && !(protocol == PPP_LCP || protocol == PPP_LQR
+ || protocol == PPP_PAP || protocol == PPP_CHAP)) {
+ MAINDEBUG(("get_input: discarding proto 0x%x in phase %d",
+ protocol, phase));
+ return;
+ }
+
+ /*
+ * Upcall the proper protocol input routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol == protocol && protp->enabled_flag) {
+ (*protp->input)(0, p, len);
+ return;
+ }
+ if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag
+ && protp->datainput != NULL) {
+ (*protp->datainput)(0, p, len);
+ return;
+ }
+ }
+
+ if (debug) {
+ const char *pname = protocol_name(protocol);
+ if (pname != NULL)
+ warn("Unsupported protocol '%s' (0x%x) received", pname, protocol);
+ else
+ warn("Unsupported protocol 0x%x received", protocol);
+ }
+ lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
+}
+
+/*
+ * new_phase - signal the start of a new phase of pppd's operation.
+ */
+void
+new_phase(p)
+ int p;
+{
+ phase = p;
+ if (new_phase_hook)
+ (*new_phase_hook)(p);
+ notify(phasechange, p);
+}
+
+/*
+ * die - clean up state and exit with the specified status.
+ */
+void
+die(status)
+ int status;
+{
+ cleanup();
+ notify(exitnotify, status);
+ syslog(LOG_INFO, "Exit.");
+ exit(status);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+/* ARGSUSED */
+static void
+cleanup()
+{
+ sys_cleanup();
+
+ if (fd_ppp >= 0)
+ the_channel->disestablish_ppp(devfd);
+ if (the_channel->cleanup)
+ (*the_channel->cleanup)();
+
+ if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", pidfilename);
+ pidfilename[0] = 0;
+ if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", linkpidfile);
+ linkpidfile[0] = 0;
+
+ if (pppdb != NULL)
+ cleanup_db();
+}
+
+/*
+ * update_link_stats - get stats at link termination.
+ */
+void
+update_link_stats(u)
+ int u;
+{
+ struct timeval now;
+ char numbuf[32];
+
+ if (!get_ppp_stats(u, &link_stats)
+ || gettimeofday(&now, NULL) < 0)
+ return;
+ link_connect_time = now.tv_sec - start_time.tv_sec;
+ link_stats_valid = 1;
+
+ slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time);
+ script_setenv("CONNECT_TIME", numbuf, 0);
+ slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_out);
+ script_setenv("BYTES_SENT", numbuf, 0);
+ slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_in);
+ script_setenv("BYTES_RCVD", numbuf, 0);
+}
+
+
+struct callout {
+ struct timeval c_time; /* time at which to call routine */
+ void *c_arg; /* argument to routine */
+ void (*c_func) __P((void *)); /* routine */
+ struct callout *c_next;
+};
+
+static struct callout *callout = NULL; /* Callout list */
+static struct timeval timenow; /* Current time */
+
+/*
+ * timeout - Schedule a timeout.
+ *
+ * Note that this timeout takes the number of milliseconds, NOT hz (as in
+ * the kernel).
+ */
+void
+timeout(func, arg, secs, usecs)
+ void (*func) __P((void *));
+ void *arg;
+ int secs, usecs;
+{
+ struct callout *newp, *p, **pp;
+
+ MAINDEBUG(("Timeout %p:%p in %d.%03d seconds.", func, arg,
+ time / 1000, time % 1000));
+
+ /*
+ * Allocate timeout.
+ */
+ if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL)
+ fatal("Out of memory in timeout()!");
+ newp->c_arg = arg;
+ newp->c_func = func;
+ gettimeofday(&timenow, NULL);
+ newp->c_time.tv_sec = timenow.tv_sec + secs;
+ newp->c_time.tv_usec = timenow.tv_usec + usecs;
+ if (newp->c_time.tv_usec >= 1000000) {
+ newp->c_time.tv_sec += newp->c_time.tv_usec / 1000000;
+ newp->c_time.tv_usec %= 1000000;
+ }
+
+ /*
+ * Find correct place and link it in.
+ */
+ for (pp = &callout; (p = *pp); pp = &p->c_next)
+ if (newp->c_time.tv_sec < p->c_time.tv_sec
+ || (newp->c_time.tv_sec == p->c_time.tv_sec
+ && newp->c_time.tv_usec < p->c_time.tv_usec))
+ break;
+ newp->c_next = p;
+ *pp = newp;
+}
+
+
+/*
+ * untimeout - Unschedule a timeout.
+ */
+void
+untimeout(func, arg)
+ void (*func) __P((void *));
+ void *arg;
+{
+ struct callout **copp, *freep;
+
+ MAINDEBUG(("Untimeout %p:%p.", func, arg));
+
+ /*
+ * Find first matching timeout and remove it from the list.
+ */
+ for (copp = &callout; (freep = *copp); copp = &freep->c_next)
+ if (freep->c_func == func && freep->c_arg == arg) {
+ *copp = freep->c_next;
+ free((char *) freep);
+ break;
+ }
+}
+
+
+/*
+ * calltimeout - Call any timeout routines which are now due.
+ */
+static void
+calltimeout()
+{
+ struct callout *p;
+
+ while (callout != NULL) {
+ p = callout;
+
+ if (gettimeofday(&timenow, NULL) < 0)
+ fatal("Failed to get time of day: %m");
+ if (!(p->c_time.tv_sec < timenow.tv_sec
+ || (p->c_time.tv_sec == timenow.tv_sec
+ && p->c_time.tv_usec <= timenow.tv_usec)))
+ break; /* no, it's not time yet */
+
+ callout = p->c_next;
+ (*p->c_func)(p->c_arg);
+
+ free((char *) p);
+ }
+}
+
+
+/*
+ * timeleft - return the length of time until the next timeout is due.
+ */
+static struct timeval *
+timeleft(tvp)
+ struct timeval *tvp;
+{
+ if (callout == NULL)
+ return NULL;
+
+ gettimeofday(&timenow, NULL);
+ tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
+ tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec;
+ if (tvp->tv_usec < 0) {
+ tvp->tv_usec += 1000000;
+ tvp->tv_sec -= 1;
+ }
+ if (tvp->tv_sec < 0)
+ tvp->tv_sec = tvp->tv_usec = 0;
+
+ return tvp;
+}
+
+
+/*
+ * kill_my_pg - send a signal to our process group, and ignore it ourselves.
+ */
+static void
+kill_my_pg(sig)
+ int sig;
+{
+ struct sigaction act, oldact;
+
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = 0;
+ kill(0, sig);
+ sigaction(sig, &act, &oldact);
+ sigaction(sig, &oldact, NULL);
+}
+
+
+/*
+ * hup - Catch SIGHUP signal.
+ *
+ * Indicates that the physical layer has been disconnected.
+ * We don't rely on this indication; if the user has sent this
+ * signal, we just take the link down.
+ */
+static void
+hup(sig)
+ int sig;
+{
+ info("Hangup (SIGHUP)");
+ got_sighup = 1;
+ if (conn_running)
+ /* Send the signal to the [dis]connector process(es) also */
+ kill_my_pg(sig);
+ notify(sigreceived, sig);
+ if (waiting)
+ siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * term - Catch SIGTERM signal and SIGINT signal (^C/del).
+ *
+ * Indicates that we should initiate a graceful disconnect and exit.
+ */
+/*ARGSUSED*/
+static void
+term(sig)
+ int sig;
+{
+ info("Terminating on signal %d.", sig);
+ got_sigterm = 1;
+ if (conn_running)
+ /* Send the signal to the [dis]connector process(es) also */
+ kill_my_pg(sig);
+ notify(sigreceived, sig);
+ if (waiting)
+ siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * chld - Catch SIGCHLD signal.
+ * Sets a flag so we will call reap_kids in the mainline.
+ */
+static void
+chld(sig)
+ int sig;
+{
+ got_sigchld = 1;
+ if (waiting)
+ siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * toggle_debug - Catch SIGUSR1 signal.
+ *
+ * Toggle debug flag.
+ */
+/*ARGSUSED*/
+static void
+toggle_debug(sig)
+ int sig;
+{
+ debug = !debug;
+ if (debug) {
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ } else {
+ setlogmask(LOG_UPTO(LOG_WARNING));
+ }
+}
+
+
+/*
+ * open_ccp - Catch SIGUSR2 signal.
+ *
+ * Try to (re)negotiate compression.
+ */
+/*ARGSUSED*/
+static void
+open_ccp(sig)
+ int sig;
+{
+ got_sigusr2 = 1;
+ if (waiting)
+ siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * bad_signal - We've caught a fatal signal. Clean up state and exit.
+ */
+static void
+bad_signal(sig)
+ int sig;
+{
+ static int crashed = 0;
+
+ if (crashed)
+ _exit(127);
+ crashed = 1;
+ error("Fatal signal %d", sig);
+ if (conn_running)
+ kill_my_pg(SIGTERM);
+ notify(sigreceived, sig);
+ die(127);
+}
+
+
+/*
+ * device_script - run a program to talk to the specified fds
+ * (e.g. to run the connector or disconnector script).
+ * stderr gets connected to the log fd or to the _PATH_CONNERRS file.
+ */
+int
+device_script(program, in, out, dont_wait)
+ char *program;
+ int in, out;
+ int dont_wait;
+{
+ int pid, fd;
+ int status = -1;
+ int errfd;
+
+ ++conn_running;
+ pid = fork();
+
+ if (pid < 0) {
+ --conn_running;
+ error("Failed to create child process: %m");
+ return -1;
+ }
+
+ if (pid != 0) {
+ if (dont_wait) {
+ record_child(pid, program, NULL, NULL);
+ status = 0;
+ } else {
+ while (waitpid(pid, &status, 0) < 0) {
+ if (errno == EINTR)
+ continue;
+ fatal("error waiting for (dis)connection process: %m");
+ }
+ --conn_running;
+ }
+ return (status == 0 ? 0 : -1);
+ }
+
+ /* here we are executing in the child */
+ /* make sure fds 0, 1, 2 are occupied */
+ while ((fd = dup(in)) >= 0) {
+ if (fd > 2) {
+ close(fd);
+ break;
+ }
+ }
+
+ /* dup in and out to fds > 2 */
+ in = dup(in);
+ out = dup(out);
+ if (log_to_fd >= 0) {
+ errfd = dup(log_to_fd);
+ } else {
+ errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
+ }
+
+ /* close fds 0 - 2 and any others we can think of */
+ close(0);
+ close(1);
+ close(2);
+ sys_close();
+ if (the_channel->close)
+ (*the_channel->close)();
+ closelog();
+
+ /* dup the in, out, err fds to 0, 1, 2 */
+ dup2(in, 0);
+ close(in);
+ dup2(out, 1);
+ close(out);
+ if (errfd >= 0) {
+ dup2(errfd, 2);
+ close(errfd);
+ }
+
+ setuid(uid);
+ if (getuid() != uid) {
+ error("setuid failed");
+ exit(1);
+ }
+ setgid(getgid());
+ execl("/bin/sh", "sh", "-c", program, (char *)0);
+ error("could not exec /bin/sh: %m");
+ exit(99);
+ /* NOTREACHED */
+}
+
+
+/*
+ * run-program - execute a program with given arguments,
+ * but don't wait for it.
+ * If the program can't be executed, logs an error unless
+ * must_exist is 0 and the program file doesn't exist.
+ * Returns -1 if it couldn't fork, 0 if the file doesn't exist
+ * or isn't an executable plain file, or the process ID of the child.
+ * If done != NULL, (*done)(arg) will be called later (within
+ * reap_kids) iff the return value is > 0.
+ */
+pid_t
+run_program(prog, args, must_exist, done, arg)
+ char *prog;
+ char **args;
+ int must_exist;
+ void (*done) __P((void *));
+ void *arg;
+{
+ int pid;
+ struct stat sbuf;
+
+ /*
+ * First check if the file exists and is executable.
+ * We don't use access() because that would use the
+ * real user-id, which might not be root, and the script
+ * might be accessible only to root.
+ */
+ errno = EINVAL;
+ if (stat(prog, &sbuf) < 0 || !S_ISREG(sbuf.st_mode)
+ || (sbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0) {
+ if (must_exist || errno != ENOENT)
+ warn("Can't execute %s: %m", prog);
+ return 0;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ error("Failed to create child process for %s: %m", prog);
+ return -1;
+ }
+ if (pid == 0) {
+ int new_fd;
+
+ /* Leave the current location */
+ (void) setsid(); /* No controlling tty. */
+ (void) umask (S_IRWXG|S_IRWXO);
+ (void) chdir ("/"); /* no current directory. */
+ setuid(0); /* set real UID = root */
+ setgid(getegid());
+
+ /* Ensure that nothing of our device environment is inherited. */
+ sys_close();
+ closelog();
+ close (0);
+ close (1);
+ close (2);
+ if (the_channel->close)
+ (*the_channel->close)();
+
+ /* Don't pass handles to the PPP device, even by accident. */
+ new_fd = open (_PATH_DEVNULL, O_RDWR);
+ if (new_fd >= 0) {
+ if (new_fd != 0) {
+ dup2 (new_fd, 0); /* stdin <- /dev/null */
+ close (new_fd);
+ }
+ dup2 (0, 1); /* stdout -> /dev/null */
+ dup2 (0, 2); /* stderr -> /dev/null */
+ }
+
+#ifdef BSD
+ /* Force the priority back to zero if pppd is running higher. */
+ if (setpriority (PRIO_PROCESS, 0, 0) < 0)
+ warn("can't reset priority to 0: %m");
+#endif
+
+ /* SysV recommends a second fork at this point. */
+
+ /* run the program */
+ execve(prog, args, script_env);
+ if (must_exist || errno != ENOENT) {
+ /* have to reopen the log, there's nowhere else
+ for the message to go. */
+ reopen_log();
+ syslog(LOG_ERR, "Can't execute %s: %m", prog);
+ closelog();
+ }
+ _exit(-1);
+ }
+
+ if (debug)
+ dbglog("Script %s started (pid %d)", prog, pid);
+ record_child(pid, prog, done, arg);
+
+ return pid;
+}
+
+
+/*
+ * record_child - add a child process to the list for reap_kids
+ * to use.
+ */
+void
+record_child(pid, prog, done, arg)
+ int pid;
+ char *prog;
+ void (*done) __P((void *));
+ void *arg;
+{
+ struct subprocess *chp;
+
+ ++n_children;
+
+ chp = (struct subprocess *) malloc(sizeof(struct subprocess));
+ if (chp == NULL) {
+ warn("losing track of %s process", prog);
+ } else {
+ chp->pid = pid;
+ chp->prog = prog;
+ chp->done = done;
+ chp->arg = arg;
+ chp->next = children;
+ children = chp;
+ }
+}
+
+
+/*
+ * reap_kids - get status from any dead child processes,
+ * and log a message for abnormal terminations.
+ */
+static int
+reap_kids(waitfor)
+ int waitfor;
+{
+ int pid, status;
+ struct subprocess *chp, **prevp;
+
+ if (n_children == 0)
+ return 0;
+ while ((pid = waitpid(-1, &status, (waitfor? 0: WNOHANG))) != -1
+ && pid != 0) {
+ for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) {
+ if (chp->pid == pid) {
+ --n_children;
+ *prevp = chp->next;
+ break;
+ }
+ }
+ if (WIFSIGNALED(status)) {
+ warn("Child process %s (pid %d) terminated with signal %d",
+ (chp? chp->prog: "??"), pid, WTERMSIG(status));
+ } else if (debug)
+ dbglog("Script %s finished (pid %d), status = 0x%x",
+ (chp? chp->prog: "??"), pid, status);
+ if (chp && chp->done)
+ (*chp->done)(chp->arg);
+ if (chp)
+ free(chp);
+ }
+ if (pid == -1) {
+ if (errno == ECHILD)
+ return -1;
+ if (errno != EINTR)
+ error("Error waiting for child process: %m");
+ }
+ return 0;
+}
+
+/*
+ * add_notifier - add a new function to be called when something happens.
+ */
+void
+add_notifier(notif, func, arg)
+ struct notifier **notif;
+ notify_func func;
+ void *arg;
+{
+ struct notifier *np;
+
+ np = malloc(sizeof(struct notifier));
+ if (np == 0)
+ novm("notifier struct");
+ np->next = *notif;
+ np->func = func;
+ np->arg = arg;
+ *notif = np;
+}
+
+/*
+ * remove_notifier - remove a function from the list of things to
+ * be called when something happens.
+ */
+void
+remove_notifier(notif, func, arg)
+ struct notifier **notif;
+ notify_func func;
+ void *arg;
+{
+ struct notifier *np;
+
+ for (; (np = *notif) != 0; notif = &np->next) {
+ if (np->func == func && np->arg == arg) {
+ *notif = np->next;
+ free(np);
+ break;
+ }
+ }
+}
+
+/*
+ * notify - call a set of functions registered with add_notify.
+ */
+void
+notify(notif, val)
+ struct notifier *notif;
+ int val;
+{
+ struct notifier *np;
+
+ while ((np = notif) != 0) {
+ notif = np->next;
+ (*np->func)(np->arg, val);
+ }
+}
+
+/*
+ * novm - log an error message saying we ran out of memory, and die.
+ */
+void
+novm(msg)
+ char *msg;
+{
+ fatal("Virtual memory exhausted allocating %s\n", msg);
+}
+
+/*
+ * script_setenv - set an environment variable value to be used
+ * for scripts that we run (e.g. ip-up, auth-up, etc.)
+ */
+void
+script_setenv(var, value, iskey)
+ char *var, *value;
+ int iskey;
+{
+ size_t varl = strlen(var);
+ size_t vl = varl + strlen(value) + 2;
+ int i;
+ char *p, *newstring;
+
+ newstring = (char *) malloc(vl+1);
+ if (newstring == 0)
+ return;
+ *newstring++ = iskey;
+ slprintf(newstring, vl, "%s=%s", var, value);
+
+ /* check if this variable is already set */
+ if (script_env != 0) {
+ for (i = 0; (p = script_env[i]) != 0; ++i) {
+ if (strncmp(p, var, varl) == 0 && p[varl] == '=') {
+ if (p[-1] && pppdb != NULL)
+ delete_db_key(p);
+ free(p-1);
+ script_env[i] = newstring;
+ if (iskey && pppdb != NULL)
+ add_db_key(newstring);
+ update_db_entry();
+ return;
+ }
+ }
+ } else {
+ /* no space allocated for script env. ptrs. yet */
+ i = 0;
+ script_env = (char **) malloc(16 * sizeof(char *));
+ if (script_env == 0)
+ return;
+ s_env_nalloc = 16;
+ }
+
+ /* reallocate script_env with more space if needed */
+ if (i + 1 >= s_env_nalloc) {
+ int new_n = i + 17;
+ char **newenv = (char **) realloc((void *)script_env,
+ new_n * sizeof(char *));
+ if (newenv == 0)
+ return;
+ script_env = newenv;
+ s_env_nalloc = new_n;
+ }
+
+ script_env[i] = newstring;
+ script_env[i+1] = 0;
+
+ if (pppdb != NULL) {
+ if (iskey)
+ add_db_key(newstring);
+ update_db_entry();
+ }
+}
+
+/*
+ * script_unsetenv - remove a variable from the environment
+ * for scripts.
+ */
+void
+script_unsetenv(var)
+ char *var;
+{
+ int vl = strlen(var);
+ int i;
+ char *p;
+
+ if (script_env == 0)
+ return;
+ for (i = 0; (p = script_env[i]) != 0; ++i) {
+ if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
+ if (p[-1] && pppdb != NULL)
+ delete_db_key(p);
+ free(p-1);
+ while ((script_env[i] = script_env[i+1]) != 0)
+ ++i;
+ break;
+ }
+ }
+ if (pppdb != NULL)
+ update_db_entry();
+}
+
+/*
+ * update_db_entry - update our entry in the database.
+ */
+static void
+update_db_entry()
+{
+ TDB_DATA key, dbuf;
+ int vlen, i;
+ char *p, *q, *vbuf;
+
+ if (script_env == NULL)
+ return;
+ vlen = 0;
+ for (i = 0; (p = script_env[i]) != 0; ++i)
+ vlen += strlen(p) + 1;
+ vbuf = malloc(vlen);
+ if (vbuf == 0)
+ novm("database entry");
+ q = vbuf;
+ for (i = 0; (p = script_env[i]) != 0; ++i)
+ q += slprintf(q, vbuf + vlen - q, "%s;", p);
+
+ key.dptr = db_key;
+ key.dsize = strlen(db_key);
+ dbuf.dptr = vbuf;
+ dbuf.dsize = vlen;
+ if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+ error("tdb_store failed: %s", tdb_error(pppdb));
+
+}
+
+/*
+ * add_db_key - add a key that we can use to look up our database entry.
+ */
+static void
+add_db_key(str)
+ const char *str;
+{
+ TDB_DATA key, dbuf;
+
+ key.dptr = (char *) str;
+ key.dsize = strlen(str);
+ dbuf.dptr = db_key;
+ dbuf.dsize = strlen(db_key);
+ if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+ error("tdb_store key failed: %s", tdb_error(pppdb));
+}
+
+/*
+ * delete_db_key - delete a key for looking up our database entry.
+ */
+static void
+delete_db_key(str)
+ const char *str;
+{
+ TDB_DATA key;
+
+ key.dptr = (char *) str;
+ key.dsize = strlen(str);
+ tdb_delete(pppdb, key);
+}
+
+/*
+ * cleanup_db - delete all the entries we put in the database.
+ */
+static void
+cleanup_db()
+{
+ TDB_DATA key;
+ int i;
+ char *p;
+
+ key.dptr = db_key;
+ key.dsize = strlen(db_key);
+ tdb_delete(pppdb, key);
+ for (i = 0; (p = script_env[i]) != 0; ++i)
+ if (p[-1])
+ delete_db_key(p);
+}
diff --git a/mdk-stage1/ppp/pppd/md4.c b/mdk-stage1/ppp/pppd/md4.c
new file mode 100644
index 000000000..cda9f943d
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/md4.c
@@ -0,0 +1,298 @@
+/*
+** ********************************************************************
+** md4.c -- Implementation of MD4 Message Digest Algorithm **
+** Updated: 2/16/90 by Ronald L. Rivest **
+** (C) 1990 RSA Data Security, Inc. **
+** ********************************************************************
+*/
+
+/*
+** To use MD4:
+** -- Include md4.h in your program
+** -- Declare an MDstruct MD to hold the state of the digest
+** computation.
+** -- Initialize MD using MDbegin(&MD)
+** -- For each full block (64 bytes) X you wish to process, call
+** MD4Update(&MD,X,512)
+** (512 is the number of bits in a full block.)
+** -- For the last block (less than 64 bytes) you wish to process,
+** MD4Update(&MD,X,n)
+** where n is the number of bits in the partial block. A partial
+** block terminates the computation, so every MD computation
+** should terminate by processing a partial block, even if it
+** has n = 0.
+** -- The message digest is available in MD.buffer[0] ...
+** MD.buffer[3]. (Least-significant byte of each word
+** should be output first.)
+** -- You can print out the digest using MDprint(&MD)
+*/
+
+/* Implementation notes:
+** This implementation assumes that ints are 32-bit quantities.
+*/
+
+#define TRUE 1
+#define FALSE 0
+
+/* Compile-time includes
+*/
+#include <stdio.h>
+#include "md4.h"
+#include "pppd.h"
+
+/* Compile-time declarations of MD4 "magic constants".
+*/
+#define I0 0x67452301 /* Initial values for MD buffer */
+#define I1 0xefcdab89
+#define I2 0x98badcfe
+#define I3 0x10325476
+#define C2 013240474631 /* round 2 constant = sqrt(2) in octal */
+#define C3 015666365641 /* round 3 constant = sqrt(3) in octal */
+/* C2 and C3 are from Knuth, The Art of Programming, Volume 2
+** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley.
+** Table 2, page 660.
+*/
+
+#define fs1 3 /* round 1 shift amounts */
+#define fs2 7
+#define fs3 11
+#define fs4 19
+#define gs1 3 /* round 2 shift amounts */
+#define gs2 5
+#define gs3 9
+#define gs4 13
+#define hs1 3 /* round 3 shift amounts */
+#define hs2 9
+#define hs3 11
+#define hs4 15
+
+/* Compile-time macro declarations for MD4.
+** Note: The "rot" operator uses the variable "tmp".
+** It assumes tmp is declared as unsigned int, so that the >>
+** operator will shift in zeros rather than extending the sign bit.
+*/
+#define f(X,Y,Z) ((X&Y) | ((~X)&Z))
+#define g(X,Y,Z) ((X&Y) | (X&Z) | (Y&Z))
+#define h(X,Y,Z) (X^Y^Z)
+#define rot(X,S) (tmp=X,(tmp<<S) | (tmp>>(32-S)))
+#define ff(A,B,C,D,i,s) A = rot((A + f(B,C,D) + X[i]),s)
+#define gg(A,B,C,D,i,s) A = rot((A + g(B,C,D) + X[i] + C2),s)
+#define hh(A,B,C,D,i,s) A = rot((A + h(B,C,D) + X[i] + C3),s)
+
+/* MD4print(MDp)
+** Print message digest buffer MDp as 32 hexadecimal digits.
+** Order is from low-order byte of buffer[0] to high-order byte of
+** buffer[3].
+** Each byte is printed with high-order hexadecimal digit first.
+** This is a user-callable routine.
+*/
+void
+MD4Print(MDp)
+MD4_CTX *MDp;
+{
+ int i,j;
+ for (i=0;i<4;i++)
+ for (j=0;j<32;j=j+8)
+ printf("%02x",(MDp->buffer[i]>>j) & 0xFF);
+}
+
+/* MD4Init(MDp)
+** Initialize message digest buffer MDp.
+** This is a user-callable routine.
+*/
+void
+MD4Init(MDp)
+MD4_CTX *MDp;
+{
+ int i;
+ MDp->buffer[0] = I0;
+ MDp->buffer[1] = I1;
+ MDp->buffer[2] = I2;
+ MDp->buffer[3] = I3;
+ for (i=0;i<8;i++) MDp->count[i] = 0;
+ MDp->done = 0;
+}
+
+/* MDblock(MDp,X)
+** Update message digest buffer MDp->buffer using 16-word data block X.
+** Assumes all 16 words of X are full of data.
+** Does not update MDp->count.
+** This routine is not user-callable.
+*/
+static void
+MDblock(MDp,Xb)
+MD4_CTX *MDp;
+unsigned char *Xb;
+{
+ register unsigned int tmp, A, B, C, D;
+ unsigned int X[16];
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ X[i] = Xb[0] + (Xb[1] << 8) + (Xb[2] << 16) + (Xb[3] << 24);
+ Xb += 4;
+ }
+
+ A = MDp->buffer[0];
+ B = MDp->buffer[1];
+ C = MDp->buffer[2];
+ D = MDp->buffer[3];
+ /* Update the message digest buffer */
+ ff(A , B , C , D , 0 , fs1); /* Round 1 */
+ ff(D , A , B , C , 1 , fs2);
+ ff(C , D , A , B , 2 , fs3);
+ ff(B , C , D , A , 3 , fs4);
+ ff(A , B , C , D , 4 , fs1);
+ ff(D , A , B , C , 5 , fs2);
+ ff(C , D , A , B , 6 , fs3);
+ ff(B , C , D , A , 7 , fs4);
+ ff(A , B , C , D , 8 , fs1);
+ ff(D , A , B , C , 9 , fs2);
+ ff(C , D , A , B , 10 , fs3);
+ ff(B , C , D , A , 11 , fs4);
+ ff(A , B , C , D , 12 , fs1);
+ ff(D , A , B , C , 13 , fs2);
+ ff(C , D , A , B , 14 , fs3);
+ ff(B , C , D , A , 15 , fs4);
+ gg(A , B , C , D , 0 , gs1); /* Round 2 */
+ gg(D , A , B , C , 4 , gs2);
+ gg(C , D , A , B , 8 , gs3);
+ gg(B , C , D , A , 12 , gs4);
+ gg(A , B , C , D , 1 , gs1);
+ gg(D , A , B , C , 5 , gs2);
+ gg(C , D , A , B , 9 , gs3);
+ gg(B , C , D , A , 13 , gs4);
+ gg(A , B , C , D , 2 , gs1);
+ gg(D , A , B , C , 6 , gs2);
+ gg(C , D , A , B , 10 , gs3);
+ gg(B , C , D , A , 14 , gs4);
+ gg(A , B , C , D , 3 , gs1);
+ gg(D , A , B , C , 7 , gs2);
+ gg(C , D , A , B , 11 , gs3);
+ gg(B , C , D , A , 15 , gs4);
+ hh(A , B , C , D , 0 , hs1); /* Round 3 */
+ hh(D , A , B , C , 8 , hs2);
+ hh(C , D , A , B , 4 , hs3);
+ hh(B , C , D , A , 12 , hs4);
+ hh(A , B , C , D , 2 , hs1);
+ hh(D , A , B , C , 10 , hs2);
+ hh(C , D , A , B , 6 , hs3);
+ hh(B , C , D , A , 14 , hs4);
+ hh(A , B , C , D , 1 , hs1);
+ hh(D , A , B , C , 9 , hs2);
+ hh(C , D , A , B , 5 , hs3);
+ hh(B , C , D , A , 13 , hs4);
+ hh(A , B , C , D , 3 , hs1);
+ hh(D , A , B , C , 11 , hs2);
+ hh(C , D , A , B , 7 , hs3);
+ hh(B , C , D , A , 15 , hs4);
+ MDp->buffer[0] += A;
+ MDp->buffer[1] += B;
+ MDp->buffer[2] += C;
+ MDp->buffer[3] += D;
+}
+
+/* MD4Update(MDp,X,count)
+** Input: X -- a pointer to an array of unsigned characters.
+** count -- the number of bits of X to use.
+** (if not a multiple of 8, uses high bits of last byte.)
+** Update MDp using the number of bits of X given by count.
+** This is the basic input routine for an MD4 user.
+** The routine completes the MD computation when count < 512, so
+** every MD computation should end with one call to MD4Update with a
+** count less than 512. A call with count 0 will be ignored if the
+** MD has already been terminated (done != 0), so an extra call with
+** count 0 can be given as a "courtesy close" to force termination
+** if desired.
+*/
+void
+MD4Update(MDp,X,count)
+MD4_CTX *MDp;
+unsigned char *X;
+unsigned int count;
+{
+ unsigned int i, tmp, bit, byte, mask;
+ unsigned char XX[64];
+ unsigned char *p;
+
+ /* return with no error if this is a courtesy close with count
+ ** zero and MDp->done is true.
+ */
+ if (count == 0 && MDp->done) return;
+ /* check to see if MD is already done and report error */
+ if (MDp->done)
+ { printf("\nError: MD4Update MD already done."); return; }
+
+ /* Add count to MDp->count */
+ tmp = count;
+ p = MDp->count;
+ while (tmp)
+ { tmp += *p;
+ *p++ = tmp;
+ tmp = tmp >> 8;
+ }
+
+ /* Process data */
+ if (count == 512)
+ { /* Full block of data to handle */
+ MDblock(MDp,X);
+ }
+ else if (count > 512) /* Check for count too large */
+ {
+ printf("\nError: MD4Update called with illegal count value %d.",
+ count);
+ return;
+ }
+ else /* partial block -- must be last block so finish up */
+ {
+ /* Find out how many bytes and residual bits there are */
+ byte = count >> 3;
+ bit = count & 7;
+ /* Copy X into XX since we need to modify it */
+ for (i=0;i<=byte;i++) XX[i] = X[i];
+ for (i=byte+1;i<64;i++) XX[i] = 0;
+ /* Add padding '1' bit and low-order zeros in last byte */
+ mask = 1 << (7 - bit);
+ XX[byte] = (XX[byte] | mask) & ~( mask - 1);
+ /* If room for bit count, finish up with this block */
+ if (byte <= 55)
+ {
+ for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
+ MDblock(MDp,XX);
+ }
+ else /* need to do two blocks to finish up */
+ {
+ MDblock(MDp,XX);
+ for (i=0;i<56;i++) XX[i] = 0;
+ for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
+ MDblock(MDp,XX);
+ }
+ /* Set flag saying we're done with MD computation */
+ MDp->done = 1;
+ }
+}
+
+/*
+** Finish up MD4 computation and return message digest.
+*/
+void
+MD4Final(buf, MD)
+unsigned char *buf;
+MD4_CTX *MD;
+{
+ int i, j;
+ unsigned int w;
+
+ MD4Update(MD, NULL, 0);
+ for (i = 0; i < 4; ++i) {
+ w = MD->buffer[i];
+ for (j = 0; j < 4; ++j) {
+ *buf++ = w;
+ w >>= 8;
+ }
+ }
+}
+
+/*
+** End of md4.c
+****************************(cut)***********************************/
diff --git a/mdk-stage1/ppp/pppd/md4.h b/mdk-stage1/ppp/pppd/md4.h
new file mode 100644
index 000000000..80e8f9a2a
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/md4.h
@@ -0,0 +1,64 @@
+
+/*
+** ********************************************************************
+** md4.h -- Header file for implementation of **
+** MD4 Message Digest Algorithm **
+** Updated: 2/13/90 by Ronald L. Rivest **
+** (C) 1990 RSA Data Security, Inc. **
+** ********************************************************************
+*/
+
+#ifndef __P
+# if defined(__STDC__) || defined(__GNUC__)
+# define __P(x) x
+# else
+# define __P(x) ()
+# endif
+#endif
+
+
+/* MDstruct is the data structure for a message digest computation.
+*/
+typedef struct {
+ unsigned int buffer[4]; /* Holds 4-word result of MD computation */
+ unsigned char count[8]; /* Number of bits processed so far */
+ unsigned int done; /* Nonzero means MD computation finished */
+} MD4_CTX;
+
+/* MD4Init(MD4_CTX *)
+** Initialize the MD4_CTX prepatory to doing a message digest
+** computation.
+*/
+extern void MD4Init __P((MD4_CTX *MD));
+
+/* MD4Update(MD,X,count)
+** Input: X -- a pointer to an array of unsigned characters.
+** count -- the number of bits of X to use (an unsigned int).
+** Updates MD using the first "count" bits of X.
+** The array pointed to by X is not modified.
+** If count is not a multiple of 8, MD4Update uses high bits of
+** last byte.
+** This is the basic input routine for a user.
+** The routine terminates the MD computation when count < 512, so
+** every MD computation should end with one call to MD4Update with a
+** count less than 512. Zero is OK for a count.
+*/
+extern void MD4Update __P((MD4_CTX *MD, unsigned char *X, unsigned int count));
+
+/* MD4Print(MD)
+** Prints message digest buffer MD as 32 hexadecimal digits.
+** Order is from low-order byte of buffer[0] to high-order byte
+** of buffer[3].
+** Each byte is printed with high-order hexadecimal digit first.
+*/
+extern void MD4Print __P((MD4_CTX *));
+
+/* MD4Final(buf, MD)
+** Returns message digest from MD and terminates the message
+** digest computation.
+*/
+extern void MD4Final __P((unsigned char *, MD4_CTX *));
+
+/*
+** End of md4.h
+****************************(cut)***********************************/
diff --git a/mdk-stage1/ppp/pppd/md5.c b/mdk-stage1/ppp/pppd/md5.c
new file mode 100644
index 000000000..db48023c5
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/md5.c
@@ -0,0 +1,306 @@
+
+
+/*
+ ***********************************************************************
+ ** md5.c -- the source code for MD5 routines **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include "md5.h"
+
+/*
+ ***********************************************************************
+ ** Message-digest routines: **
+ ** To form the message digest for a message M **
+ ** (1) Initialize a context buffer mdContext using MD5Init **
+ ** (2) Call MD5Update on mdContext and M **
+ ** (3) Call MD5Final on mdContext **
+ ** The message digest is now in mdContext->digest[0...15] **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform ();
+
+static unsigned char PADDING[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+ {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+#ifdef __STDC__
+#define UL(x) x##U
+#else
+#define UL(x) x
+#endif
+
+/* The routine MD5Init initializes the message-digest context
+ mdContext. All fields are set to zero.
+ */
+void MD5Init (mdContext)
+MD5_CTX *mdContext;
+{
+ mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+ /* Load magic initialization constants.
+ */
+ mdContext->buf[0] = (UINT4)0x67452301;
+ mdContext->buf[1] = (UINT4)0xefcdab89;
+ mdContext->buf[2] = (UINT4)0x98badcfe;
+ mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+/* The routine MD5Update updates the message-digest context to
+ account for the presence of each of the characters inBuf[0..inLen-1]
+ in the message whose digest is being computed.
+ */
+void MD5Update (mdContext, inBuf, inLen)
+MD5_CTX *mdContext;
+unsigned char *inBuf;
+unsigned int inLen;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+ mdContext->i[1]++;
+ mdContext->i[0] += ((UINT4)inLen << 3);
+ mdContext->i[1] += ((UINT4)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer, increment mdi */
+ mdContext->in[mdi++] = *inBuf++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+ ends with the desired message digest in mdContext->digest[0...15].
+ */
+void MD5Final (hash, mdContext)
+unsigned char hash[];
+MD5_CTX *mdContext;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD5Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+ memcpy(hash, mdContext->digest, 16);
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void Transform (buf, in)
+UINT4 *buf;
+UINT4 *in;
+{
+ UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+ FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */
+ FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */
+ FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */
+ FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */
+ FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */
+ FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */
+ FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */
+ FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */
+ FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */
+ FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */
+ FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */
+ FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */
+ FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */
+ FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */
+ FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */
+ FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */
+
+ /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+ GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */
+ GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */
+ GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */
+ GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */
+ GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */
+ GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */
+ GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */
+ GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */
+ GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */
+ GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */
+ GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */
+ GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */
+ GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */
+ GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */
+ GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */
+ GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */
+
+ /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+ HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */
+ HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */
+ HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */
+ HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */
+ HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */
+ HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */
+ HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */
+ HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */
+ HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */
+ HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */
+ HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */
+ HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */
+ HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */
+ HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */
+ HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */
+ HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */
+
+ /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+ II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */
+ II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */
+ II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */
+ II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */
+ II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */
+ II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */
+ II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */
+ II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */
+ II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */
+ II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */
+ II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */
+ II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */
+ II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */
+ II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */
+ II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */
+ II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ ***********************************************************************
+ ** End of md5.c **
+ ******************************** (cut) ********************************
+ */
diff --git a/mdk-stage1/ppp/pppd/md5.h b/mdk-stage1/ppp/pppd/md5.h
new file mode 100644
index 000000000..7492b2228
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/md5.h
@@ -0,0 +1,58 @@
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5 **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ** -- G modified to have y&~z instead of y&z **
+ ** -- FF, GG, HH modified to add in last register done **
+ ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
+ ** -- distinct additive constant for each step **
+ ** -- round 4 added, working mod 7 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#ifndef __MD5_INCLUDE__
+
+/* typedef a 32-bit type */
+typedef unsigned int UINT4;
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+ UINT4 i[2]; /* number of _bits_ handled mod 2^64 */
+ UINT4 buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5Init ();
+void MD5Update ();
+void MD5Final ();
+
+#define __MD5_INCLUDE__
+#endif /* __MD5_INCLUDE__ */
diff --git a/mdk-stage1/ppp/pppd/multilink.c b/mdk-stage1/ppp/pppd/multilink.c
new file mode 100644
index 000000000..e5f2ac40d
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/multilink.c
@@ -0,0 +1,396 @@
+/*
+ * multilink.c - support routines for multilink.
+ *
+ * Copyright (c) 2000 Paul Mackerras.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms. The name of the author may not be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <errno.h>
+#include <signal.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "tdb.h"
+
+bool endpoint_specified; /* user gave explicit endpoint discriminator */
+char *bundle_id; /* identifier for our bundle */
+
+extern TDB_CONTEXT *pppdb;
+extern char db_key[];
+
+static int get_default_epdisc __P((struct epdisc *));
+static int parse_num __P((char *str, const char *key, int *valp));
+static int owns_unit __P((TDB_DATA pid, int unit));
+
+#define set_ip_epdisc(ep, addr) do { \
+ ep->length = 4; \
+ ep->value[0] = addr >> 24; \
+ ep->value[1] = addr >> 16; \
+ ep->value[2] = addr >> 8; \
+ ep->value[3] = addr; \
+} while (0)
+
+#define LOCAL_IP_ADDR(addr) \
+ (((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \
+ || ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \
+ || ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */
+
+#define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH)
+
+void
+mp_check_options()
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ lcp_options *ao = &lcp_allowoptions[0];
+
+ if (!multilink)
+ return;
+ /* if we're doing multilink, we have to negotiate MRRU */
+ if (!wo->neg_mrru) {
+ /* mrru not specified, default to mru */
+ wo->mrru = wo->mru;
+ wo->neg_mrru = 1;
+ }
+ ao->mrru = ao->mru;
+ ao->neg_mrru = 1;
+
+ if (!wo->neg_endpoint && !noendpoint) {
+ /* get a default endpoint value */
+ wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
+ }
+}
+
+/*
+ * Make a new bundle or join us to an existing bundle
+ * if we are doing multilink.
+ */
+int
+mp_join_bundle()
+{
+ lcp_options *go = &lcp_gotoptions[0];
+ lcp_options *ho = &lcp_hisoptions[0];
+ lcp_options *ao = &lcp_allowoptions[0];
+ int unit, pppd_pid;
+ int l, mtu;
+ char *p;
+ TDB_DATA key, pid, rec;
+
+ if (!go->neg_mrru || !ho->neg_mrru) {
+ /* not doing multilink */
+ if (go->neg_mrru)
+ notice("oops, multilink negotiated only for receive");
+ mtu = ho->neg_mru? ho->mru: PPP_MRU;
+ if (mtu > ao->mru)
+ mtu = ao->mru;
+ if (demand) {
+ /* already have a bundle */
+ cfg_bundle(0, 0, 0, 0);
+ netif_set_mtu(0, mtu);
+ return 0;
+ }
+ make_new_bundle(0, 0, 0, 0);
+ set_ifunit(1);
+ netif_set_mtu(0, mtu);
+ return 0;
+ }
+
+ /*
+ * Find the appropriate bundle or join a new one.
+ * First we make up a name for the bundle.
+ * The length estimate is worst-case assuming every
+ * character has to be quoted.
+ */
+ l = 4 * strlen(peer_authname) + 10;
+ if (ho->neg_endpoint)
+ l += 3 * ho->endpoint.length + 8;
+ if (bundle_name)
+ l += 3 * strlen(bundle_name) + 2;
+ bundle_id = malloc(l);
+ if (bundle_id == 0)
+ novm("bundle identifier");
+
+ p = bundle_id;
+ p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
+ if (ho->neg_endpoint || bundle_name)
+ *p++ = '/';
+ if (ho->neg_endpoint)
+ p += slprintf(p, bundle_id+l-p, "%s",
+ epdisc_to_str(&ho->endpoint));
+ if (bundle_name)
+ p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
+
+ /*
+ * For demand mode, we only need to configure the bundle
+ * and attach the link.
+ */
+ mtu = MIN(ho->mrru, ao->mru);
+ if (demand) {
+ cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+ netif_set_mtu(0, mtu);
+ script_setenv("BUNDLE", bundle_id + 7, 1);
+ return 0;
+ }
+
+ /*
+ * Check if the bundle ID is already in the database.
+ */
+ unit = -1;
+ tdb_writelock(pppdb);
+ key.dptr = bundle_id;
+ key.dsize = p - bundle_id;
+ pid = tdb_fetch(pppdb, key);
+ if (pid.dptr != NULL) {
+ /* bundle ID exists, see if the pppd record exists */
+ rec = tdb_fetch(pppdb, pid);
+ if (rec.dptr != NULL) {
+ /* it is, parse the interface number */
+ parse_num(rec.dptr, "IFNAME=ppp", &unit);
+ /* check the pid value */
+ if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
+ || !process_exists(pppd_pid)
+ || !owns_unit(pid, unit))
+ unit = -1;
+ free(rec.dptr);
+ }
+ free(pid.dptr);
+ }
+
+ if (unit >= 0) {
+ /* attach to existing unit */
+ if (bundle_attach(unit)) {
+ set_ifunit(0);
+ script_setenv("BUNDLE", bundle_id + 7, 0);
+ tdb_writeunlock(pppdb);
+ info("Link attached to %s", ifname);
+ return 1;
+ }
+ /* attach failed because bundle doesn't exist */
+ }
+
+ /* we have to make a new bundle */
+ make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+ set_ifunit(1);
+ netif_set_mtu(0, mtu);
+ script_setenv("BUNDLE", bundle_id + 7, 1);
+ tdb_writeunlock(pppdb);
+ info("New bundle %s created", ifname);
+ return 0;
+}
+
+static int
+parse_num(str, key, valp)
+ char *str;
+ const char *key;
+ int *valp;
+{
+ char *p, *endp;
+ int i;
+
+ p = strstr(str, key);
+ if (p != 0) {
+ p += strlen(key);
+ i = strtol(p, &endp, 10);
+ if (endp != p && (*endp == 0 || *endp == ';')) {
+ *valp = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Check whether the pppd identified by `key' still owns ppp unit `unit'.
+ */
+static int
+owns_unit(key, unit)
+ TDB_DATA key;
+ int unit;
+{
+ char ifkey[32];
+ TDB_DATA kd, vd;
+ int ret = 0;
+
+ slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
+ kd.dptr = ifkey;
+ kd.dsize = strlen(ifkey);
+ vd = tdb_fetch(pppdb, kd);
+ if (vd.dptr != NULL) {
+ ret = vd.dsize == key.dsize
+ && memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
+ free(vd.dptr);
+ }
+ return ret;
+}
+
+static int
+get_default_epdisc(ep)
+ struct epdisc *ep;
+{
+ char *p;
+ struct hostent *hp;
+ u_int32_t addr;
+
+ /* First try for an ethernet MAC address */
+ p = get_first_ethernet();
+ if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) {
+ ep->class = EPD_MAC;
+ ep->length = 6;
+ return 1;
+ }
+
+ /* see if our hostname corresponds to a reasonable IP address */
+ hp = gethostbyname(hostname);
+ if (hp != NULL) {
+ addr = *(u_int32_t *)hp->h_addr;
+ if (!bad_ip_adrs(addr)) {
+ addr = ntohl(addr);
+ if (!LOCAL_IP_ADDR(addr)) {
+ ep->class = EPD_IP;
+ set_ip_epdisc(ep, addr);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * epdisc_to_str - make a printable string from an endpoint discriminator.
+ */
+
+static char *endp_class_names[] = {
+ "null", "local", "IP", "MAC", "magic", "phone"
+};
+
+char *
+epdisc_to_str(ep)
+ struct epdisc *ep;
+{
+ static char str[MAX_ENDP_LEN*3+8];
+ u_char *p = ep->value;
+ int i, mask = 0;
+ char *q, c, c2;
+
+ if (ep->class == EPD_NULL && ep->length == 0)
+ return "null";
+ if (ep->class == EPD_IP && ep->length == 4) {
+ u_int32_t addr;
+
+ GETLONG(addr, p);
+ slprintf(str, sizeof(str), "IP:%I", htonl(addr));
+ return str;
+ }
+
+ c = ':';
+ c2 = '.';
+ if (ep->class == EPD_MAC && ep->length == 6)
+ c2 = ':';
+ else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
+ mask = 3;
+ q = str;
+ if (ep->class <= EPD_PHONENUM)
+ q += slprintf(q, sizeof(str)-1, "%s",
+ endp_class_names[ep->class]);
+ else
+ q += slprintf(q, sizeof(str)-1, "%d", ep->class);
+ c = ':';
+ for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
+ if ((i & mask) == 0) {
+ *q++ = c;
+ c = c2;
+ }
+ q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
+ }
+ return str;
+}
+
+static int hexc_val(int c)
+{
+ if (c >= 'a')
+ return c - 'a' + 10;
+ if (c >= 'A')
+ return c - 'A' + 10;
+ return c - '0';
+}
+
+int
+str_to_epdisc(ep, str)
+ struct epdisc *ep;
+ char *str;
+{
+ int i, l;
+ char *p, *endp;
+
+ for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
+ int sl = strlen(endp_class_names[i]);
+ if (strncasecmp(str, endp_class_names[i], sl) == 0) {
+ str += sl;
+ break;
+ }
+ }
+ if (i > EPD_PHONENUM) {
+ /* not a class name, try a decimal class number */
+ i = strtol(str, &endp, 10);
+ if (endp == str)
+ return 0; /* can't parse class number */
+ str = endp;
+ }
+ ep->class = i;
+ if (*str == 0) {
+ ep->length = 0;
+ return 1;
+ }
+ if (*str != ':' && *str != '.')
+ return 0;
+ ++str;
+
+ if (i == EPD_IP) {
+ u_int32_t addr;
+ i = parse_dotted_ip(str, &addr);
+ if (i == 0 || str[i] != 0)
+ return 0;
+ set_ip_epdisc(ep, addr);
+ return 1;
+ }
+ if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
+ ep->length = 6;
+ return 1;
+ }
+
+ p = str;
+ for (l = 0; l < MAX_ENDP_LEN; ++l) {
+ if (*str == 0)
+ break;
+ if (p <= str)
+ for (p = str; isxdigit(*p); ++p)
+ ;
+ i = p - str;
+ if (i == 0)
+ return 0;
+ ep->value[l] = hexc_val(*str++);
+ if ((i & 1) == 0)
+ ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
+ if (*str == ':' || *str == '.')
+ ++str;
+ }
+ if (*str != 0 || (ep->class == EPD_MAC && l != 6))
+ return 0;
+ ep->length = l;
+ return 1;
+}
+
diff --git a/mdk-stage1/ppp/pppd/options.c b/mdk-stage1/ppp/pppd/options.c
new file mode 100644
index 000000000..9ebac372e
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/options.c
@@ -0,0 +1,1513 @@
+/*
+ * options.c - handles option processing for PPP.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <pwd.h>
+#ifdef PLUGIN
+#include <dlfcn.h>
+#endif
+#ifdef PPP_FILTER
+#include <pcap.h>
+#include <pcap-int.h> /* XXX: To get struct pcap */
+#endif
+
+#include "pppd.h"
+#include "pathnames.h"
+
+#if defined(ultrix) || defined(NeXT)
+char *strdup __P((char *));
+#endif
+
+static const char rcsid[] = RCSID;
+
+struct option_value {
+ struct option_value *next;
+ const char *source;
+ char value[1];
+};
+
+/*
+ * Option variables and default values.
+ */
+#ifdef PPP_FILTER
+int dflag = 0; /* Tell libpcap we want debugging */
+#endif
+int debug = 0; /* Debug flag */
+int kdebugflag = 0; /* Tell kernel to print debug messages */
+int default_device = 1; /* Using /dev/tty or equivalent */
+char devnam[MAXPATHLEN]; /* Device name */
+bool nodetach = 0; /* Don't detach from controlling tty */
+bool updetach = 0; /* Detach once link is up */
+int maxconnect = 0; /* Maximum connect time */
+char user[MAXNAMELEN]; /* Username for PAP */
+char passwd[MAXSECRETLEN]; /* Password for PAP */
+bool persist = 0; /* Reopen link after it goes down */
+char our_name[MAXNAMELEN]; /* Our name for authentication purposes */
+bool demand = 0; /* do dial-on-demand */
+char *ipparam = NULL; /* Extra parameter for ip up/down scripts */
+int idle_time_limit = 0; /* Disconnect if idle for this many seconds */
+int holdoff = 30; /* # seconds to pause before reconnecting */
+bool holdoff_specified; /* true if a holdoff value has been given */
+int log_to_fd = 1; /* send log messages to this fd too */
+bool log_default = 1; /* log_to_fd is default (stdout) */
+int maxfail = 10; /* max # of unsuccessful connection attempts */
+char linkname[MAXPATHLEN]; /* logical name for link */
+bool tune_kernel; /* may alter kernel settings */
+int connect_delay = 1000; /* wait this many ms after connect script */
+int req_unit = -1; /* requested interface unit */
+bool multilink = 0; /* Enable multilink operation */
+char *bundle_name = NULL; /* bundle name for multilink */
+bool dump_options; /* print out option values */
+bool dryrun; /* print out option values and exit */
+char *domain; /* domain name set by domain option */
+
+extern option_t auth_options[];
+extern struct stat devstat;
+
+#ifdef PPP_FILTER
+struct bpf_program pass_filter;/* Filter program for packets to pass */
+struct bpf_program active_filter; /* Filter program for link-active pkts */
+pcap_t pc; /* Fake struct pcap so we can compile expr */
+#endif
+
+char *current_option; /* the name of the option being parsed */
+int privileged_option; /* set iff the current option came from root */
+char *option_source; /* string saying where the option came from */
+int option_priority = OPRIO_CFGFILE; /* priority of the current options */
+bool devnam_fixed; /* can no longer change device name */
+
+static int logfile_fd = -1; /* fd opened for log file */
+static char logfile_name[MAXPATHLEN]; /* name of log file */
+
+/*
+ * Prototypes
+ */
+static int setdomain __P((char **));
+static int readfile __P((char **));
+static int callfile __P((char **));
+static int showversion __P((char **));
+static int showhelp __P((char **));
+static void usage __P((void));
+static int setlogfile __P((char **));
+#ifdef PLUGIN
+static int loadplugin __P((char **));
+#endif
+
+#ifdef PPP_FILTER
+static int setpassfilter __P((char **));
+static int setactivefilter __P((char **));
+#endif
+
+static option_t *find_option __P((const char *name));
+static int process_option __P((option_t *, char *, char **));
+static int n_arguments __P((option_t *));
+static int number_option __P((char *, u_int32_t *, int));
+
+/*
+ * Structure to store extra lists of options.
+ */
+struct option_list {
+ option_t *options;
+ struct option_list *next;
+};
+
+static struct option_list *extra_options = NULL;
+
+/*
+ * Valid arguments.
+ */
+option_t general_options[] = {
+ { "debug", o_int, &debug,
+ "Increase debugging level", OPT_INC | OPT_NOARG | 1 },
+ { "-d", o_int, &debug,
+ "Increase debugging level",
+ OPT_ALIAS | OPT_INC | OPT_NOARG | 1 },
+
+ { "kdebug", o_int, &kdebugflag,
+ "Set kernel driver debug level", OPT_PRIO },
+
+ { "nodetach", o_bool, &nodetach,
+ "Don't detach from controlling tty", OPT_PRIO | 1 },
+ { "-detach", o_bool, &nodetach,
+ "Don't detach from controlling tty", OPT_ALIAS | OPT_PRIOSUB | 1 },
+ { "updetach", o_bool, &updetach,
+ "Detach from controlling tty once link is up",
+ OPT_PRIOSUB | OPT_A2CLR | 1, &nodetach },
+
+ { "holdoff", o_int, &holdoff,
+ "Set time in seconds before retrying connection", OPT_PRIO },
+
+ { "idle", o_int, &idle_time_limit,
+ "Set time in seconds before disconnecting idle link", OPT_PRIO },
+
+ { "maxconnect", o_int, &maxconnect,
+ "Set connection time limit",
+ OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF },
+
+ { "domain", o_special, (void *)setdomain,
+ "Add given domain name to hostname",
+ OPT_PRIO | OPT_PRIV | OPT_A2STRVAL, &domain },
+
+ { "file", o_special, (void *)readfile,
+ "Take options from a file", OPT_NOPRINT },
+ { "call", o_special, (void *)callfile,
+ "Take options from a privileged file", OPT_NOPRINT },
+
+ { "persist", o_bool, &persist,
+ "Keep on reopening connection after close", OPT_PRIO | 1 },
+ { "nopersist", o_bool, &persist,
+ "Turn off persist option", OPT_PRIOSUB },
+
+ { "demand", o_bool, &demand,
+ "Dial on demand", OPT_INITONLY | 1, &persist },
+
+ { "--version", o_special_noarg, (void *)showversion,
+ "Show version number" },
+ { "--help", o_special_noarg, (void *)showhelp,
+ "Show brief listing of options" },
+ { "-h", o_special_noarg, (void *)showhelp,
+ "Show brief listing of options", OPT_ALIAS },
+
+ { "logfile", o_special, (void *)setlogfile,
+ "Append log messages to this file",
+ OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, &logfile_name },
+ { "logfd", o_int, &log_to_fd,
+ "Send log messages to this file descriptor",
+ OPT_PRIOSUB | OPT_A2CLR, &log_default },
+ { "nolog", o_int, &log_to_fd,
+ "Don't send log messages to any file",
+ OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) },
+ { "nologfd", o_int, &log_to_fd,
+ "Don't send log messages to any file descriptor",
+ OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
+
+ { "linkname", o_string, linkname,
+ "Set logical name for link",
+ OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXPATHLEN },
+
+ { "maxfail", o_int, &maxfail,
+ "Maximum number of unsuccessful connection attempts to allow",
+ OPT_PRIO },
+
+ { "ktune", o_bool, &tune_kernel,
+ "Alter kernel settings as necessary", OPT_PRIO | 1 },
+ { "noktune", o_bool, &tune_kernel,
+ "Don't alter kernel settings", OPT_PRIOSUB },
+
+ { "connect-delay", o_int, &connect_delay,
+ "Maximum time (in ms) to wait after connect script finishes",
+ OPT_PRIO },
+
+ { "unit", o_int, &req_unit,
+ "PPP interface unit number to use if possible",
+ OPT_PRIO | OPT_LLIMIT, 0, 0 },
+
+ { "dump", o_bool, &dump_options,
+ "Print out option values after parsing all options", 1 },
+ { "dryrun", o_bool, &dryrun,
+ "Stop after parsing, printing, and checking options", 1 },
+
+#ifdef HAVE_MULTILINK
+ { "multilink", o_bool, &multilink,
+ "Enable multilink operation", OPT_PRIO | 1 },
+ { "mp", o_bool, &multilink,
+ "Enable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 1 },
+ { "nomultilink", o_bool, &multilink,
+ "Disable multilink operation", OPT_PRIOSUB | 0 },
+ { "nomp", o_bool, &multilink,
+ "Disable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 0 },
+
+ { "bundle", o_string, &bundle_name,
+ "Bundle name for multilink", OPT_PRIO },
+#endif /* HAVE_MULTILINK */
+
+#ifdef PLUGIN
+ { "plugin", o_special, (void *)loadplugin,
+ "Load a plug-in module into pppd", OPT_PRIV | OPT_A2LIST },
+#endif
+
+#ifdef PPP_FILTER
+ { "pdebug", o_int, &dflag,
+ "libpcap debugging", OPT_PRIO },
+
+ { "pass-filter", 1, setpassfilter,
+ "set filter for packets to pass", OPT_PRIO },
+
+ { "active-filter", 1, setactivefilter,
+ "set filter for active pkts", OPT_PRIO },
+#endif
+
+ { NULL }
+};
+
+#ifndef IMPLEMENTATION
+#define IMPLEMENTATION ""
+#endif
+
+static char *usage_string = "\
+pppd version %s\n\
+Usage: %s [ options ], where options are:\n\
+ <device> Communicate over the named device\n\
+ <speed> Set the baud rate to <speed>\n\
+ <loc>:<rem> Set the local and/or remote interface IP\n\
+ addresses. Either one may be omitted.\n\
+ asyncmap <n> Set the desired async map to hex <n>\n\
+ auth Require authentication from peer\n\
+ connect <p> Invoke shell command <p> to set up the serial line\n\
+ crtscts Use hardware RTS/CTS flow control\n\
+ defaultroute Add default route through interface\n\
+ file <f> Take options from file <f>\n\
+ modem Use modem control lines\n\
+ mru <n> Set MRU value to <n> for negotiation\n\
+See pppd(8) for more options.\n\
+";
+
+/*
+ * parse_args - parse a string of arguments from the command line.
+ */
+int
+parse_args(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *arg;
+ option_t *opt;
+ int n;
+
+ privileged_option = privileged;
+ option_source = "command line";
+ option_priority = OPRIO_CMDLINE;
+ while (argc > 0) {
+ arg = *argv++;
+ --argc;
+ opt = find_option(arg);
+ if (opt == NULL) {
+ option_error("unrecognized option '%s'", arg);
+ usage();
+ return 0;
+ }
+ n = n_arguments(opt);
+ if (argc < n) {
+ option_error("too few parameters for option %s", arg);
+ return 0;
+ }
+ if (!process_option(opt, arg, argv))
+ return 0;
+ argc -= n;
+ argv += n;
+ }
+ return 1;
+}
+
+/*
+ * options_from_file - Read a string of options from a file,
+ * and interpret them.
+ */
+int
+options_from_file(filename, must_exist, check_prot, priv)
+ char *filename;
+ int must_exist;
+ int check_prot;
+ int priv;
+{
+ FILE *f;
+ int i, newline, ret, err;
+ option_t *opt;
+ int oldpriv, n;
+ char *oldsource;
+ char *argv[MAXARGS];
+ char args[MAXARGS][MAXWORDLEN];
+ char cmd[MAXWORDLEN];
+
+ if (check_prot)
+ seteuid(getuid());
+ f = fopen(filename, "r");
+ err = errno;
+ if (check_prot)
+ seteuid(0);
+ if (f == NULL) {
+ errno = err;
+ if (!must_exist) {
+ if (err != ENOENT && err != ENOTDIR)
+ warn("Warning: can't open options file %s: %m", filename);
+ return 1;
+ }
+ option_error("Can't open options file %s: %m", filename);
+ return 0;
+ }
+
+ oldpriv = privileged_option;
+ privileged_option = priv;
+ oldsource = option_source;
+ option_source = strdup(filename);
+ if (option_source == NULL)
+ option_source = "file";
+ ret = 0;
+ while (getword(f, cmd, &newline, filename)) {
+ opt = find_option(cmd);
+ if (opt == NULL) {
+ option_error("In file %s: unrecognized option '%s'",
+ filename, cmd);
+ goto err;
+ }
+ n = n_arguments(opt);
+ for (i = 0; i < n; ++i) {
+ if (!getword(f, args[i], &newline, filename)) {
+ option_error(
+ "In file %s: too few parameters for option '%s'",
+ filename, cmd);
+ goto err;
+ }
+ argv[i] = args[i];
+ }
+ if (!process_option(opt, cmd, argv))
+ goto err;
+ }
+ ret = 1;
+
+err:
+ fclose(f);
+ privileged_option = oldpriv;
+ option_source = oldsource;
+ return ret;
+}
+
+/*
+ * options_from_user - See if the use has a ~/.ppprc file,
+ * and if so, interpret options from it.
+ */
+int
+options_from_user()
+{
+ char *user, *path, *file;
+ int ret;
+ struct passwd *pw;
+ size_t pl;
+
+ pw = getpwuid(getuid());
+ if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0)
+ return 1;
+ file = _PATH_USEROPT;
+ pl = strlen(user) + strlen(file) + 2;
+ path = malloc(pl);
+ if (path == NULL)
+ novm("init file name");
+ slprintf(path, pl, "%s/%s", user, file);
+ option_priority = OPRIO_CFGFILE;
+ ret = options_from_file(path, 0, 1, privileged);
+ free(path);
+ return ret;
+}
+
+/*
+ * options_for_tty - See if an options file exists for the serial
+ * device, and if so, interpret options from it.
+ * We only allow the per-tty options file to override anything from
+ * the command line if it is something that the user can't override
+ * once it has been set by root; this is done by giving configuration
+ * files a lower priority than the command line.
+ */
+int
+options_for_tty()
+{
+ char *dev, *path, *p;
+ int ret;
+ size_t pl;
+
+ dev = devnam;
+ if (strncmp(dev, "/dev/", 5) == 0)
+ dev += 5;
+ if (dev[0] == 0 || strcmp(dev, "tty") == 0)
+ return 1; /* don't look for /etc/ppp/options.tty */
+ pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1;
+ path = malloc(pl);
+ if (path == NULL)
+ novm("tty init file name");
+ slprintf(path, pl, "%s%s", _PATH_TTYOPT, dev);
+ /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
+ for (p = path + strlen(_PATH_TTYOPT); *p != 0; ++p)
+ if (*p == '/')
+ *p = '.';
+ option_priority = OPRIO_CFGFILE;
+ ret = options_from_file(path, 0, 0, 1);
+ free(path);
+ return ret;
+}
+
+/*
+ * options_from_list - process a string of options in a wordlist.
+ */
+int
+options_from_list(w, priv)
+ struct wordlist *w;
+ int priv;
+{
+ char *argv[MAXARGS];
+ option_t *opt;
+ int i, n, ret = 0;
+ struct wordlist *w0;
+
+ privileged_option = priv;
+ option_source = "secrets file";
+ option_priority = OPRIO_SECFILE;
+
+ while (w != NULL) {
+ opt = find_option(w->word);
+ if (opt == NULL) {
+ option_error("In secrets file: unrecognized option '%s'",
+ w->word);
+ goto err;
+ }
+ n = n_arguments(opt);
+ w0 = w;
+ for (i = 0; i < n; ++i) {
+ w = w->next;
+ if (w == NULL) {
+ option_error(
+ "In secrets file: too few parameters for option '%s'",
+ w0->word);
+ goto err;
+ }
+ argv[i] = w->word;
+ }
+ if (!process_option(opt, w0->word, argv))
+ goto err;
+ w = w->next;
+ }
+ ret = 1;
+
+err:
+ return ret;
+}
+
+/*
+ * match_option - see if this option matches an option_t structure.
+ */
+static int
+match_option(name, opt, dowild)
+ char *name;
+ option_t *opt;
+ int dowild;
+{
+ int (*match) __P((char *, char **, int));
+
+ if (dowild != (opt->type == o_wild))
+ return 0;
+ if (!dowild)
+ return strcmp(name, opt->name) == 0;
+ match = (int (*) __P((char *, char **, int))) opt->addr;
+ return (*match)(name, NULL, 0);
+}
+
+/*
+ * find_option - scan the option lists for the various protocols
+ * looking for an entry with the given name.
+ * This could be optimized by using a hash table.
+ */
+static option_t *
+find_option(name)
+ const char *name;
+{
+ option_t *opt;
+ struct option_list *list;
+ int i, dowild;
+
+ for (dowild = 0; dowild <= 1; ++dowild) {
+ for (opt = general_options; opt->name != NULL; ++opt)
+ if (match_option(name, opt, dowild))
+ return opt;
+ for (opt = auth_options; opt->name != NULL; ++opt)
+ if (match_option(name, opt, dowild))
+ return opt;
+ for (list = extra_options; list != NULL; list = list->next)
+ for (opt = list->options; opt->name != NULL; ++opt)
+ if (match_option(name, opt, dowild))
+ return opt;
+ for (opt = the_channel->options; opt->name != NULL; ++opt)
+ if (match_option(name, opt, dowild))
+ return opt;
+ for (i = 0; protocols[i] != NULL; ++i)
+ if ((opt = protocols[i]->options) != NULL)
+ for (; opt->name != NULL; ++opt)
+ if (match_option(name, opt, dowild))
+ return opt;
+ }
+ return NULL;
+}
+
+/*
+ * process_option - process one new-style option.
+ */
+static int
+process_option(opt, cmd, argv)
+ option_t *opt;
+ char *cmd;
+ char **argv;
+{
+ u_int32_t v;
+ int iv, a;
+ char *sv;
+ int (*parser) __P((char **));
+ int (*wildp) __P((char *, char **, int));
+ char *optopt = (opt->type == o_wild)? "": " option";
+ int prio = option_priority;
+ option_t *mainopt = opt;
+
+ if ((opt->flags & OPT_PRIVFIX) && privileged_option)
+ prio += OPRIO_ROOT;
+ while (mainopt->flags & OPT_PRIOSUB)
+ --mainopt;
+ if (mainopt->flags & OPT_PRIO) {
+ if (prio < mainopt->priority) {
+ /* new value doesn't override old */
+ if (prio == OPRIO_CMDLINE && mainopt->priority > OPRIO_ROOT) {
+ option_error("%s%s set in %s cannot be overridden\n",
+ opt->name, optopt, mainopt->source);
+ return 0;
+ }
+ return 1;
+ }
+ if (prio > OPRIO_ROOT && mainopt->priority == OPRIO_CMDLINE)
+ warn("%s%s from %s overrides command line",
+ opt->name, optopt, option_source);
+ }
+
+ if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) {
+ option_error("%s%s cannot be changed after initialization",
+ opt->name, optopt);
+ return 0;
+ }
+ if ((opt->flags & OPT_PRIV) && !privileged_option) {
+ option_error("using the %s%s requires root privilege",
+ opt->name, optopt);
+ return 0;
+ }
+ if ((opt->flags & OPT_ENABLE) && *(bool *)(opt->addr2) == 0) {
+ option_error("%s%s is disabled", opt->name, optopt);
+ return 0;
+ }
+ if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) {
+ option_error("the %s%s may not be changed in %s",
+ opt->name, optopt, option_source);
+ return 0;
+ }
+
+ switch (opt->type) {
+ case o_bool:
+ v = opt->flags & OPT_VALUE;
+ *(bool *)(opt->addr) = v;
+ if (opt->addr2 && (opt->flags & OPT_A2COPY))
+ *(bool *)(opt->addr2) = v;
+ break;
+
+ case o_int:
+ iv = 0;
+ if ((opt->flags & OPT_NOARG) == 0) {
+ if (!int_option(*argv, &iv))
+ return 0;
+ if ((((opt->flags & OPT_LLIMIT) && iv < opt->lower_limit)
+ || ((opt->flags & OPT_ULIMIT) && iv > opt->upper_limit))
+ && !((opt->flags & OPT_ZEROOK && iv == 0))) {
+ char *zok = (opt->flags & OPT_ZEROOK)? " zero or": "";
+ switch (opt->flags & OPT_LIMITS) {
+ case OPT_LLIMIT:
+ option_error("%s value must be%s >= %d",
+ opt->name, zok, opt->lower_limit);
+ break;
+ case OPT_ULIMIT:
+ option_error("%s value must be%s <= %d",
+ opt->name, zok, opt->upper_limit);
+ break;
+ case OPT_LIMITS:
+ option_error("%s value must be%s between %d and %d",
+ opt->name, opt->lower_limit, opt->upper_limit);
+ break;
+ }
+ return 0;
+ }
+ }
+ a = opt->flags & OPT_VALUE;
+ if (a >= 128)
+ a -= 256; /* sign extend */
+ iv += a;
+ if (opt->flags & OPT_INC)
+ iv += *(int *)(opt->addr);
+ if ((opt->flags & OPT_NOINCR) && !privileged_option) {
+ int oldv = *(int *)(opt->addr);
+ if ((opt->flags & OPT_ZEROINF) ?
+ (oldv != 0 && (iv == 0 || iv > oldv)) : (iv > oldv)) {
+ option_error("%s value cannot be increased", opt->name);
+ return 0;
+ }
+ }
+ *(int *)(opt->addr) = iv;
+ if (opt->addr2 && (opt->flags & OPT_A2COPY))
+ *(int *)(opt->addr2) = iv;
+ break;
+
+ case o_uint32:
+ if (opt->flags & OPT_NOARG) {
+ v = opt->flags & OPT_VALUE;
+ if (v & 0x80)
+ v |= 0xffffff00U;
+ } else if (!number_option(*argv, &v, 16))
+ return 0;
+ if (opt->flags & OPT_OR)
+ v |= *(u_int32_t *)(opt->addr);
+ *(u_int32_t *)(opt->addr) = v;
+ if (opt->addr2 && (opt->flags & OPT_A2COPY))
+ *(u_int32_t *)(opt->addr2) = v;
+ break;
+
+ case o_string:
+ if (opt->flags & OPT_STATIC) {
+ strlcpy((char *)(opt->addr), *argv, opt->upper_limit);
+ } else {
+ sv = strdup(*argv);
+ if (sv == NULL)
+ novm("option argument");
+ *(char **)(opt->addr) = sv;
+ }
+ break;
+
+ case o_special_noarg:
+ case o_special:
+ parser = (int (*) __P((char **))) opt->addr;
+ if (!(*parser)(argv))
+ return 0;
+ if (opt->flags & OPT_A2LIST) {
+ struct option_value *ovp, **pp;
+
+ ovp = malloc(sizeof(*ovp) + strlen(*argv));
+ if (ovp != 0) {
+ strcpy(ovp->value, *argv);
+ ovp->source = option_source;
+ ovp->next = NULL;
+ pp = (struct option_value **) &opt->addr2;
+ while (*pp != 0)
+ pp = &(*pp)->next;
+ *pp = ovp;
+ }
+ }
+ break;
+
+ case o_wild:
+ wildp = (int (*) __P((char *, char **, int))) opt->addr;
+ if (!(*wildp)(cmd, argv, 1))
+ return 0;
+ break;
+ }
+
+ if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE
+ |OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST)) == 0)
+ *(bool *)(opt->addr2) = !(opt->flags & OPT_A2CLR);
+
+ mainopt->source = option_source;
+ mainopt->priority = prio;
+ mainopt->winner = opt - mainopt;
+
+ return 1;
+}
+
+/*
+ * override_value - if the option priorities would permit us to
+ * override the value of option, return 1 and update the priority
+ * and source of the option value. Otherwise returns 0.
+ */
+int
+override_value(option, priority, source)
+ const char *option;
+ int priority;
+ const char *source;
+{
+ option_t *opt;
+
+ opt = find_option(option);
+ if (opt == NULL)
+ return 0;
+ while (opt->flags & OPT_PRIOSUB)
+ --opt;
+ if ((opt->flags & OPT_PRIO) && priority < opt->priority)
+ return 0;
+ opt->priority = priority;
+ opt->source = source;
+ opt->winner = -1;
+ return 1;
+}
+
+/*
+ * n_arguments - tell how many arguments an option takes
+ */
+static int
+n_arguments(opt)
+ option_t *opt;
+{
+ return (opt->type == o_bool || opt->type == o_special_noarg
+ || (opt->flags & OPT_NOARG))? 0: 1;
+}
+
+/*
+ * add_options - add a list of options to the set we grok.
+ */
+void
+add_options(opt)
+ option_t *opt;
+{
+ struct option_list *list;
+
+ list = malloc(sizeof(*list));
+ if (list == 0)
+ novm("option list entry");
+ list->options = opt;
+ list->next = extra_options;
+ extra_options = list;
+}
+
+/*
+ * check_options - check that options are valid and consistent.
+ */
+void
+check_options()
+{
+ if (logfile_fd >= 0 && logfile_fd != log_to_fd)
+ close(logfile_fd);
+}
+
+/*
+ * print_option - print out an option and its value
+ */
+static void
+print_option(opt, mainopt, printer, arg)
+ option_t *opt, *mainopt;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int i, v;
+ char *p;
+
+ if (opt->flags & OPT_NOPRINT)
+ return;
+ switch (opt->type) {
+ case o_bool:
+ v = opt->flags & OPT_VALUE;
+ if (*(bool *)opt->addr != v)
+ /* this can happen legitimately, e.g. lock
+ option turned off for default device */
+ break;
+ printer(arg, "%s", opt->name);
+ break;
+ case o_int:
+ v = opt->flags & OPT_VALUE;
+ if (v >= 128)
+ v -= 256;
+ i = *(int *)opt->addr;
+ if (opt->flags & OPT_NOARG) {
+ printer(arg, "%s", opt->name);
+ if (i != v) {
+ if (opt->flags & OPT_INC) {
+ for (; i > v; i -= v)
+ printer(arg, " %s", opt->name);
+ } else
+ printer(arg, " # oops: %d not %d\n",
+ i, v);
+ }
+ } else {
+ printer(arg, "%s %d", opt->name, i);
+ }
+ break;
+ case o_uint32:
+ printer(arg, "%s", opt->name);
+ if ((opt->flags & OPT_NOARG) == 0)
+ printer(arg, " %x", *(u_int32_t *)opt->addr);
+ break;
+
+ case o_string:
+ if (opt->flags & OPT_HIDE) {
+ p = "??????";
+ } else {
+ p = (char *) opt->addr;
+ if ((opt->flags & OPT_STATIC) == 0)
+ p = *(char **)p;
+ }
+ printer(arg, "%s %q", opt->name, p);
+ break;
+
+ case o_special:
+ case o_special_noarg:
+ case o_wild:
+ if (opt->type != o_wild) {
+ printer(arg, "%s", opt->name);
+ if (n_arguments(opt) == 0)
+ break;
+ printer(arg, " ");
+ }
+ if (opt->flags & OPT_A2PRINTER) {
+ void (*oprt) __P((option_t *,
+ void ((*)__P((void *, char *, ...))),
+ void *));
+ oprt = opt->addr2;
+ (*oprt)(opt, printer, arg);
+ } else if (opt->flags & OPT_A2STRVAL) {
+ p = (char *) opt->addr2;
+ if ((opt->flags & OPT_STATIC) == 0)
+ p = *(char **)p;
+ printer("%q", p);
+ } else if (opt->flags & OPT_A2LIST) {
+ struct option_value *ovp;
+
+ ovp = (struct option_value *) opt->addr2;
+ for (;;) {
+ printer(arg, "%q", ovp->value);
+ if ((ovp = ovp->next) == NULL)
+ break;
+ printer(arg, "\t\t# (from %s)\n%s ",
+ ovp->source, opt->name);
+ }
+ } else {
+ printer(arg, "xxx # [don't know how to print value]");
+ }
+ break;
+
+ default:
+ printer(arg, "# %s value (type %d??)", opt->name, opt->type);
+ break;
+ }
+ printer(arg, "\t\t# (from %s)\n", mainopt->source);
+}
+
+/*
+ * print_option_list - print out options in effect from an
+ * array of options.
+ */
+static void
+print_option_list(opt, printer, arg)
+ option_t *opt;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ while (opt->name != NULL) {
+ if (opt->priority != OPRIO_DEFAULT
+ && opt->winner != (short int) -1)
+ print_option(opt + opt->winner, opt, printer, arg);
+ do {
+ ++opt;
+ } while (opt->flags & OPT_PRIOSUB);
+ }
+}
+
+/*
+ * print_options - print out what options are in effect.
+ */
+void
+print_options(printer, arg)
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ struct option_list *list;
+ int i;
+
+ printer(arg, "pppd options in effect:\n");
+ print_option_list(general_options, printer, arg);
+ print_option_list(auth_options, printer, arg);
+ for (list = extra_options; list != NULL; list = list->next)
+ print_option_list(list->options, printer, arg);
+ print_option_list(the_channel->options, printer, arg);
+ for (i = 0; protocols[i] != NULL; ++i)
+ print_option_list(protocols[i]->options, printer, arg);
+}
+
+/*
+ * usage - print out a message telling how to use the program.
+ */
+static void
+usage()
+{
+ if (phase == PHASE_INITIALIZE)
+ fprintf(stderr, usage_string, VERSION, progname);
+}
+
+/*
+ * showhelp - print out usage message and exit.
+ */
+static int
+showhelp(argv)
+ char **argv;
+{
+ if (phase == PHASE_INITIALIZE) {
+ usage();
+ exit(0);
+ }
+ return 0;
+}
+
+/*
+ * showversion - print out the version number and exit.
+ */
+static int
+showversion(argv)
+ char **argv;
+{
+ if (phase == PHASE_INITIALIZE) {
+ fprintf(stderr, "pppd version %s\n", VERSION);
+ exit(0);
+ }
+ return 0;
+}
+
+/*
+ * option_error - print a message about an error in an option.
+ * The message is logged, and also sent to
+ * stderr if phase == PHASE_INITIALIZE.
+ */
+void
+option_error __V((char *fmt, ...))
+{
+ va_list args;
+ char buf[1024];
+
+#if defined(__STDC__)
+ va_start(args, fmt);
+#else
+ char *fmt;
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vslprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ if (phase == PHASE_INITIALIZE)
+ fprintf(stderr, "%s: %s\n", progname, buf);
+ syslog(LOG_ERR, "%s", buf);
+}
+
+#if 0
+/*
+ * readable - check if a file is readable by the real user.
+ */
+int
+readable(fd)
+ int fd;
+{
+ uid_t uid;
+ int i;
+ struct stat sbuf;
+
+ uid = getuid();
+ if (uid == 0)
+ return 1;
+ if (fstat(fd, &sbuf) != 0)
+ return 0;
+ if (sbuf.st_uid == uid)
+ return sbuf.st_mode & S_IRUSR;
+ if (sbuf.st_gid == getgid())
+ return sbuf.st_mode & S_IRGRP;
+ for (i = 0; i < ngroups; ++i)
+ if (sbuf.st_gid == groups[i])
+ return sbuf.st_mode & S_IRGRP;
+ return sbuf.st_mode & S_IROTH;
+}
+#endif
+
+/*
+ * Read a word from a file.
+ * Words are delimited by white-space or by quotes (" or ').
+ * Quotes, white-space and \ may be escaped with \.
+ * \<newline> is ignored.
+ */
+int
+getword(f, word, newlinep, filename)
+ FILE *f;
+ char *word;
+ int *newlinep;
+ char *filename;
+{
+ int c, len, escape;
+ int quoted, comment;
+ int value, digit, got, n;
+
+#define isoctal(c) ((c) >= '0' && (c) < '8')
+
+ *newlinep = 0;
+ len = 0;
+ escape = 0;
+ comment = 0;
+
+ /*
+ * First skip white-space and comments.
+ */
+ for (;;) {
+ c = getc(f);
+ if (c == EOF)
+ break;
+
+ /*
+ * A newline means the end of a comment; backslash-newline
+ * is ignored. Note that we cannot have escape && comment.
+ */
+ if (c == '\n') {
+ if (!escape) {
+ *newlinep = 1;
+ comment = 0;
+ } else
+ escape = 0;
+ continue;
+ }
+
+ /*
+ * Ignore characters other than newline in a comment.
+ */
+ if (comment)
+ continue;
+
+ /*
+ * If this character is escaped, we have a word start.
+ */
+ if (escape)
+ break;
+
+ /*
+ * If this is the escape character, look at the next character.
+ */
+ if (c == '\\') {
+ escape = 1;
+ continue;
+ }
+
+ /*
+ * If this is the start of a comment, ignore the rest of the line.
+ */
+ if (c == '#') {
+ comment = 1;
+ continue;
+ }
+
+ /*
+ * A non-whitespace character is the start of a word.
+ */
+ if (!isspace(c))
+ break;
+ }
+
+ /*
+ * Save the delimiter for quoted strings.
+ */
+ if (!escape && (c == '"' || c == '\'')) {
+ quoted = c;
+ c = getc(f);
+ } else
+ quoted = 0;
+
+ /*
+ * Process characters until the end of the word.
+ */
+ while (c != EOF) {
+ if (escape) {
+ /*
+ * This character is escaped: backslash-newline is ignored,
+ * various other characters indicate particular values
+ * as for C backslash-escapes.
+ */
+ escape = 0;
+ if (c == '\n') {
+ c = getc(f);
+ continue;
+ }
+
+ got = 0;
+ switch (c) {
+ case 'a':
+ value = '\a';
+ break;
+ case 'b':
+ value = '\b';
+ break;
+ case 'f':
+ value = '\f';
+ break;
+ case 'n':
+ value = '\n';
+ break;
+ case 'r':
+ value = '\r';
+ break;
+ case 's':
+ value = ' ';
+ break;
+ case 't':
+ value = '\t';
+ break;
+
+ default:
+ if (isoctal(c)) {
+ /*
+ * \ddd octal sequence
+ */
+ value = 0;
+ for (n = 0; n < 3 && isoctal(c); ++n) {
+ value = (value << 3) + (c & 07);
+ c = getc(f);
+ }
+ got = 1;
+ break;
+ }
+
+ if (c == 'x') {
+ /*
+ * \x<hex_string> sequence
+ */
+ value = 0;
+ c = getc(f);
+ for (n = 0; n < 2 && isxdigit(c); ++n) {
+ digit = toupper(c) - '0';
+ if (digit > 10)
+ digit += '0' + 10 - 'A';
+ value = (value << 4) + digit;
+ c = getc (f);
+ }
+ got = 1;
+ break;
+ }
+
+ /*
+ * Otherwise the character stands for itself.
+ */
+ value = c;
+ break;
+ }
+
+ /*
+ * Store the resulting character for the escape sequence.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = value;
+ ++len;
+
+ if (!got)
+ c = getc(f);
+ continue;
+
+ }
+
+ /*
+ * Not escaped: see if we've reached the end of the word.
+ */
+ if (quoted) {
+ if (c == quoted)
+ break;
+ } else {
+ if (isspace(c) || c == '#') {
+ ungetc (c, f);
+ break;
+ }
+ }
+
+ /*
+ * Backslash starts an escape sequence.
+ */
+ if (c == '\\') {
+ escape = 1;
+ c = getc(f);
+ continue;
+ }
+
+ /*
+ * An ordinary character: store it in the word and get another.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = c;
+ ++len;
+
+ c = getc(f);
+ }
+
+ /*
+ * End of the word: check for errors.
+ */
+ if (c == EOF) {
+ if (ferror(f)) {
+ if (errno == 0)
+ errno = EIO;
+ option_error("Error reading %s: %m", filename);
+ die(1);
+ }
+ /*
+ * If len is zero, then we didn't find a word before the
+ * end of the file.
+ */
+ if (len == 0)
+ return 0;
+ }
+
+ /*
+ * Warn if the word was too long, and append a terminating null.
+ */
+ if (len >= MAXWORDLEN) {
+ option_error("warning: word in file %s too long (%.20s...)",
+ filename, word);
+ len = MAXWORDLEN - 1;
+ }
+ word[len] = 0;
+
+ return 1;
+
+#undef isoctal
+
+}
+
+/*
+ * number_option - parse an unsigned numeric parameter for an option.
+ */
+static int
+number_option(str, valp, base)
+ char *str;
+ u_int32_t *valp;
+ int base;
+{
+ char *ptr;
+
+ *valp = strtoul(str, &ptr, base);
+ if (ptr == str) {
+ option_error("invalid numeric parameter '%s' for %s option",
+ str, current_option);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * int_option - like number_option, but valp is int *,
+ * the base is assumed to be 0, and *valp is not changed
+ * if there is an error.
+ */
+int
+int_option(str, valp)
+ char *str;
+ int *valp;
+{
+ u_int32_t v;
+
+ if (!number_option(str, &v, 0))
+ return 0;
+ *valp = (int) v;
+ return 1;
+}
+
+
+/*
+ * The following procedures parse options.
+ */
+
+/*
+ * readfile - take commands from a file.
+ */
+static int
+readfile(argv)
+ char **argv;
+{
+ return options_from_file(*argv, 1, 1, privileged_option);
+}
+
+/*
+ * callfile - take commands from /etc/ppp/peers/<name>.
+ * Name may not contain /../, start with / or ../, or end in /..
+ */
+static int
+callfile(argv)
+ char **argv;
+{
+ char *fname, *arg, *p;
+ int l, ok;
+
+ arg = *argv;
+ ok = 1;
+ if (arg[0] == '/' || arg[0] == 0)
+ ok = 0;
+ else {
+ for (p = arg; *p != 0; ) {
+ if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
+ ok = 0;
+ break;
+ }
+ while (*p != '/' && *p != 0)
+ ++p;
+ if (*p == '/')
+ ++p;
+ }
+ }
+ if (!ok) {
+ option_error("call option value may not contain .. or start with /");
+ return 0;
+ }
+
+ l = strlen(arg) + strlen(_PATH_PEERFILES) + 1;
+ if ((fname = (char *) malloc(l)) == NULL)
+ novm("call file name");
+ slprintf(fname, l, "%s%s", _PATH_PEERFILES, arg);
+
+ ok = options_from_file(fname, 1, 1, 1);
+
+ free(fname);
+ return ok;
+}
+
+#ifdef PPP_FILTER
+/*
+ * setpassfilter - Set the pass filter for packets
+ */
+static int
+setpassfilter(argv)
+ char **argv;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
+ return 1;
+ option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
+ return 0;
+}
+
+/*
+ * setactivefilter - Set the active filter for packets
+ */
+static int
+setactivefilter(argv)
+ char **argv;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
+ return 1;
+ option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
+ return 0;
+}
+#endif
+
+/*
+ * setdomain - Set domain name to append to hostname
+ */
+static int
+setdomain(argv)
+ char **argv;
+{
+ gethostname(hostname, MAXNAMELEN);
+ if (**argv != 0) {
+ if (**argv != '.')
+ strncat(hostname, ".", MAXNAMELEN - strlen(hostname));
+ domain = hostname + strlen(hostname);
+ strncat(hostname, *argv, MAXNAMELEN - strlen(hostname));
+ }
+ hostname[MAXNAMELEN-1] = 0;
+ return (1);
+}
+
+
+static int
+setlogfile(argv)
+ char **argv;
+{
+ int fd, err;
+
+ if (!privileged_option)
+ seteuid(getuid());
+ fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644);
+ if (fd < 0 && errno == EEXIST)
+ fd = open(*argv, O_WRONLY | O_APPEND);
+ err = errno;
+ if (!privileged_option)
+ seteuid(0);
+ if (fd < 0) {
+ errno = err;
+ option_error("Can't open log file %s: %m", *argv);
+ return 0;
+ }
+ strlcpy(logfile_name, *argv, sizeof(logfile_name));
+ if (logfile_fd >= 0)
+ close(logfile_fd);
+ logfile_fd = fd;
+ log_to_fd = fd;
+ log_default = 0;
+ return 1;
+}
+
+#ifdef PLUGIN
+static int
+loadplugin(argv)
+ char **argv;
+{
+ char *arg = *argv;
+ void *handle;
+ const char *err;
+ void (*init) __P((void));
+ char *path = arg;
+ const char *vers;
+
+ if (strchr(arg, '/') == 0) {
+ const char *base = _PATH_PLUGIN;
+ int l = strlen(base) + strlen(arg) + 2;
+ path = malloc(l);
+ if (path == 0)
+ novm("plugin file path");
+ strlcpy(path, base, l);
+ strlcat(path, "/", l);
+ strlcat(path, arg, l);
+ }
+ handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
+ if (handle == 0) {
+ err = dlerror();
+ if (err != 0)
+ option_error("%s", err);
+ option_error("Couldn't load plugin %s", arg);
+ goto err;
+ }
+ init = (void (*)(void))dlsym(handle, "plugin_init");
+ if (init == 0) {
+ option_error("%s has no initialization entry point", arg);
+ goto errclose;
+ }
+ vers = (const char *) dlsym(handle, "pppd_version");
+ if (vers == 0) {
+ warn("Warning: plugin %s has no version information", arg);
+ } else if (strcmp(vers, VERSION) != 0) {
+ option_error("Plugin %s is for pppd version %s, this is %s",
+ vers, VERSION);
+ goto errclose;
+ }
+ info("Plugin %s loaded.", arg);
+ (*init)();
+ return 1;
+
+ errclose:
+ dlclose(handle);
+ err:
+ if (path != arg)
+ free(path);
+ return 0;
+}
+#endif /* PLUGIN */
diff --git a/mdk-stage1/ppp/pppd/patchlevel.h b/mdk-stage1/ppp/pppd/patchlevel.h
new file mode 100644
index 000000000..2a2d816f1
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/patchlevel.h
@@ -0,0 +1,4 @@
+/* $Id$ */
+
+#define VERSION "2.4.1"
+#define DATE "25 March 2001"
diff --git a/mdk-stage1/ppp/pppd/pathnames.h b/mdk-stage1/ppp/pppd/pathnames.h
new file mode 100644
index 000000000..d8eee70de
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/pathnames.h
@@ -0,0 +1,57 @@
+/*
+ * define path names
+ *
+ * $Id$
+ */
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+
+#else /* HAVE_PATHS_H */
+#ifndef _PATH_VARRUN
+#define _PATH_VARRUN "/etc/ppp/"
+#endif
+#define _PATH_DEVNULL "/dev/null"
+#endif /* HAVE_PATHS_H */
+
+#ifndef _ROOT_PATH
+#define _ROOT_PATH
+#endif
+
+#define _PATH_UPAPFILE _ROOT_PATH "/etc/ppp/pap-secrets"
+#define _PATH_CHAPFILE _ROOT_PATH "/etc/ppp/chap-secrets"
+#define _PATH_SYSOPTIONS _ROOT_PATH "/etc/ppp/options"
+#define _PATH_IPUP _ROOT_PATH "/etc/ppp/ip-up"
+#define _PATH_IPDOWN _ROOT_PATH "/etc/ppp/ip-down"
+#define _PATH_AUTHUP _ROOT_PATH "/etc/ppp/auth-up"
+#define _PATH_AUTHDOWN _ROOT_PATH "/etc/ppp/auth-down"
+#define _PATH_TTYOPT _ROOT_PATH "/etc/ppp/options."
+#define _PATH_CONNERRS _ROOT_PATH "/etc/ppp/connect-errors"
+#define _PATH_PEERFILES _ROOT_PATH "/etc/ppp/peers/"
+#define _PATH_RESOLV _ROOT_PATH "/etc/ppp/resolv.conf"
+
+#define _PATH_USEROPT ".ppprc"
+
+#ifdef INET6
+#define _PATH_IPV6UP _ROOT_PATH "/etc/ppp/ipv6-up"
+#define _PATH_IPV6DOWN _ROOT_PATH "/etc/ppp/ipv6-down"
+#endif
+
+#ifdef IPX_CHANGE
+#define _PATH_IPXUP _ROOT_PATH "/etc/ppp/ipx-up"
+#define _PATH_IPXDOWN _ROOT_PATH "/etc/ppp/ipx-down"
+#endif /* IPX_CHANGE */
+
+#ifdef __STDC__
+#define _PATH_PPPDB _ROOT_PATH _PATH_VARRUN "pppd.tdb"
+#else /* __STDC__ */
+#ifdef HAVE_PATHS_H
+#define _PATH_PPPDB "/var/run/pppd.tdb"
+#else
+#define _PATH_PPPDB "/etc/ppp/pppd.tdb"
+#endif
+#endif /* __STDC__ */
+
+#ifdef PLUGIN
+#define _PATH_PLUGIN "/usr/lib/pppd/" VERSION
+#endif /* PLUGIN */
diff --git a/mdk-stage1/ppp/pppd/plugins/Makefile.linux b/mdk-stage1/ppp/pppd/plugins/Makefile.linux
new file mode 100644
index 000000000..a64256461
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/plugins/Makefile.linux
@@ -0,0 +1,19 @@
+CC = gcc
+CFLAGS = -g -O2 -I.. -I../../include -fPIC
+LDFLAGS = -shared
+INSTALL = install
+
+all: minconn.so passprompt.so
+
+minconn.so: minconn.c
+ $(CC) -o $@ $(LDFLAGS) $(CFLAGS) minconn.c
+
+passprompt.so: passprompt.c
+ $(CC) -o $@ $(LDFLAGS) $(CFLAGS) passprompt.c
+
+LIBDIR = /usr/lib/pppd
+
+install: minconn.so passprompt.so
+ version=`awk -F '"' '/VERSION/ { print $$2; }' ../patchlevel.h`; \
+ $(INSTALL) -d $(LIBDIR)/$$version; \
+ $(INSTALL) $? $(LIBDIR)/$$version \ No newline at end of file
diff --git a/mdk-stage1/ppp/pppd/plugins/Makefile.sol2 b/mdk-stage1/ppp/pppd/plugins/Makefile.sol2
new file mode 100644
index 000000000..8f4398258
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/plugins/Makefile.sol2
@@ -0,0 +1,27 @@
+#
+# Makefile for plugins on Solaris 2
+#
+# $Id$
+#
+
+include ../../svr4/Makedefs
+
+CFLAGS = -c -O -I.. -I../../include $(COPTS)
+LDFLAGS = -G
+
+all: minconn.so
+
+minconn.so: minconn.o
+ ld -o $@ $(LDFLAGS) -h $@ minconn.o
+
+minconn.o: minconn.c
+ $(CC) $(CFLAGS) -c $?
+
+passprompt.so: passprompt.o
+ ld -o $@ $(LDFLAGS) -h $@ passprompt.o
+
+passprompt.o: passprompt.c
+ $(CC) $(CFLAGS) -c $?
+
+clean:
+ rm -f *.o *.so
diff --git a/mdk-stage1/ppp/pppd/plugins/minconn.c b/mdk-stage1/ppp/pppd/plugins/minconn.c
new file mode 100644
index 000000000..02ea34bf6
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/plugins/minconn.c
@@ -0,0 +1,46 @@
+/*
+ * minconn.c - pppd plugin to implement a `minconnect' option.
+ *
+ * Copyright 1999 Paul Mackerras.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms. The name of the author
+ * may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#include <stddef.h>
+#include <time.h>
+#include "pppd.h"
+
+char pppd_version[] = VERSION;
+
+static int minconnect = 0;
+
+static option_t my_options[] = {
+ { "minconnect", o_int, &minconnect,
+ "Set minimum connect time before idle timeout applies" },
+ { NULL }
+};
+
+static int my_get_idle(struct ppp_idle *idle)
+{
+ time_t t;
+
+ if (idle == NULL)
+ return minconnect? minconnect: idle_time_limit;
+ t = idle->xmit_idle;
+ if (idle->recv_idle < t)
+ t = idle->recv_idle;
+ return idle_time_limit - t;
+}
+
+void plugin_init(void)
+{
+ info("plugin_init");
+ add_options(my_options);
+ idle_time_hook = my_get_idle;
+}
diff --git a/mdk-stage1/ppp/pppd/plugins/passprompt.c b/mdk-stage1/ppp/pppd/plugins/passprompt.c
new file mode 100644
index 000000000..5e6a7f90b
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/plugins/passprompt.c
@@ -0,0 +1,108 @@
+/*
+ * passprompt.c - pppd plugin to invoke an external PAP password prompter
+ *
+ * Copyright 1999 Paul Mackerras, Alan Curry.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include "pppd.h"
+
+char pppd_version[] = VERSION;
+
+static char promptprog[PATH_MAX+1];
+
+static option_t options[] = {
+ { "promptprog", o_string, promptprog,
+ "External PAP password prompting program",
+ OPT_STATIC, NULL, PATH_MAX },
+ { NULL }
+};
+
+static int promptpass(char *user, char *passwd)
+{
+ int p[2];
+ pid_t kid;
+ int readgood, wstat;
+ size_t red;
+
+ if (promptprog[0] == 0 || access(promptprog, X_OK) < 0)
+ return -1; /* sorry, can't help */
+
+ if (!passwd)
+ return 1;
+
+ if (pipe(p)) {
+ warn("Can't make a pipe for %s", promptprog);
+ return 0;
+ }
+ if ((kid = fork()) == (pid_t) -1) {
+ warn("Can't fork to run %s", promptprog);
+ close(p[0]);
+ close(p[1]);
+ return 0;
+ }
+ if (!kid) {
+ /* we are the child, exec the program */
+ char *argv[4], fdstr[32];
+ sys_close();
+ closelog();
+ close(p[0]);
+ seteuid(getuid());
+ setegid(getgid());
+ argv[0] = promptprog;
+ argv[1] = user;
+ argv[2] = remote_name;
+ sprintf(fdstr, "%d", p[1]);
+ argv[3] = fdstr;
+ argv[4] = 0;
+ execv(*argv, argv);
+ _exit(127);
+ }
+
+ /* we are the parent, read the password from the pipe */
+ close(p[1]);
+ readgood = 0;
+ do {
+ red = read(p[0], passwd + readgood, MAXSECRETLEN-1 - readgood);
+ if (red == 0)
+ break;
+ if (red < 0) {
+ error("Can't read secret from %s: %m", promptprog);
+ readgood = -1;
+ break;
+ }
+ readgood += red;
+ } while (readgood < MAXSECRETLEN - 1);
+ passwd[readgood] = 0;
+ close(p[0]);
+
+ /* now wait for child to exit */
+ while (waitpid(kid, &wstat, 0) < 0) {
+ if (errno != EINTR) {
+ warn("error waiting for %s: %m", promptprog);
+ break;
+ }
+ }
+
+ if (readgood < 0)
+ return 0;
+ if (!WIFEXITED(wstat))
+ warn("%s terminated abnormally", promptprog);
+ if (WEXITSTATUS(wstat))
+ warn("%s exited with code %d", promptprog, WEXITSTATUS(status));
+
+ return 1;
+}
+
+void plugin_init(void)
+{
+ add_options(options);
+ pap_passwd_hook = promptpass;
+}
diff --git a/mdk-stage1/ppp/pppd/ppp.pam b/mdk-stage1/ppp/pppd/ppp.pam
new file mode 100644
index 000000000..475a4bc88
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/ppp.pam
@@ -0,0 +1,6 @@
+#%PAM-1.0
+# Information for the PPPD process with the 'login' option.
+auth required pam_nologin.so
+auth required pam_unix.so
+account required pam_unix.so
+session required pam_unix.so \ No newline at end of file
diff --git a/mdk-stage1/ppp/pppd/pppd.8 b/mdk-stage1/ppp/pppd/pppd.8
new file mode 100644
index 000000000..ab091cd83
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/pppd.8
@@ -0,0 +1,1591 @@
+.\" manual page [] for pppd 2.4
+.\" $Id$
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH PPPD 8
+.SH NAME
+pppd \- Point to Point Protocol daemon
+.SH SYNOPSIS
+.B pppd
+[
+.I tty_name
+] [
+.I speed
+] [
+.I options
+]
+.SH DESCRIPTION
+.LP
+The Point-to-Point Protocol (PPP) provides a method for transmitting
+datagrams over serial point-to-point links. PPP
+is composed of three parts: a method for encapsulating datagrams over
+serial links, an extensible Link Control Protocol (LCP), and
+a family of Network Control Protocols (NCP) for establishing
+and configuring different network-layer protocols.
+.LP
+The encapsulation scheme is provided by driver code in the kernel.
+Pppd provides the basic LCP, authentication support, and an NCP for
+establishing and configuring the Internet Protocol (IP) (called the IP
+Control Protocol, IPCP).
+.SH FREQUENTLY USED OPTIONS
+.TP
+.I <tty_name>
+Communicate over the named device. The string "/dev/" is prepended if
+necessary. If no device name is given, or if the name of the terminal
+connected to the standard input is given, pppd will use that terminal,
+and will not fork to put itself in the background. A value for this
+option from a privileged source cannot be overridden by a
+non-privileged user.
+.TP
+.I <speed>
+Set the baud rate to <speed> (a decimal number). On systems such as
+4.4BSD and NetBSD, any speed can be specified. Other systems
+(e.g. SunOS) allow only a limited set of speeds.
+.TP
+.B asyncmap \fI<map>
+Set the async character map to <map>. This map describes which
+control characters cannot be successfully received over the serial
+line. Pppd will ask the peer to send these characters as a 2-byte
+escape sequence. The argument is a 32 bit hex number with each bit
+representing a character to escape. Bit 0 (00000001) represents the
+character 0x00; bit 31 (80000000) represents the character 0x1f or ^_.
+If multiple \fIasyncmap\fR options are given, the values are ORed
+together. If no \fIasyncmap\fR option is given, no async character
+map will be negotiated for the receive direction; the peer should then
+escape \fIall\fR control characters. To escape transmitted
+characters, use the \fIescape\fR option.
+.TP
+.B auth
+Require the peer to authenticate itself before allowing network
+packets to be sent or received. This option is the default if the
+system has a default route. If neither this option nor the
+\fInoauth\fR option is specified, pppd will only allow the peer to use
+IP addresses to which the system does not already have a route.
+.TP
+.B call \fIname
+Read options from the file /etc/ppp/peers/\fIname\fR. This file may
+contain privileged options, such as \fInoauth\fR, even if pppd
+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 connect \fIscript
+Use the executable or shell command specified by \fIscript\fR to set
+up the serial line. This script would typically use the chat(8)
+program to dial the modem and start the remote ppp session. A value
+for this option from a privileged source cannot be overridden by a
+non-privileged user.
+.TP
+.B crtscts
+Use hardware flow control (i.e. RTS/CTS) to control the flow of
+data on the serial port. If neither the \fIcrtscts\fR, the
+\fInocrtscts\fR, the \fIcdtrcts\fR nor the \fInocdtrcts\fR option
+is given, the hardware flow control setting for the serial port is
+left unchanged.
+Some serial ports (such as Macintosh serial ports) lack a true
+RTS output. Such serial ports use this mode to implement
+unidirectional flow control. The serial port will
+suspend transmission when requested by the modem (via CTS)
+but will be unable to request the modem stop sending to the
+computer. This mode retains the ability to use DTR as
+a modem control line.
+.TP
+.B defaultroute
+Add a default route to the system routing tables, using the peer as
+the gateway, when IPCP negotiation is successfully completed.
+This entry is removed when the PPP connection is broken. This option
+is privileged if the \fInodefaultroute\fR option has been specified.
+.TP
+.B disconnect \fIscript
+Run the executable or shell command specified by \fIscript\fR after
+pppd has terminated the link. This script could, for example, issue
+commands to the modem to cause it to hang up if hardware modem control
+signals were not available. The disconnect script is not run if the
+modem has already hung up. A value for this option from a privileged
+source cannot be overridden by a non-privileged user.
+.TP
+.B escape \fIxx,yy,...
+Specifies that certain characters should be escaped on transmission
+(regardless of whether the peer requests them to be escaped with its
+async control character map). The characters to be escaped are
+specified as a list of hex numbers separated by commas. Note that
+almost any character can be specified for the \fIescape\fR option,
+unlike the \fIasyncmap\fR option which only allows control characters
+to be specified. The characters which may not be escaped are those
+with hex values 0x20 - 0x3f or 0x5e.
+.TP
+.B file \fIname
+Read options from file \fIname\fR (the format is described below).
+The file must be readable by the user who has invoked pppd.
+.TP
+.B init \fIscript
+Run the executable or shell command specified by \fIscript\fR to
+initialize the serial line. This script would typically use the
+chat(8) program to configure the modem to enable auto answer. A value
+for this option from a privileged source cannot be overridden by a
+non-privileged user.
+.TP
+.B lock
+Specifies that pppd should create a UUCP-style lock file for the
+serial device to ensure exclusive access to the device.
+.TP
+.B mru \fIn
+Set the MRU [Maximum Receive Unit] value to \fIn\fR. Pppd
+will ask the peer to send packets of no more than \fIn\fR bytes. The
+minimum MRU value is 128. The default MRU value is 1500. A value of
+296 is recommended for slow links (40 bytes for TCP/IP header + 256
+bytes of data). (Note that for IPv6 MRU must be at least 1280)
+.TP
+.B mtu \fIn
+Set the MTU [Maximum Transmit Unit] value to \fIn\fR. Unless the
+peer requests a smaller value via MRU negotiation, pppd will
+request that the kernel networking code send data packets of no more
+than \fIn\fR bytes through the PPP network interface. (Note that for
+IPv6 MTU must be at least 1280)
+.TP
+.B passive
+Enables the "passive" option in the LCP. With this option, pppd will
+attempt to initiate a connection; if no reply is received from the
+peer, pppd will then just wait passively for a valid LCP packet from
+the peer, instead of exiting, as it would without this option.
+.SH OPTIONS
+.TP
+.I <local_IP_address>\fB:\fI<remote_IP_address>
+Set the local and/or remote interface IP addresses. Either one may be
+omitted. The IP addresses can be specified with a host name or in
+decimal dot notation (e.g. 150.234.56.78). The default local
+address is the (first) IP address of the system (unless the
+\fInoipdefault\fR
+option is given). The remote address will be obtained from the peer
+if not specified in any option. Thus, in simple cases, this option is
+not required. If a local and/or remote IP address is specified with
+this option, pppd
+will not accept a different value from the peer in the IPCP
+negotiation, unless the \fIipcp-accept-local\fR and/or
+\fIipcp-accept-remote\fR options are given, respectively.
+.TP
+.B ipv6 \fI<local_interface_identifier>\fR,\fI<remote_interface_identifier>
+Set the local and/or remote 64-bit interface identifier. Either one may be
+omitted. The identifier must be specified in standard ascii notation of
+IPv6 addresses (e.g. ::dead:beef). If the
+\fIipv6cp-use-ipaddr\fR
+option is given, the local identifier is the local IPv4 address (see above).
+On systems which supports a unique persistent id, such as EUI-48 derived
+from the Ethernet MAC address, \fIipv6cp-use-persistent\fR option can be
+used to replace the \fIipv6 <local>,<remote>\fR option. Otherwise the
+identifier is randomized.
+.TP
+.B active-filter \fIfilter-expression
+Specifies a packet filter to be applied to data packets to determine
+which packets are to be regarded as link activity, and therefore reset
+the idle timer, or cause the link to be brought up in demand-dialling
+mode. This option is useful in conjunction with the
+\fBidle\fR option if there are packets being sent or received
+regularly over the link (for example, routing information packets)
+which would otherwise prevent the link from ever appearing to be idle.
+The \fIfilter-expression\fR syntax is as described for tcpdump(1),
+except that qualifiers which are inappropriate for a PPP link, such as
+\fBether\fR and \fBarp\fR, are not permitted. Generally the filter
+expression should be enclosed in single-quotes to prevent whitespace
+in the expression from being interpreted by the shell. This option
+is currently only available under NetBSD, and then only
+if both the kernel and pppd were compiled with PPP_FILTER defined.
+.TP
+.B allow-ip \fIaddress(es)
+Allow peers to use the given IP address or subnet without
+authenticating themselves. The parameter is parsed as for each
+element of the list of allowed IP addresses in the secrets files (see
+the AUTHENTICATION section below).
+.TP
+.B bsdcomp \fInr,nt
+Request that the peer compress packets that it sends, using the
+BSD-Compress scheme, with a maximum code size of \fInr\fR bits, and
+agree to compress packets sent to the peer with a maximum code size of
+\fInt\fR bits. If \fInt\fR is not specified, it defaults to the value
+given for \fInr\fR. Values in the range 9 to 15 may be used for
+\fInr\fR and \fInt\fR; larger values give better compression but
+consume more kernel memory for compression dictionaries.
+Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
+compression in the corresponding direction. Use \fInobsdcomp\fR or
+\fIbsdcomp 0\fR to disable BSD-Compress compression entirely.
+.TP
+.B cdtrcts
+Use a non-standard hardware flow control (i.e. DTR/CTS) to control
+the flow of data on the serial port. If neither the \fIcrtscts\fR,
+the \fInocrtscts\fR, the \fIcdtrcts\fR nor the \fInocdtrcts\fR
+option is given, the hardware flow control setting for the serial
+port is left unchanged.
+Some serial ports (such as Macintosh serial ports) lack a true
+RTS output. Such serial ports use this mode to implement true
+bi-directional flow control. The sacrifice is that this flow
+control mode does not permit using DTR as a modem control line.
+.TP
+.B chap-interval \fIn
+If this option is given, pppd will rechallenge the peer every \fIn\fR
+seconds.
+.TP
+.B chap-max-challenge \fIn
+Set the maximum number of CHAP challenge transmissions to \fIn\fR
+(default 10).
+.TP
+.B chap-restart \fIn
+Set the CHAP restart interval (retransmission timeout for challenges)
+to \fIn\fR seconds (default 3).
+.TP
+.B connect-delay \fIn
+Wait for up \fIn\fR milliseconds after the connect script finishes for
+a valid PPP packet from the peer. At the end of this time, or when a
+valid PPP packet is received from the peer, pppd will commence
+negotiation by sending its first LCP packet. The default value is
+1000 (1 second). This wait period only applies if the \fBconnect\fR
+or \fBpty\fR option is used.
+.TP
+.B debug
+Enables connection debugging facilities.
+If this option is given, pppd will log the contents of all
+control packets sent or received in a readable form. The packets are
+logged through syslog with facility \fIdaemon\fR and level
+\fIdebug\fR. This information can be directed to a file by setting up
+/etc/syslog.conf appropriately (see syslog.conf(5)).
+.TP
+.B default-asyncmap
+Disable asyncmap negotiation, forcing all control characters to be
+escaped for both the transmit and the receive direction.
+.TP
+.B default-mru
+Disable MRU [Maximum Receive Unit] negotiation. With this option,
+pppd will use the default MRU value of 1500 bytes for both the
+transmit and receive direction.
+.TP
+.B deflate \fInr,nt
+Request that the peer compress packets that it sends, using the
+Deflate scheme, with a maximum window size of \fI2**nr\fR bytes, and
+agree to compress packets sent to the peer with a maximum window size
+of \fI2**nt\fR bytes. If \fInt\fR is not specified, it defaults to
+the value given for \fInr\fR. Values in the range 9 to 15 may be used
+for \fInr\fR and \fInt\fR; larger values give better compression but
+consume more kernel memory for compression dictionaries.
+Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
+compression in the corresponding direction. Use \fInodeflate\fR or
+\fIdeflate 0\fR to disable Deflate compression entirely. (Note: pppd
+requests Deflate compression in preference to BSD-Compress if the peer
+can do either.)
+.TP
+.B demand
+Initiate the link only on demand, i.e. when data traffic is present.
+With this option, the remote IP address must be specified by the user
+on the command line or in an options file. Pppd will initially
+configure the interface and enable it for IP traffic without
+connecting to the peer. When traffic is available, pppd will
+connect to the peer and perform negotiation, authentication, etc.
+When this is completed, pppd will commence passing data packets
+(i.e., IP packets) across the link.
+
+The \fIdemand\fR option implies the \fIpersist\fR option. If this
+behaviour is not desired, use the \fInopersist\fR option after the
+\fIdemand\fR option. The \fIidle\fR and \fIholdoff\fR
+options are also useful in conjuction with the \fIdemand\fR option.
+.TP
+.B domain \fId
+Append the domain name \fId\fR to the local host name for authentication
+purposes. For example, if gethostname() returns the name porsche, but
+the fully qualified domain name is porsche.Quotron.COM, you could
+specify \fIdomain Quotron.COM\fR. Pppd would then use the name
+\fIporsche.Quotron.COM\fR for looking up secrets in the secrets file,
+and as the default name to send to the peer when authenticating itself
+to the peer. This option is privileged.
+.TP
+.B dryrun
+With the \fBdryrun\fR option, pppd will print out all the option
+values which have been set and then exit, after parsing the command
+line and options files and checking the option values, but before
+initiating the link. The option values are logged at level info, and
+also printed to standard output unless the device on standard output
+is the device that pppd would be using to communicate with the peer.
+.TP
+.B dump
+With the \fBdump\fR option, pppd will print out all the option values
+which have been set. This option is like the \fBdryrun\fR option
+except that pppd proceeds as normal rather than exiting.
+.TP
+.B endpoint \fI<epdisc>
+Sets the endpoint discriminator sent by the local machine to the peer
+during multilink negotiation to \fI<epdisc>\fR. The default is to use
+the MAC address of the first ethernet interface on the system, if any,
+otherwise the IPv4 address corresponding to the hostname, if any,
+provided it is not in the multicast or locally-assigned IP address
+ranges, or the localhost address. The endpoint discriminator can be
+the string \fBnull\fR or of the form \fItype\fR:\fIvalue\fR, where
+type is a decimal number or one of the strings \fBlocal\fR, \fBIP\fR,
+\fBMAC\fR, \fBmagic\fR, or \fBphone\fR. The value is an IP address in
+dotted-decimal notation for the \fBIP\fR type, or a string of bytes in
+hexadecimal, separated by periods or colons for the other types. For
+the MAC type, the value may also be the name of an ethernet or similar
+network interface. This option is currently only available under
+Linux.
+.TP
+.B hide-password
+When logging the contents of PAP packets, this option causes pppd to
+exclude the password string from the log. This is the default.
+.TP
+.B holdoff \fIn
+Specifies how many seconds to wait before re-initiating the link after
+it terminates. This option only has any effect if the \fIpersist\fR
+or \fIdemand\fR option is used. The holdoff period is not applied if
+the link was terminated because it was idle.
+.TP
+.B idle \fIn
+Specifies that pppd should disconnect if the link is idle for \fIn\fR
+seconds. The link is idle when no data packets (i.e. IP packets) are
+being sent or received. Note: it is not advisable to use this option
+with the \fIpersist\fR option without the \fIdemand\fR option.
+If the \fBactive-filter\fR
+option is given, data packets which are rejected by the specified
+activity filter also count as the link being idle.
+.TP
+.B ipcp-accept-local
+With this option, pppd will accept the peer's idea of our local IP
+address, even if the local IP address was specified in an option.
+.TP
+.B ipcp-accept-remote
+With this option, pppd will accept the peer's idea of its (remote) IP
+address, even if the remote IP address was specified in an option.
+.TP
+.B ipcp-max-configure \fIn
+Set the maximum number of IPCP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B ipcp-max-failure \fIn
+Set the maximum number of IPCP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B ipcp-max-terminate \fIn
+Set the maximum number of IPCP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B ipcp-restart \fIn
+Set the IPCP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B ipparam \fIstring
+Provides an extra parameter to the ip-up and ip-down scripts. If this
+option is given, the \fIstring\fR supplied is given as the 6th
+parameter to those scripts.
+.TP
+.B ipv6cp-max-configure \fIn
+Set the maximum number of IPv6CP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B ipv6cp-max-failure \fIn
+Set the maximum number of IPv6CP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B ipv6cp-max-terminate \fIn
+Set the maximum number of IPv6CP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B ipv6cp-restart \fIn
+Set the IPv6CP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B ipx
+Enable the IPXCP and IPX protocols. This option is presently only
+supported under Linux, and only if your kernel has been configured to
+include IPX support.
+.TP
+.B ipx-network \fIn
+Set the IPX network number in the IPXCP configure request frame to
+\fIn\fR, a hexadecimal number (without a leading 0x). There is no
+valid default. If this option is not specified, the network number is
+obtained from the peer. If the peer does not have the network number,
+the IPX protocol will not be started.
+.TP
+.B ipx-node \fIn\fB:\fIm
+Set the IPX node numbers. The two node numbers are separated from each
+other with a colon character. The first number \fIn\fR is the local
+node number. The second number \fIm\fR is the peer's node number. Each
+node number is a hexadecimal number, at most 10 digits long. The node
+numbers on the ipx-network must be unique. There is no valid
+default. If this option is not specified then the node numbers are
+obtained from the peer.
+.TP
+.B ipx-router-name \fI<string>
+Set the name of the router. This is a string and is sent to the peer
+as information data.
+.TP
+.B ipx-routing \fIn
+Set the routing protocol to be received by this option. More than one
+instance of \fIipx-routing\fR may be specified. The '\fInone\fR'
+option (0) may be specified as the only instance of ipx-routing. The
+values may be \fI0\fR for \fINONE\fR, \fI2\fR for \fIRIP/SAP\fR, and
+\fI4\fR for \fINLSP\fR.
+.TP
+.B ipxcp-accept-local
+Accept the peer's NAK for the node number specified in the ipx-node
+option. If a node number was specified, and non-zero, the default is
+to insist that the value be used. If you include this option then you
+will permit the peer to override the entry of the node number.
+.TP
+.B ipxcp-accept-network
+Accept the peer's NAK for the network number specified in the
+ipx-network option. If a network number was specified, and non-zero, the
+default is to insist that the value be used. If you include this
+option then you will permit the peer to override the entry of the node
+number.
+.TP
+.B ipxcp-accept-remote
+Use the peer's network number specified in the configure request
+frame. If a node number was specified for the peer and this option was
+not specified, the peer will be forced to use the value which you have
+specified.
+.TP
+.B ipxcp-max-configure \fIn
+Set the maximum number of IPXCP configure request frames which the
+system will send to \fIn\fR. The default is 10.
+.TP
+.B ipxcp-max-failure \fIn
+Set the maximum number of IPXCP NAK frames which the local system will
+send before it rejects the options. The default value is 3.
+.TP
+.B ipxcp-max-terminate \fIn
+Set the maximum nuber of IPXCP terminate request frames before the
+local system considers that the peer is not listening to them. The
+default value is 3.
+.TP
+.B kdebug \fIn
+Enable debugging code in the kernel-level PPP driver. The argument
+values depend on the specific kernel driver, but in general a value of
+1 will enable general kernel debug messages. (Note that these
+messages are usually only useful for debugging the kernel driver
+itself.) For the Linux 2.2.x kernel driver, the value is a sum of
+bits: 1 to
+enable general debug messages, 2 to request that the contents of
+received packets be printed, and 4 to request that the contents of
+transmitted packets be printed. On most systems, messages printed by
+the kernel are logged by syslog(1) to a file as directed in the
+/etc/syslog.conf configuration file.
+.TP
+.B ktune
+Enables pppd to alter kernel settings as appropriate. Under Linux,
+pppd will enable IP forwarding (i.e. set /proc/sys/net/ipv4/ip_forward
+to 1) if the \fIproxyarp\fR option is used, and will enable the
+dynamic IP address option (i.e. set /proc/sys/net/ipv4/ip_dynaddr to
+1) in demand mode if the local address changes.
+.TP
+.B lcp-echo-failure \fIn
+If this option is given, pppd will presume the peer to be dead
+if \fIn\fR LCP echo-requests are sent without receiving a valid LCP
+echo-reply. If this happens, pppd will terminate the
+connection. Use of this option requires a non-zero value for the
+\fIlcp-echo-interval\fR parameter. This option can be used to enable
+pppd to terminate after the physical connection has been broken
+(e.g., the modem has hung up) in situations where no hardware modem
+control lines are available.
+.TP
+.B lcp-echo-interval \fIn
+If this option is given, pppd will send an LCP echo-request frame to
+the peer every \fIn\fR seconds. Normally the peer should respond to
+the echo-request by sending an echo-reply. This option can be used
+with the \fIlcp-echo-failure\fR option to detect that the peer is no
+longer connected.
+.TP
+.B lcp-max-configure \fIn
+Set the maximum number of LCP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B lcp-max-failure \fIn
+Set the maximum number of LCP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B lcp-max-terminate \fIn
+Set the maximum number of LCP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B lcp-restart \fIn
+Set the LCP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B linkname \fIname\fR
+Sets the logical name of the link to \fIname\fR. Pppd will create a
+file named \fBppp-\fIname\fB.pid\fR in /var/run (or /etc/ppp on some
+systems) containing its process ID. This can be useful in determining
+which instance of pppd is responsible for the link to a given peer
+system. This is a privileged option.
+.TP
+.B local
+Don't use the modem control lines. With this option, pppd will ignore
+the state of the CD (Carrier Detect) signal from the modem and will
+not change the state of the DTR (Data Terminal Ready) signal.
+.TP
+.B logfd \fIn
+Send log messages to file descriptor \fIn\fR. Pppd will send log
+messages to at most one file or file descriptor (as well as sending
+the log messages to syslog), so this option and the \fBlogfile\fR
+option are mutually exclusive. The default is for pppd to send log
+messages to stdout (file descriptor 1), unless the serial port is
+already open on stdout.
+.TP
+.B logfile \fIfilename
+Append log messages to the file \fIfilename\fR (as well as sending the
+log messages to syslog). The file is opened with the privileges of
+the user who invoked pppd, in append mode.
+.TP
+.B login
+Use the system password database for authenticating the peer using
+PAP, and record the user in the system wtmp file. Note that the peer
+must have an entry in the /etc/ppp/pap-secrets file as well as the
+system password database to be allowed access.
+.TP
+.B maxconnect \fIn
+Terminate the connection when it has been available for network
+traffic for \fIn\fR seconds (i.e. \fIn\fR seconds after the first
+network control protocol comes up).
+.TP
+.B maxfail \fIn
+Terminate after \fIn\fR consecutive failed connection attempts. A
+value of 0 means no limit. The default value is 10.
+.TP
+.B modem
+Use the modem control lines. This option is the default. With this
+option, pppd will wait for the CD (Carrier Detect) signal from the
+modem to be asserted when opening the serial device (unless a connect
+script is specified), and it will drop the DTR (Data Terminal Ready)
+signal briefly when the connection is terminated and before executing
+the connect script. On Ultrix, this option implies hardware flow
+control, as for the \fIcrtscts\fR option.
+.TP
+.B mp
+Enables the use of PPP multilink; this is an alias for the `multilink'
+option. This option is currently only available under Linux.
+.TP
+.B mpshortseq
+Enables the use of short (12-bit) sequence numbers in multilink
+headers, as opposed to 24-bit sequence numbers. This option is only
+available under Linux, and only has any effect if multilink is
+enabled (see the multilink option).
+.TP
+.B mrru \fIn
+Sets the Maximum Reconstructed Receive Unit to \fIn\fR. The MRRU is
+the maximum size for a received packet on a multilink bundle, and is
+analogous to the MRU for the individual links. This option is
+currently only available under Linux, and only has any effect if
+multilink is enabled (see the multilink option).
+.TP
+.B ms-dns \fI<addr>
+If pppd is acting as a server for Microsoft Windows clients, this
+option allows pppd to supply one or two DNS (Domain Name Server)
+addresses to the clients. The first instance of this option specifies
+the primary DNS address; the second instance (if given) specifies the
+secondary DNS address. (This option was present in some older
+versions of pppd under the name \fBdns-addr\fR.)
+.TP
+.B ms-wins \fI<addr>
+If pppd is acting as a server for Microsoft Windows or "Samba"
+clients, this option allows pppd to supply one or two WINS (Windows
+Internet Name Services) server addresses to the clients. The first
+instance of this option specifies the primary WINS address; the second
+instance (if given) specifies the secondary WINS address.
+.TP
+.B multilink
+Enables the use of the PPP multilink protocol. If the peer also
+supports multilink, then this link can become part of a bundle between
+the local system and the peer. If there is an existing bundle to the
+peer, pppd will join this link to that bundle, otherwise pppd will
+create a new bundle. See the MULTILINK section below. This option is
+currently only available under Linux.
+.TP
+.B name \fIname
+Set the name of the local system for authentication purposes to
+\fIname\fR. This is a privileged option. With this option, pppd will
+use lines in the secrets files which have \fIname\fR as the second
+field when looking for a secret to use in authenticating the peer. In
+addition, unless overridden with the \fIuser\fR option, \fIname\fR
+will be used as the name to send to the peer when authenticating the
+local system to the peer. (Note that pppd does not append the domain
+name to \fIname\fR.)
+.TP
+.B netmask \fIn
+Set the interface netmask to \fIn\fR, a 32 bit netmask in "decimal dot"
+notation (e.g. 255.255.255.0). If this option is given, the value
+specified is ORed with the default netmask. The default netmask is
+chosen based on the negotiated remote IP address; it is the
+appropriate network mask for the class of the remote IP address, ORed
+with the netmasks for any non point-to-point network interfaces in the
+system which are on the same network. (Note: on some platforms, pppd
+will always use 255.255.255.255 for the netmask, if that is the only
+appropriate value for a point-to-point interface.)
+.TP
+.B noaccomp
+Disable Address/Control compression in both directions (send and
+receive).
+.TP
+.B noauth
+Do not require the peer to authenticate itself. This option is
+privileged.
+.TP
+.B nobsdcomp
+Disables BSD-Compress compression; \fBpppd\fR will not request or
+agree to compress packets using the BSD-Compress scheme.
+.TP
+.B noccp
+Disable CCP (Compression Control Protocol) negotiation. This option
+should only be required if the peer is buggy and gets confused by
+requests from pppd for CCP negotiation.
+.TP
+.B nocrtscts
+Disable hardware flow control (i.e. RTS/CTS) on the serial port.
+If neither the \fIcrtscts\fR nor the \fInocrtscts\fR nor the
+\fIcdtrcts\fR nor the \fInocdtrcts\fR option is given, the hardware
+flow control setting for the serial port is left unchanged.
+.TP
+.B nocdtrcts
+This option is a synonym for \fInocrtscts\fR. Either of these options will
+disable both forms of hardware flow control.
+.TP
+.B nodefaultroute
+Disable the \fIdefaultroute\fR option. The system administrator who
+wishes to prevent users from creating default routes with pppd
+can do so by placing this option in the /etc/ppp/options file.
+.TP
+.B nodeflate
+Disables Deflate compression; pppd will not request or agree to
+compress packets using the Deflate scheme.
+.TP
+.B nodetach
+Don't detach from the controlling terminal. Without this option, if a
+serial device other than the terminal on the standard input is
+specified, pppd will fork to become a background process.
+.TP
+.B noendpoint
+Disables pppd from sending an endpoint discriminator to the peer or
+accepting one from the peer (see the MULTILINK section below). This
+option should only be required if the peer is buggy.
+.TP
+.B noip
+Disable IPCP negotiation and IP communication. This option should
+only be required if the peer is buggy and gets confused by requests
+from pppd for IPCP negotiation.
+.TP
+.B noipv6
+Disable IPv6CP negotiation and IPv6 communication. This option should
+only be required if the peer is buggy and gets confused by requests
+from pppd for IPv6CP negotiation.
+.TP
+.B noipdefault
+Disables the default behaviour when no local IP address is specified,
+which is to determine (if possible) the local IP address from the
+hostname. With this option, the peer will have to supply the local IP
+address during IPCP negotiation (unless it specified explicitly on the
+command line or in an options file).
+.TP
+.B noipx
+Disable the IPXCP and IPX protocols. This option should only be
+required if the peer is buggy and gets confused by requests from pppd
+for IPXCP negotiation.
+.TP
+.B noktune
+Opposite of the \fIktune\fR option; disables pppd from changing system
+settings.
+.TP
+.B nolog
+Do not send log messages to a file or file descriptor. This option
+cancels the \fBlogfd\fR and \fBlogfile\fR options.
+.TP
+.B nomagic
+Disable magic number negotiation. With this option, pppd cannot
+detect a looped-back line. This option should only be needed if the
+peer is buggy.
+.TP
+.B nomp
+Disables the use of PPP multilink. This option is currently only
+available under Linux.
+.TP
+.B nompshortseq
+Disables the use of short (12-bit) sequence numbers in the PPP
+multilink protocol, forcing the use of 24-bit sequence numbers. This
+option is currently only available under Linux, and only has any
+effect if multilink is enabled.
+.TP
+.B nomultilink
+Disables the use of PPP multilink. This option is currently only
+available under Linux.
+.TP
+.B nopcomp
+Disable protocol field compression negotiation in both the receive and
+the transmit direction.
+.TP
+.B nopersist
+Exit once a connection has been made and terminated. This is the
+default unless the \fIpersist\fR or \fIdemand\fR option has been
+specified.
+.TP
+.B nopredictor1
+Do not accept or agree to Predictor-1 compression.
+.TP
+.B noproxyarp
+Disable the \fIproxyarp\fR option. The system administrator who
+wishes to prevent users from creating proxy ARP entries with pppd can
+do so by placing this option in the /etc/ppp/options file.
+.TP
+.B notty
+Normally, pppd requires a terminal device. With this option, pppd
+will allocate itself a pseudo-tty master/slave pair and use the slave
+as its terminal device. Pppd will create a child process to act as a
+`character shunt' to transfer characters between the pseudo-tty master
+and its standard input and output. Thus pppd will transmit characters
+on its standard output and receive characters on its standard input
+even if they are not terminal devices. This option increases the
+latency and CPU overhead of transferring data over the ppp interface
+as all of the characters sent and received must flow through the
+character shunt process. An explicit device name may not be given if
+this option is used.
+.TP
+.B novj
+Disable Van Jacobson style TCP/IP header compression in both the
+transmit and the receive direction.
+.TP
+.B novjccomp
+Disable the connection-ID compression option in Van Jacobson style
+TCP/IP header compression. With this option, pppd will not omit the
+connection-ID byte from Van Jacobson compressed TCP/IP headers, nor
+ask the peer to do so.
+.TP
+.B papcrypt
+Indicates that all secrets in the /etc/ppp/pap-secrets file which are
+used for checking the identity of the peer are encrypted, and thus
+pppd should not accept a password which, before encryption, is
+identical to the secret from the /etc/ppp/pap-secrets file.
+.TP
+.B pap-max-authreq \fIn
+Set the maximum number of PAP authenticate-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B pap-restart \fIn
+Set the PAP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B pap-timeout \fIn
+Set the maximum time that pppd will wait for the peer to authenticate
+itself with PAP to \fIn\fR seconds (0 means no limit).
+.TP
+.B pass-filter \fIfilter-expression
+Specifies a packet filter to applied to data packets being sent or
+received to determine which packets should be allowed to pass.
+Packets which are rejected by the filter are silently discarded. This
+option can be used to prevent specific network daemons (such as
+routed) using up link bandwidth, or to provide a basic firewall
+capability.
+The \fIfilter-expression\fR syntax is as described for tcpdump(1),
+except that qualifiers which are inappropriate for a PPP link, such as
+\fBether\fR and \fBarp\fR, are not permitted. Generally the filter
+expression should be enclosed in single-quotes to prevent whitespace
+in the expression from being interpreted by the shell. Note that it
+is possible to apply different constraints to incoming and outgoing
+packets using the \fBinbound\fR and \fBoutbound\fR qualifiers. This
+option is currently only available under NetBSD, and then only if both
+the kernel and pppd were compiled with PPP_FILTER defined.
+.TP
+.B persist
+Do not exit after a connection is terminated; instead try to reopen
+the connection.
+.TP
+.B plugin \fIfilename
+Load the shared library object file \fIfilename\fR as a plugin. This
+is a privileged option.
+.TP
+.B predictor1
+Request that the peer compress frames that it sends using Predictor-1
+compression, and agree to compress transmitted frames with Predictor-1
+if requested. This option has no effect unless the kernel driver
+supports Predictor-1 compression.
+.TP
+.B privgroup \fIgroup-name
+Allows members of group \fIgroup-name\fR to use privileged options.
+This is a privileged option. Use of this option requires care as
+there is no guarantee that members of \fIgroup-name\fR cannot use pppd
+to become root themselves. Consider it equivalent to putting the
+members of \fIgroup-name\fR in the kmem or disk group.
+.TP
+.B proxyarp
+Add an entry to this system's ARP [Address Resolution Protocol] table
+with the IP address of the peer and the Ethernet address of this
+system. This will have the effect of making the peer appear to other
+systems to be on the local ethernet.
+.TP
+.B pty \fIscript
+Specifies that the command \fIscript\fR is to be used to communicate
+rather than a specific terminal device. Pppd will allocate itself a
+pseudo-tty master/slave pair and use the slave as its terminal
+device. The \fIscript\fR will be run in a child process with the
+pseudo-tty master as its standard input and output. An explicit
+device name may not be given if this option is used. (Note: if the
+\fIrecord\fR option is used in conjuction with the \fIpty\fR option,
+the child process will have pipes on its standard input and output.)
+.TP
+.B receive-all
+With this option, pppd will accept all control characters from the
+peer, including those marked in the receive asyncmap. Without this
+option, pppd will discard those characters as specified in RFC1662.
+This option should only be needed if the peer is buggy.
+.TP
+.B record \fIfilename
+Specifies that pppd should record all characters sent and received to
+a file named \fIfilename\fR. This file is opened in append mode,
+using the user's user-ID and permissions. This option is implemented
+using a pseudo-tty and a process to transfer characters between the
+pseudo-tty and the real serial device, so it will increase the latency
+and CPU overhead of transferring data over the ppp interface. The
+characters are stored in a tagged format with timestamps, which can be
+displayed in readable form using the pppdump(8) program.
+.TP
+.B remotename \fIname
+Set the assumed name of the remote system for authentication purposes
+to \fIname\fR.
+.TP
+.B refuse-chap
+With this option, pppd will not agree to authenticate itself to the
+peer using CHAP.
+.TP
+.B refuse-pap
+With this option, pppd will not agree to authenticate itself to the
+peer using PAP.
+.TP
+.B require-chap
+Require the peer to authenticate itself using CHAP [Challenge
+Handshake Authentication Protocol] authentication.
+.TP
+.B require-pap
+Require the peer to authenticate itself using PAP [Password
+Authentication Protocol] authentication.
+.TP
+.B show-password
+When logging the contents of PAP packets, this option causes pppd to
+show the password string in the log message.
+.TP
+.B silent
+With this option, pppd will not transmit LCP packets to initiate a
+connection until a valid LCP packet is received from the peer (as for
+the `passive' option with ancient versions of pppd).
+.TP
+.B sync
+Use synchronous HDLC serial encoding instead of asynchronous.
+The device used by pppd with this option must have sync support.
+Currently supports Microgate SyncLink adapters
+under Linux and FreeBSD 2.2.8 and later.
+.TP
+.B updetach
+With this option, pppd will detach from its controlling terminal once
+it has successfully established the ppp connection (to the point where
+the first network control protocol, usually the IP control protocol,
+has come up).
+.TP
+.B usehostname
+Enforce the use of the hostname (with domain name appended, if given)
+as the name of the local system for authentication purposes (overrides
+the \fIname\fR option). This option is not normally needed since the
+\fIname\fR option is privileged.
+.TP
+.B usepeerdns
+Ask the peer for up to 2 DNS server addresses. The addresses supplied
+by the peer (if any) are passed to the /etc/ppp/ip-up script in the
+environment variables DNS1 and DNS2. In addition, pppd will create an
+/etc/ppp/resolv.conf file containing one or two nameserver lines with
+the address(es) supplied by the peer.
+.TP
+.B user \fIname
+Sets the name used for authenticating the local system to the peer to
+\fIname\fR.
+.TP
+.B vj-max-slots \fIn
+Sets the number of connection slots to be used by the Van Jacobson
+TCP/IP header compression and decompression code to \fIn\fR, which
+must be between 2 and 16 (inclusive).
+.TP
+.B welcome \fIscript
+Run the executable or shell command specified by \fIscript\fR before
+initiating PPP negotiation, after the connect script (if any) has
+completed. A value for this option from a privileged source cannot be
+overridden by a non-privileged user.
+.TP
+.B xonxoff
+Use software flow control (i.e. XON/XOFF) to control the flow of data on
+the serial port.
+.SH OPTIONS FILES
+Options can be taken from files as well as the command line. Pppd
+reads options from the files /etc/ppp/options, ~/.ppprc and
+/etc/ppp/options.\fIttyname\fR (in that order) before processing the
+options on the command line. (In fact, the command-line options are
+scanned to find the terminal name before the options.\fIttyname\fR
+file is read.) In forming the name of the options.\fIttyname\fR file,
+the initial /dev/ is removed from the terminal name, and any remaining
+/ characters are replaced with dots.
+.PP
+An options file is parsed into a series of words, delimited by
+whitespace. Whitespace can be included in a word by enclosing the
+word in double-quotes ("). A backslash (\\) quotes the following character.
+A hash (#) starts a comment, which continues until the end of the
+line. There is no restriction on using the \fIfile\fR or \fIcall\fR
+options within an options file.
+.SH SECURITY
+.I pppd
+provides system administrators with sufficient access control that PPP
+access to a server machine can be provided to legitimate users without
+fear of compromising the security of the server or the network it's
+on. This control is provided through restrictions on which IP
+addresses the peer may use, based on its authenticated identity (if
+any), and through restrictions on which options a non-privileged user
+may use. Several of pppd's options are privileged, in particular
+those which permit potentially insecure configurations; these options
+are only accepted in files which are under the control of the system
+administrator, or if pppd is being run by root.
+.PP
+The default behaviour of pppd is to allow an unauthenticated peer to
+use a given IP address only if the system does not already have a
+route to that IP address. For example, a system with a
+permanent connection to the wider internet will normally have a
+default route, and thus all peers will have to authenticate themselves
+in order to set up a connection. On such a system, the \fIauth\fR
+option is the default. On the other hand, a system where the
+PPP link is the only connection to the internet will not normally have
+a default route, so the peer will be able to use almost any IP address
+without authenticating itself.
+.PP
+As indicated above, some security-sensitive options are privileged,
+which means that they may not be used by an ordinary non-privileged
+user running a setuid-root pppd, either on the command line, in the
+user's ~/.ppprc file, or in an options file read using the \fIfile\fR
+option. Privileged options may be used in /etc/ppp/options file or in
+an options file read using the \fIcall\fR option. If pppd is being
+run by the root user, privileged options can be used without
+restriction.
+.PP
+When opening the device, pppd uses either the invoking user's user ID
+or the root UID (that is, 0), depending on whether the device name was
+specified by the user or the system administrator. If the device name
+comes from a privileged source, that is, /etc/ppp/options or an
+options file read using the \fIcall\fR option, pppd uses full root
+privileges when opening the device. Thus, by creating an appropriate
+file under /etc/ppp/peers, the system administrator can allow users to
+establish a ppp connection via a device which they would not normally
+have permission to access. Otherwise pppd uses the invoking user's
+real UID when opening the device.
+.SH AUTHENTICATION
+Authentication is the process whereby one peer convinces the other of
+its identity. This involves the first peer sending its name to the
+other, together with some kind of secret information which could only
+come from the genuine authorized user of that name. In such an
+exchange, we will call the first peer the "client" and the other the
+"server". The client has a name by which it identifies itself to the
+server, and the server also has a name by which it identifies itself
+to the client. Generally the genuine client shares some secret (or
+password) with the server, and authenticates itself by proving that it
+knows that secret. Very often, the names used for authentication
+correspond to the internet hostnames of the peers, but this is not
+essential.
+.LP
+At present, pppd supports two authentication protocols: the Password
+Authentication Protocol (PAP) and the Challenge Handshake
+Authentication Protocol (CHAP). PAP involves the client sending its
+name and a cleartext password to the server to authenticate itself.
+In contrast, the server initiates the CHAP authentication exchange by
+sending a challenge to the client (the challenge packet includes the
+server's name). The client must respond with a response which
+includes its name plus a hash value derived from the shared secret and
+the challenge, in order to prove that it knows the secret.
+.LP
+The PPP protocol, being symmetrical, allows both peers to require the
+other to authenticate itself. In that case, two separate and
+independent authentication exchanges will occur. The two exchanges
+could use different authentication protocols, and in principle,
+different names could be used in the two exchanges.
+.LP
+The default behaviour of pppd is to agree to authenticate if
+requested, and to not require authentication from the peer. However,
+pppd will not agree to authenticate itself with a particular protocol
+if it has no secrets which could be used to do so.
+.LP
+Pppd stores secrets for use in authentication in secrets
+files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP).
+Both secrets files have the same format. The secrets files can
+contain secrets for pppd to use in authenticating itself to other
+systems, as well as secrets for pppd to use when authenticating other
+systems to itself.
+.LP
+Each line in a secrets file contains one secret. A given secret is
+specific to a particular combination of client and server - it can
+only be used by that client to authenticate itself to that server.
+Thus each line in a secrets file has at least 3 fields: the name of
+the client, the name of the server, and the secret. These fields may
+be followed by a list of the IP addresses that the specified client
+may use when connecting to the specified server.
+.LP
+A secrets file is parsed into words as for a options file, so the
+client name, server name and secrets fields must each be one word,
+with any embedded spaces or other special characters quoted or
+escaped. Note that case is significant in the client and server names
+and in the secret.
+.LP
+If the secret starts with an `@', what follows is assumed to be the
+name of a file from which to read the secret. A "*" as the client or
+server name matches any name. When selecting a secret, pppd takes the
+best match, i.e. the match with the fewest wildcards.
+.LP
+Any following words on the same line are taken to be a list of
+acceptable IP addresses for that client. If there are only 3 words on
+the line, or if the first word is "-", then all IP addresses are
+disallowed. To allow any address, use "*". A word starting with "!"
+indicates that the specified address is \fInot\fR acceptable. An
+address may be followed by "/" and a number \fIn\fR, to indicate a
+whole subnet, i.e. all addresses which have the same value in the most
+significant \fIn\fR bits. In this form, the address may be followed
+by a plus sign ("+") to indicate that one address from the subnet is
+authorized, based on the ppp network interface unit number in use.
+In this case, the host part of the address will be set to the unit
+number plus one.
+.LP
+Thus a secrets file contains both secrets for use in authenticating
+other hosts, plus secrets which we use for authenticating ourselves to
+others. When pppd is authenticating the peer (checking the peer's
+identity), it chooses a secret with the peer's name in the first
+field and the name of the local system in the second field. The
+name of the local system defaults to the hostname, with the domain
+name appended if the \fIdomain\fR option is used. This default can be
+overridden with the \fIname\fR option, except when the
+\fIusehostname\fR option is used.
+.LP
+When pppd is choosing a secret to use in authenticating itself to the
+peer, it first determines what name it is going to use to identify
+itself to the peer. This name can be specified by the user with the
+\fIuser\fR option. If this option is not used, the name defaults to
+the name of the local system, determined as described in the previous
+paragraph. Then pppd looks for a secret with this name in the first
+field and the peer's name in the second field. Pppd will know the
+name of the peer if CHAP authentication is being used, because the
+peer will have sent it in the challenge packet. However, if PAP is being
+used, pppd will have to determine the peer's name from the options
+specified by the user. The user can specify the peer's name directly
+with the \fIremotename\fR option. Otherwise, if the remote IP address
+was specified by a name (rather than in numeric form), that name will
+be used as the peer's name. Failing that, pppd will use the null
+string as the peer's name.
+.LP
+When authenticating the peer with PAP, the supplied password is first
+compared with the secret from the secrets file. If the password
+doesn't match the secret, the password is encrypted using crypt() and
+checked against the secret again. Thus secrets for authenticating the
+peer can be stored in encrypted form if desired. If the
+\fIpapcrypt\fR option is given, the first (unencrypted) comparison is
+omitted, for better security.
+.LP
+Furthermore, if the \fIlogin\fR option was specified, the username and
+password are also checked against the system password database. Thus,
+the system administrator can set up the pap-secrets file to allow PPP
+access only to certain users, and to restrict the set of IP addresses
+that each user can use. Typically, when using the \fIlogin\fR option,
+the secret in /etc/ppp/pap-secrets would be "", which will match any
+password supplied by the peer. This avoids the need to have the same
+secret in two places.
+.LP
+Authentication must be satisfactorily completed before IPCP (or any
+other Network Control Protocol) can be started. If the peer is
+required to authenticate itself, and fails to do so, pppd will
+terminated the link (by closing LCP). If IPCP negotiates an
+unacceptable IP address for the remote host, IPCP will be closed. IP
+packets can only be sent or received when IPCP is open.
+.LP
+In some cases it is desirable to allow some hosts which can't
+authenticate themselves to connect and use one of a restricted set of
+IP addresses, even when the local host generally requires
+authentication. If the peer refuses to authenticate itself when
+requested, pppd takes that as equivalent to authenticating with PAP
+using the empty string for the username and password. Thus, by adding
+a line to the pap-secrets file which specifies the empty string for
+the client and password, it is possible to allow restricted access to
+hosts which refuse to authenticate themselves.
+.SH ROUTING
+.LP
+When IPCP negotiation is completed successfully, pppd will inform the
+kernel of the local and remote IP addresses for the ppp interface.
+This is sufficient to create a host route to the remote end of the
+link, which will enable the peers to exchange IP packets.
+Communication with other machines generally requires further
+modification to routing tables and/or ARP (Address Resolution
+Protocol) tables. In most cases the \fIdefaultroute\fR and/or
+\fIproxyarp\fR options are sufficient for this, but in some cases
+further intervention is required. The /etc/ppp/ip-up script can be
+used for this.
+.LP
+Sometimes it is desirable to add a default route through the remote
+host, as in the case of a machine whose only connection to the
+Internet is through the ppp interface. The \fIdefaultroute\fR option
+causes pppd to create such a default route when IPCP comes up, and
+delete it when the link is terminated.
+.LP
+In some cases it is desirable to use proxy ARP, for example on a
+server machine connected to a LAN, in order to allow other hosts to
+communicate with the remote host. The \fIproxyarp\fR option causes
+pppd to look for a network interface on the same subnet as the remote
+host (an interface supporting broadcast and ARP, which is up and not a
+point-to-point or loopback interface). If found, pppd creates a
+permanent, published ARP entry with the IP address of the remote host
+and the hardware address of the network interface found.
+.LP
+When the \fIdemand\fR option is used, the interface IP addresses have
+already been set at the point when IPCP comes up. If pppd has not
+been able to negotiate the same addresses that it used to configure
+the interface (for example when the peer is an ISP that uses dynamic
+IP address assignment), pppd has to change the interface IP addresses
+to the negotiated addresses. This may disrupt existing connections,
+and the use of demand dialling with peers that do dynamic IP address
+assignment is not recommended.
+.SH MULTILINK
+Multilink PPP provides the capability to combine two or more PPP links
+between a pair of machines into a single `bundle', which appears as a
+single virtual PPP link which has the combined bandwidth of the
+individual links. Currently, multilink PPP is only supported under
+Linux.
+.LP
+Pppd detects that the link it is controlling is connected to the same
+peer as another link using the peer's endpoint discriminator and the
+authenticated identity of the peer (if it authenticates itself). The
+endpoint discriminator is a block of data which is hopefully unique
+for each peer. Several types of data can be used, including
+locally-assigned strings of bytes, IP addresses, MAC addresses,
+randomly strings of bytes, or E-164 phone numbers. The endpoint
+discriminator sent to the peer by pppd can be set using the endpoint
+option.
+.LP
+In circumstances the peer may send no endpoint discriminator or a
+non-unique value. The optional bundle option adds an extra string
+which is added to the peer's endpoint discriminator and authenticated
+identity when matching up links to be joined together in a bundle.
+The bundle option can also be used to allow the establishment of
+multiple bundles between the local system and the peer. Pppd uses a
+TDB database in /var/run/pppd.tdb to match up links.
+.LP
+Assuming that multilink is enabled and the peer is willing to
+negotiate multilink, then when pppd is invoked to bring up the first
+link to the peer, it will detect that no other link is connected to
+the peer and create a new bundle, that is, another ppp network
+interface unit. When another pppd is invoked to bring up another link
+to the peer, it will detect the existing bundle and join its link to
+it. Currently, if the first pppd terminates (for example, because of
+a hangup or a received signal) the bundle is destroyed.
+.SH EXAMPLES
+.LP
+The following examples assume that the /etc/ppp/options file contains
+the \fIauth\fR option (as in the default /etc/ppp/options file in the
+ppp distribution).
+.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
+ttyS0 19200 crtscts
+.br
+connect '/usr/sbin/chat -v -f /etc/ppp/chat-isp'
+.br
+noauth
+.LP
+In this example, we are using chat to dial the ISP's modem and go
+through any logon sequence required. The /etc/ppp/chat-isp file
+contains the script used by chat; it could for example contain
+something like this:
+.IP
+ABORT "NO CARRIER"
+.br
+ABORT "NO DIALTONE"
+.br
+ABORT "ERROR"
+.br
+ABORT "NO ANSWER"
+.br
+ABORT "BUSY"
+.br
+ABORT "Username/Password Incorrect"
+.br
+"" "at"
+.br
+OK "at&d0&c1"
+.br
+OK "atdt2468135"
+.br
+"name:" "^Umyuserid"
+.br
+"word:" "\\qmypassword"
+.br
+"ispts" "\\q^Uppp"
+.br
+"~-^Uppp-~"
+.LP
+See the chat(8) man page for details of chat scripts.
+.LP
+Pppd can also be used to provide a dial-in ppp service for users. If
+the users already have login accounts, the simplest way to set up the
+ppp service is to let the users log in to their accounts and run pppd
+(installed setuid-root) with a command such as
+.IP
+pppd proxyarp
+.LP
+To allow a user to use the PPP facilities, you need to allocate an IP
+address for that user's machine and create an entry in
+/etc/ppp/pap-secrets or /etc/ppp/chap-secrets (depending on which
+authentication method the PPP implementation on the user's machine
+supports), so that the user's
+machine can authenticate itself. For example, if Joe has a machine
+called "joespc" which is to be allowed to dial in to the machine
+called "server" and use the IP address joespc.my.net, you would add an
+entry like this to /etc/ppp/pap-secrets or /etc/ppp/chap-secrets:
+.IP
+joespc server "joe's secret" joespc.my.net
+.LP
+Alternatively, you can create a username called (for example) "ppp",
+whose login shell is pppd and whose home directory is /etc/ppp.
+Options to be used when pppd is run this way can be put in
+/etc/ppp/.ppprc.
+.LP
+If your serial connection is any more complicated than a piece of
+wire, you may need to arrange for some control characters to be
+escaped. In particular, it is often useful to escape XON (^Q) and
+XOFF (^S), using \fIasyncmap a0000\fR. If the path includes a telnet,
+you probably should escape ^] as well (\fIasyncmap 200a0000\fR). If
+the path includes an rlogin, you will need to use the \fIescape ff\fR
+option on the end which is running the rlogin client, since many
+rlogin implementations are not transparent; they will remove the
+sequence [0xff, 0xff, 0x73, 0x73, followed by any 8 bytes] from the
+stream.
+.SH DIAGNOSTICS
+.LP
+Messages are sent to the syslog daemon using facility LOG_DAEMON.
+(This can be overriden by recompiling pppd with the macro
+LOG_PPP defined as the desired facility.) In order to see the error
+and debug messages, you will need to edit your /etc/syslog.conf file
+to direct the messages to the desired output device or file.
+.LP
+The \fIdebug\fR option causes the contents of all control packets sent
+or received to be logged, that is, all LCP, PAP, CHAP or IPCP packets.
+This can be useful if the PPP negotiation does not succeed or if
+authentication fails.
+If debugging is enabled at compile time, the \fIdebug\fR option also
+causes other debugging messages to be logged.
+.LP
+Debugging can also be enabled or disabled by sending a SIGUSR1 signal
+to the pppd process. This signal acts as a toggle.
+.SH EXIT STATUS
+The exit status of pppd is set to indicate whether any error was
+detected, or the reason for the link being terminated. The values
+used are:
+.TP
+.B 0
+Pppd has detached, or otherwise the connection was successfully
+established and terminated at the peer's request.
+.TP
+.B 1
+An immediately fatal error of some kind occurred, such as an essential
+system call failing, or running out of virtual memory.
+.TP
+.B 2
+An error was detected in processing the options given, such as two
+mutually exclusive options being used.
+.TP
+.B 3
+Pppd is not setuid-root and the invoking user is not root.
+.TP
+.B 4
+The kernel does not support PPP, for example, the PPP kernel driver is
+not included or cannot be loaded.
+.TP
+.B 5
+Pppd terminated because it was sent a SIGINT, SIGTERM or SIGHUP
+signal.
+.TP
+.B 6
+The serial port could not be locked.
+.TP
+.B 7
+The serial port could not be opened.
+.TP
+.B 8
+The connect script failed (returned a non-zero exit status).
+.TP
+.B 9
+The command specified as the argument to the \fIpty\fR option could
+not be run.
+.TP
+.B 10
+The PPP negotiation failed, that is, it didn't reach the point where
+at least one network protocol (e.g. IP) was running.
+.TP
+.B 11
+The peer system failed (or refused) to authenticate itself.
+.TP
+.B 12
+The link was established successfully and terminated because it was
+idle.
+.TP
+.B 13
+The link was established successfully and terminated because the
+connect time limit was reached.
+.TP
+.B 14
+Callback was negotiated and an incoming call should arrive shortly.
+.TP
+.B 15
+The link was terminated because the peer is not responding to echo
+requests.
+.TP
+.B 16
+The link was terminated by the modem hanging up.
+.TP
+.B 17
+The PPP negotiation failed because serial loopback was detected.
+.TP
+.B 18
+The init script failed (returned a non-zero exit status).
+.TP
+.B 19
+We failed to authenticate ourselves to the peer.
+.SH SCRIPTS
+Pppd invokes scripts at various stages in its processing which can be
+used to perform site-specific ancillary processing. These scripts are
+usually shell scripts, but could be executable code files instead.
+Pppd does not wait for the scripts to finish. The scripts are
+executed as root (with the real and effective user-id set to 0), so
+that they can do things such as update routing tables or run
+privileged daemons. Be careful that the contents of these scripts do
+not compromise your system's security. Pppd runs the scripts with
+standard input, output and error redirected to /dev/null, and with an
+environment that is empty except for some environment variables that
+give information about the link. The environment variables that pppd
+sets are:
+.TP
+.B DEVICE
+The name of the serial tty device being used.
+.TP
+.B IFNAME
+The name of the network interface being used.
+.TP
+.B IPLOCAL
+The IP address for the local end of the link. This is only set when
+IPCP has come up.
+.TP
+.B IPREMOTE
+The IP address for the remote end of the link. This is only set when
+IPCP has come up.
+.TP
+.B PEERNAME
+The authenticated name of the peer. This is only set if the peer
+authenticates itself.
+.TP
+.B SPEED
+The baud rate of the tty device.
+.TP
+.B ORIG_UID
+The real user-id of the user who invoked pppd.
+.TP
+.B PPPLOGNAME
+The username of the real user-id that invoked pppd. This is always set.
+.P
+For the ip-down and auth-down scripts, pppd also sets the following
+variables giving statistics for the connection:
+.TP
+.B CONNECT_TIME
+The number of seconds from when the PPP negotiation started until the
+connection was terminated.
+.TP
+.B BYTES_SENT
+The number of bytes sent (at the level of the serial port) during the
+connection.
+.TP
+.B BYTES_RCVD
+The number of bytes received (at the level of the serial port) during
+the connection.
+.TP
+.B LINKNAME
+The logical name of the link, set with the \fIlinkname\fR option.
+.P
+Pppd invokes the following scripts, if they exist. It is not an error
+if they don't exist.
+.TP
+.B /etc/ppp/auth-up
+A program or script which is executed after the remote system
+successfully authenticates itself. It is executed with the parameters
+.IP
+\fIinterface-name peer-name user-name tty-device speed\fR
+.IP
+Note that this script is not executed if the peer doesn't authenticate
+itself, for example when the \fInoauth\fR option is used.
+.TP
+.B /etc/ppp/auth-down
+A program or script which is executed when the link goes down, if
+/etc/ppp/auth-up was previously executed. It is executed in the same
+manner with the same parameters as /etc/ppp/auth-up.
+.TP
+.B /etc/ppp/ip-up
+A program or script which is executed when the link is available for
+sending and receiving IP packets (that is, IPCP has come up). It is
+executed with the parameters
+.IP
+\fIinterface-name tty-device speed local-IP-address
+remote-IP-address ipparam\fR
+.TP
+.B /etc/ppp/ip-down
+A program or script which is executed when the link is no longer
+available for sending and receiving IP packets. This script can be
+used for undoing the effects of the /etc/ppp/ip-up script. It is
+invoked in the same manner and with the same parameters as the ip-up
+script.
+.TP
+.B /etc/ppp/ipv6-up
+Like /etc/ppp/ip-up, except that it is executed when the link is available
+for sending and receiving IPv6 packets. It is executed with the parameters
+.IP
+\fIinterface-name tty-device speed local-link-local-address
+remote-link-local-address ipparam\fR
+.TP
+.B /etc/ppp/ipv6-down
+Similar to /etc/ppp/ip-down, but it is executed when IPv6 packets can no
+longer be transmitted on the link. It is executed with the same parameters
+as the ipv6-up script.
+.TP
+.B /etc/ppp/ipx-up
+A program or script which is executed when the link is available for
+sending and receiving IPX packets (that is, IPXCP has come up). It is
+executed with the parameters
+.IP
+\fIinterface-name tty-device speed network-number local-IPX-node-address
+remote-IPX-node-address local-IPX-routing-protocol remote-IPX-routing-protocol
+local-IPX-router-name remote-IPX-router-name ipparam pppd-pid\fR
+.IP
+The local-IPX-routing-protocol and remote-IPX-routing-protocol field
+may be one of the following:
+.IP
+NONE to indicate that there is no routing protocol
+.br
+RIP to indicate that RIP/SAP should be used
+.br
+NLSP to indicate that Novell NLSP should be used
+.br
+RIP NLSP to indicate that both RIP/SAP and NLSP should be used
+.TP
+.B /etc/ppp/ipx-down
+A program or script which is executed when the link is no longer
+available for sending and receiving IPX packets. This script can be
+used for undoing the effects of the /etc/ppp/ipx-up script. It is
+invoked in the same manner and with the same parameters as the ipx-up
+script.
+.SH FILES
+.TP
+.B /var/run/ppp\fIn\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp\fIn\fB.pid \fR(others)
+Process-ID for pppd process on ppp interface unit \fIn\fR.
+.TP
+.B /var/run/ppp-\fIname\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp-\fIname\fB.pid \fR(others)
+Process-ID for pppd process for logical link \fIname\fR (see the
+\fIlinkname\fR option).
+.TP
+.B /etc/ppp/pap-secrets
+Usernames, passwords and IP addresses for PAP authentication. This
+file should be owned by root and not readable or writable by any other
+user. Pppd will log a warning if this is not the case.
+.TP
+.B /etc/ppp/chap-secrets
+Names, secrets and IP addresses for CHAP authentication. As for
+/etc/ppp/pap-secrets, this file should be owned by root and not
+readable or writable by any other user. Pppd will log a warning if
+this is not the case.
+.TP
+.B /etc/ppp/options
+System default options for pppd, read before user default options or
+command-line options.
+.TP
+.B ~/.ppprc
+User default options, read before /etc/ppp/options.\fIttyname\fR.
+.TP
+.B /etc/ppp/options.\fIttyname
+System default options for the serial port being used, read after
+~/.ppprc. In forming the \fIttyname\fR part of this
+filename, an initial /dev/ is stripped from the port name (if
+present), and any slashes in the remaining part are converted to
+dots.
+.TP
+.B /etc/ppp/peers
+A directory containing options files which may contain privileged
+options, even if pppd was invoked by a user other than root. The
+system administrator can create options files in this directory to
+permit non-privileged users to dial out without requiring the peer to
+authenticate, but only to certain trusted peers.
+.SH SEE ALSO
+.TP
+.B RFC1144
+Jacobson, V.
+\fICompressing TCP/IP headers for low-speed serial links.\fR
+February 1990.
+.TP
+.B RFC1321
+Rivest, R.
+.I The MD5 Message-Digest Algorithm.
+April 1992.
+.TP
+.B RFC1332
+McGregor, G.
+.I PPP Internet Protocol Control Protocol (IPCP).
+May 1992.
+.TP
+.B RFC1334
+Lloyd, B.; Simpson, W.A.
+.I PPP authentication protocols.
+October 1992.
+.TP
+.B RFC1661
+Simpson, W.A.
+.I The Point\-to\-Point Protocol (PPP).
+July 1994.
+.TP
+.B RFC1662
+Simpson, W.A.
+.I PPP in HDLC-like Framing.
+July 1994.
+.TP
+.B RFC2472
+Haskin, D.
+.I IP Version 6 over PPP
+December 1998.
+.SH NOTES
+The following signals have the specified effect when sent to pppd.
+.TP
+.B SIGINT, SIGTERM
+These signals cause pppd to terminate the link (by closing LCP),
+restore the serial device settings, and exit.
+.TP
+.B SIGHUP
+This signal causes pppd to terminate the link, restore the serial
+device settings, and close the serial device. If the \fIpersist\fR or
+\fIdemand\fR option has been specified, pppd will try to reopen the
+serial device and start another connection (after the holdoff period).
+Otherwise pppd will exit. If this signal is received during the
+holdoff period, it causes pppd to end the holdoff period immediately.
+.TP
+.B SIGUSR1
+This signal toggles the state of the \fIdebug\fR option.
+.TP
+.B SIGUSR2
+This signal causes pppd to renegotiate compression. This can be
+useful to re-enable compression after it has been disabled as a result
+of a fatal decompression error. (Fatal decompression errors generally
+indicate a bug in one or other implementation.)
+
+.SH AUTHORS
+Paul Mackerras (Paul.Mackerras@cs.anu.edu.au), based on earlier work by
+Drew Perkins,
+Brad Clements,
+Karl Fox,
+Greg Christy,
+and
+Brad Parker.
diff --git a/mdk-stage1/ppp/pppd/pppd.h b/mdk-stage1/ppp/pppd/pppd.h
new file mode 100644
index 000000000..02f6dfcef
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/pppd.h
@@ -0,0 +1,787 @@
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * TODO:
+ */
+
+#ifndef __PPPD_H__
+#define __PPPD_H__
+
+#include <stdio.h> /* for FILE */
+#include <limits.h> /* for NGROUPS_MAX */
+#include <sys/param.h> /* for MAXPATHLEN and BSD4_4, if defined */
+#include <sys/types.h> /* for u_int32_t, if defined */
+#include <sys/time.h> /* for struct timeval */
+#include <net/ppp_defs.h>
+#include "patchlevel.h"
+
+#if defined(__STDC__)
+#include <stdarg.h>
+#define __V(x) x
+#else
+#include <varargs.h>
+#define __V(x) (va_alist) va_dcl
+#define const
+#define volatile
+#endif
+
+#ifdef INET6
+#include "eui64.h"
+#endif
+
+/*
+ * Limits.
+ */
+
+#define NUM_PPP 1 /* One PPP interface supported (per process) */
+#define MAXWORDLEN 1024 /* max length of word in file (incl null) */
+#define MAXARGS 1 /* max # args to a command */
+#define MAXNAMELEN 256 /* max length of hostname or name for auth */
+#define MAXSECRETLEN 256 /* max length of password or secret */
+
+/*
+ * Option descriptor structure.
+ */
+
+typedef unsigned char bool;
+
+enum opt_type {
+ o_special_noarg = 0,
+ o_special = 1,
+ o_bool,
+ o_int,
+ o_uint32,
+ o_string,
+ o_wild,
+};
+
+typedef struct {
+ char *name; /* name of the option */
+ enum opt_type type;
+ void *addr;
+ char *description;
+ int flags;
+ void *addr2;
+ int upper_limit;
+ int lower_limit;
+ const char *source;
+ short int priority;
+ short int winner;
+} option_t;
+
+/* Values for flags */
+#define OPT_VALUE 0xff /* mask for presupplied value */
+#define OPT_HEX 0x100 /* int option is in hex */
+#define OPT_NOARG 0x200 /* option doesn't take argument */
+#define OPT_OR 0x400 /* OR in argument to value */
+#define OPT_INC 0x800 /* increment value */
+#define OPT_PRIV 0x1000 /* privileged option */
+#define OPT_STATIC 0x2000 /* string option goes into static array */
+#define OPT_LLIMIT 0x4000 /* check value against lower limit */
+#define OPT_ULIMIT 0x8000 /* check value against upper limit */
+#define OPT_LIMITS (OPT_LLIMIT|OPT_ULIMIT)
+#define OPT_ZEROOK 0x10000 /* 0 value is OK even if not within limits */
+#define OPT_HIDE 0x10000 /* for o_string, print value as ?????? */
+#define OPT_A2LIST 0x10000 /* for o_special, keep list of values */
+#define OPT_NOINCR 0x20000 /* value mustn't be increased */
+#define OPT_ZEROINF 0x40000 /* with OPT_NOINCR, 0 == infinity */
+#define OPT_PRIO 0x80000 /* process option priorities for this option */
+#define OPT_PRIOSUB 0x100000 /* subsidiary member of priority group */
+#define OPT_ALIAS 0x200000 /* option is alias for previous option */
+#define OPT_A2COPY 0x400000 /* addr2 -> second location to rcv value */
+#define OPT_ENABLE 0x800000 /* use *addr2 as enable for option */
+#define OPT_A2CLR 0x1000000 /* clear *(bool *)addr2 */
+#define OPT_PRIVFIX 0x2000000 /* user can't override if set by root */
+#define OPT_INITONLY 0x4000000 /* option can only be set in init phase */
+#define OPT_DEVEQUIV 0x8000000 /* equiv to device name */
+#define OPT_DEVNAM (OPT_INITONLY | OPT_DEVEQUIV)
+#define OPT_A2PRINTER 0x10000000 /* *addr2 is a fn for printing option */
+#define OPT_A2STRVAL 0x20000000 /* *addr2 points to current string value */
+#define OPT_NOPRINT 0x40000000 /* don't print this option at all */
+
+#define OPT_VAL(x) ((x) & OPT_VALUE)
+
+/* Values for priority */
+#define OPRIO_DEFAULT 0 /* a default value */
+#define OPRIO_CFGFILE 1 /* value from a configuration file */
+#define OPRIO_CMDLINE 2 /* value from the command line */
+#define OPRIO_SECFILE 3 /* value from options in a secrets file */
+#define OPRIO_ROOT 100 /* added to priority if OPT_PRIVFIX && root */
+
+#ifndef GIDSET_TYPE
+#define GIDSET_TYPE gid_t
+#endif
+
+/* Structure representing a list of permitted IP addresses. */
+struct permitted_ip {
+ int permit; /* 1 = permit, 0 = forbid */
+ u_int32_t base; /* match if (addr & mask) == base */
+ u_int32_t mask; /* base and mask are in network byte order */
+};
+
+/*
+ * Unfortunately, the linux kernel driver uses a different structure
+ * for statistics from the rest of the ports.
+ * This structure serves as a common representation for the bits
+ * pppd needs.
+ */
+struct pppd_stats {
+ unsigned int bytes_in;
+ unsigned int bytes_out;
+};
+
+/* Used for storing a sequence of words. Usually malloced. */
+struct wordlist {
+ struct wordlist *next;
+ char *word;
+};
+
+/* An endpoint discriminator, used with multilink. */
+#define MAX_ENDP_LEN 20 /* maximum length of discriminator value */
+struct epdisc {
+ unsigned char class;
+ unsigned char length;
+ unsigned char value[MAX_ENDP_LEN];
+};
+
+/* values for epdisc.class */
+#define EPD_NULL 0 /* null discriminator, no data */
+#define EPD_LOCAL 1
+#define EPD_IP 2
+#define EPD_MAC 3
+#define EPD_MAGIC 4
+#define EPD_PHONENUM 5
+
+typedef void (*notify_func) __P((void *, int));
+
+struct notifier {
+ struct notifier *next;
+ notify_func func;
+ void *arg;
+};
+
+/*
+ * Global variables.
+ */
+
+extern int hungup; /* Physical layer has disconnected */
+extern int ifunit; /* Interface unit number */
+extern char ifname[]; /* Interface name */
+extern char hostname[]; /* Our hostname */
+extern u_char outpacket_buf[]; /* Buffer for outgoing packets */
+extern int phase; /* Current state of link - see values below */
+extern int baud_rate; /* Current link speed in bits/sec */
+extern char *progname; /* Name of this program */
+extern int redirect_stderr;/* Connector's stderr should go to file */
+extern char peer_authname[];/* Authenticated name of peer */
+extern int privileged; /* We were run by real-uid root */
+extern int need_holdoff; /* Need holdoff period after link terminates */
+extern char **script_env; /* Environment variables for scripts */
+extern int detached; /* Have detached from controlling tty */
+extern GIDSET_TYPE groups[NGROUPS_MAX]; /* groups the user is in */
+extern int ngroups; /* How many groups valid in groups */
+extern struct pppd_stats link_stats; /* byte/packet counts etc. for link */
+extern int link_stats_valid; /* set if link_stats is valid */
+extern int link_connect_time; /* time the link was up for */
+extern int using_pty; /* using pty as device (notty or pty opt.) */
+extern int log_to_fd; /* logging to this fd as well as syslog */
+extern bool log_default; /* log_to_fd is default (stdout) */
+extern char *no_ppp_msg; /* message to print if ppp not in kernel */
+extern volatile int status; /* exit status for pppd */
+extern bool devnam_fixed; /* can no longer change devnam */
+extern int unsuccess; /* # unsuccessful connection attempts */
+extern int do_callback; /* set if we want to do callback next */
+extern int doing_callback; /* set if this is a callback */
+extern char ppp_devnam[MAXPATHLEN];
+extern struct notifier *pidchange; /* for notifications of pid changing */
+extern struct notifier *phasechange; /* for notifications of phase changes */
+extern struct notifier *exitnotify; /* for notification that we're exiting */
+extern struct notifier *sigreceived; /* notification of received signal */
+extern int listen_time; /* time to listen first (ms) */
+
+/* Values for do_callback and doing_callback */
+#define CALLBACK_DIALIN 1 /* we are expecting the call back */
+#define CALLBACK_DIALOUT 2 /* we are dialling out to call back */
+
+/*
+ * Variables set by command-line options.
+ */
+
+extern int debug; /* Debug flag */
+extern int kdebugflag; /* Tell kernel to print debug messages */
+extern int default_device; /* Using /dev/tty or equivalent */
+extern char devnam[MAXPATHLEN]; /* Device name */
+extern int crtscts; /* Use hardware flow control */
+extern bool modem; /* Use modem control lines */
+extern int inspeed; /* Input/Output speed requested */
+extern u_int32_t netmask; /* IP netmask to set on interface */
+extern bool lockflag; /* Create lock file to lock the serial dev */
+extern bool nodetach; /* Don't detach from controlling tty */
+extern bool updetach; /* Detach from controlling tty when link up */
+extern char *initializer; /* Script to initialize physical link */
+extern char *connect_script; /* Script to establish physical link */
+extern char *disconnect_script; /* Script to disestablish physical link */
+extern char *welcomer; /* Script to welcome client after connection */
+extern char *ptycommand; /* Command to run on other side of pty */
+extern int maxconnect; /* Maximum connect time (seconds) */
+extern char user[MAXNAMELEN];/* Our name for authenticating ourselves */
+extern char passwd[MAXSECRETLEN]; /* Password for PAP or CHAP */
+extern bool auth_required; /* Peer is required to authenticate */
+extern bool persist; /* Reopen link after it goes down */
+extern bool uselogin; /* Use /etc/passwd for checking PAP */
+extern char our_name[MAXNAMELEN];/* Our name for authentication purposes */
+extern char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+extern bool explicit_remote;/* remote_name specified with remotename opt */
+extern bool demand; /* Do dial-on-demand */
+extern char *ipparam; /* Extra parameter for ip up/down scripts */
+extern bool cryptpap; /* Others' PAP passwords are encrypted */
+extern int idle_time_limit;/* Shut down link if idle for this long */
+extern int holdoff; /* Dead time before restarting */
+extern bool holdoff_specified; /* true if user gave a holdoff value */
+extern bool notty; /* Stdin/out is not a tty */
+extern char *pty_socket; /* Socket to connect to pty */
+extern char *record_file; /* File to record chars sent/received */
+extern bool sync_serial; /* Device is synchronous serial device */
+extern int maxfail; /* Max # of unsuccessful connection attempts */
+extern char linkname[MAXPATHLEN]; /* logical name for link */
+extern bool tune_kernel; /* May alter kernel settings as necessary */
+extern int connect_delay; /* Time to delay after connect script */
+extern int max_data_rate; /* max bytes/sec through charshunt */
+extern int req_unit; /* interface unit number to use */
+extern bool multilink; /* enable multilink operation */
+extern bool noendpoint; /* don't send or accept endpt. discrim. */
+extern char *bundle_name; /* bundle name for multilink */
+extern bool dump_options; /* print out option values */
+extern bool dryrun; /* check everything, print options, exit */
+
+#ifdef PPP_FILTER
+extern struct bpf_program pass_filter; /* Filter for pkts to pass */
+extern struct bpf_program active_filter; /* Filter for link-active pkts */
+#endif
+
+#ifdef MSLANMAN
+extern bool ms_lanman; /* Use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+extern char *current_option; /* the name of the option being parsed */
+extern int privileged_option; /* set iff the current option came from root */
+extern char *option_source; /* string saying where the option came from */
+extern int option_priority; /* priority of current options */
+
+/*
+ * Values for phase.
+ */
+#define PHASE_DEAD 0
+#define PHASE_INITIALIZE 1
+#define PHASE_SERIALCONN 2
+#define PHASE_DORMANT 3
+#define PHASE_ESTABLISH 4
+#define PHASE_AUTHENTICATE 5
+#define PHASE_CALLBACK 6
+#define PHASE_NETWORK 7
+#define PHASE_RUNNING 8
+#define PHASE_TERMINATE 9
+#define PHASE_DISCONNECT 10
+#define PHASE_HOLDOFF 11
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular protocol.
+ */
+struct protent {
+ u_short protocol; /* PPP protocol number */
+ /* Initialization procedure */
+ void (*init) __P((int unit));
+ /* Process a received packet */
+ void (*input) __P((int unit, u_char *pkt, int len));
+ /* Process a received protocol-reject */
+ void (*protrej) __P((int unit));
+ /* Lower layer has come up */
+ void (*lowerup) __P((int unit));
+ /* Lower layer has gone down */
+ void (*lowerdown) __P((int unit));
+ /* Open the protocol */
+ void (*open) __P((int unit));
+ /* Close the protocol */
+ void (*close) __P((int unit, char *reason));
+ /* Print a packet in readable form */
+ int (*printpkt) __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+ /* Process a received data packet */
+ void (*datainput) __P((int unit, u_char *pkt, int len));
+ bool enabled_flag; /* 0 iff protocol is disabled */
+ char *name; /* Text name of protocol */
+ char *data_name; /* Text name of corresponding data protocol */
+ option_t *options; /* List of command-line options */
+ /* Check requested options, assign defaults */
+ void (*check_options) __P((void));
+ /* Configure interface for demand-dial */
+ int (*demand_conf) __P((int unit));
+ /* Say whether to bring up link for this pkt */
+ int (*active_pkt) __P((u_char *pkt, int len));
+};
+
+/* Table of pointers to supported protocols */
+extern struct protent *protocols[];
+
+/*
+ * This struct contains pointers to a set of procedures for
+ * doing operations on a "channel". A channel provides a way
+ * to send and receive PPP packets - the canonical example is
+ * a serial port device in PPP line discipline (or equivalently
+ * with PPP STREAMS modules pushed onto it).
+ */
+struct channel {
+ /* set of options for this channel */
+ option_t *options;
+ /* find and process a per-channel options file */
+ void (*process_extra_options) __P((void));
+ /* check all the options that have been given */
+ void (*check_options) __P((void));
+ /* get the channel ready to do PPP, return a file descriptor */
+ int (*connect) __P((void));
+ /* we're finished with the channel */
+ void (*disconnect) __P((void));
+ /* put the channel into PPP `mode' */
+ int (*establish_ppp) __P((int));
+ /* take the channel out of PPP `mode', restore loopback if demand */
+ void (*disestablish_ppp) __P((int));
+ /* set the transmit-side PPP parameters of the channel */
+ void (*send_config) __P((int, u_int32_t, int, int));
+ /* set the receive-side PPP parameters of the channel */
+ void (*recv_config) __P((int, u_int32_t, int, int));
+ /* cleanup on error or normal exit */
+ void (*cleanup) __P((void));
+ /* close the device, called in children after fork */
+ void (*close) __P((void));
+};
+
+extern struct channel *the_channel;
+
+#define ppp_send_config(unit, mtu, accm, pc, acc) \
+do { \
+ if (the_channel->send_config) \
+ (*the_channel->send_config)((mtu), (accm), (pc), (acc)); \
+} while (0)
+
+#define ppp_recv_config(unit, mtu, accm, pc, acc) \
+do { \
+ if (the_channel->send_config) \
+ (*the_channel->recv_config)((mtu), (accm), (pc), (acc)); \
+} while (0)
+
+/*
+ * Prototypes.
+ */
+
+/* Procedures exported from main.c. */
+void set_ifunit __P((int)); /* set stuff that depends on ifunit */
+void detach __P((void)); /* Detach from controlling tty */
+void die __P((int)); /* Cleanup and exit */
+void quit __P((void)); /* like die(1) */
+void novm __P((char *)); /* Say we ran out of memory, and die */
+void timeout __P((void (*func)(void *), void *arg, int s, int us));
+ /* Call func(arg) after s.us seconds */
+void untimeout __P((void (*func)(void *), void *arg));
+ /* Cancel call to func(arg) */
+void record_child __P((int, char *, void (*) (void *), void *));
+int device_script __P((char *cmd, int in, int out, int dont_wait));
+ /* Run `cmd' with given stdin and stdout */
+pid_t run_program __P((char *prog, char **args, int must_exist,
+ void (*done)(void *), void *arg));
+ /* Run program prog with args in child */
+void reopen_log __P((void)); /* (re)open the connection to syslog */
+void update_link_stats __P((int)); /* Get stats at link termination */
+void script_setenv __P((char *, char *, int)); /* set script env var */
+void script_unsetenv __P((char *)); /* unset script env var */
+void new_phase __P((int)); /* signal start of new phase */
+void add_notifier __P((struct notifier **, notify_func, void *));
+void remove_notifier __P((struct notifier **, notify_func, void *));
+void notify __P((struct notifier *, int));
+
+/* Procedures exported from tty.c. */
+void tty_init __P((void));
+
+/* Procedures exported from utils.c. */
+void log_packet __P((u_char *, int, char *, int));
+ /* Format a packet and log it with syslog */
+void print_string __P((char *, int, void (*) (void *, char *, ...),
+ void *)); /* Format a string for output */
+int slprintf __P((char *, int, char *, ...)); /* sprintf++ */
+int vslprintf __P((char *, int, char *, va_list)); /* vsprintf++ */
+size_t strlcpy __P((char *, const char *, size_t)); /* safe strcpy */
+size_t strlcat __P((char *, const char *, size_t)); /* safe strncpy */
+void dbglog __P((char *, ...)); /* log a debug message */
+void info __P((char *, ...)); /* log an informational message */
+void notice __P((char *, ...)); /* log a notice-level message */
+void warn __P((char *, ...)); /* log a warning message */
+void error __P((char *, ...)); /* log an error message */
+void fatal __P((char *, ...)); /* log an error message and die(1) */
+void init_pr_log __P((char *, int)); /* initialize for using pr_log */
+void pr_log __P((void *, char *, ...)); /* printer fn, output to syslog */
+void end_pr_log __P((void)); /* finish up after using pr_log */
+
+/* Procedures exported from auth.c */
+void link_required __P((int)); /* we are starting to use the link */
+void link_terminated __P((int)); /* we are finished with the link */
+void link_down __P((int)); /* the LCP layer has left the Opened state */
+void link_established __P((int)); /* the link is up; authenticate now */
+void start_networks __P((void)); /* start all the network control protos */
+void np_up __P((int, int)); /* a network protocol has come up */
+void np_down __P((int, int)); /* a network protocol has gone down */
+void np_finished __P((int, int)); /* a network protocol no longer needs link */
+void auth_peer_fail __P((int, int));
+ /* peer failed to authenticate itself */
+void auth_peer_success __P((int, int, char *, int));
+ /* peer successfully authenticated itself */
+void auth_withpeer_fail __P((int, int));
+ /* we failed to authenticate ourselves */
+void auth_withpeer_success __P((int, int));
+ /* we successfully authenticated ourselves */
+void auth_check_options __P((void));
+ /* check authentication options supplied */
+void auth_reset __P((int)); /* check what secrets we have */
+int check_passwd __P((int, char *, int, char *, int, char **));
+ /* Check peer-supplied username/password */
+int get_secret __P((int, char *, char *, char *, int *, int));
+ /* get "secret" for chap */
+int auth_ip_addr __P((int, u_int32_t));
+ /* check if IP address is authorized */
+int bad_ip_adrs __P((u_int32_t));
+ /* check if IP address is unreasonable */
+
+/* Procedures exported from demand.c */
+void demand_conf __P((void)); /* config interface(s) for demand-dial */
+void demand_block __P((void)); /* set all NPs to queue up packets */
+void demand_unblock __P((void)); /* set all NPs to pass packets */
+void demand_discard __P((void)); /* set all NPs to discard packets */
+void demand_rexmit __P((int)); /* retransmit saved frames for an NP */
+int loop_chars __P((unsigned char *, int)); /* process chars from loopback */
+int loop_frame __P((unsigned char *, int)); /* should we bring link up? */
+
+/* Procedures exported from multilink.c */
+void mp_check_options __P((void)); /* Check multilink-related options */
+int mp_join_bundle __P((void)); /* join our link to an appropriate bundle */
+char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */
+int str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */
+
+/* Procedures exported from sys-*.c */
+void sys_init __P((void)); /* Do system-dependent initialization */
+void sys_cleanup __P((void)); /* Restore system state before exiting */
+int sys_check_options __P((void)); /* Check options specified */
+void sys_close __P((void)); /* Clean up in a child before execing */
+int ppp_available __P((void)); /* Test whether ppp kernel support exists */
+int get_pty __P((int *, int *, char *, int)); /* Get pty master/slave */
+int open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */
+int tty_establish_ppp __P((int)); /* Turn serial port into a ppp interface */
+void tty_disestablish_ppp __P((int)); /* Restore port to normal operation */
+void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
+int bundle_attach __P((int)); /* Attach link to existing bundle */
+void cfg_bundle __P((int, int, int, int)); /* Configure existing bundle */
+void clean_check __P((void)); /* Check if line was 8-bit clean */
+void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */
+void restore_tty __P((int)); /* Restore port's original parameters */
+void setdtr __P((int, int)); /* Raise or lower port's DTR line */
+void output __P((int, u_char *, int)); /* Output a PPP packet */
+void wait_input __P((struct timeval *));
+ /* Wait for input, with timeout */
+void add_fd __P((int)); /* Add fd to set to wait for */
+void remove_fd __P((int)); /* Remove fd from set to wait for */
+int read_packet __P((u_char *)); /* Read PPP packet */
+int get_loop_output __P((void)); /* Read pkts from loopback */
+void tty_send_config __P((int, u_int32_t, int, int));
+ /* Configure i/f transmit parameters */
+void tty_set_xaccm __P((ext_accm));
+ /* Set extended transmit ACCM */
+void tty_recv_config __P((int, u_int32_t, int, int));
+ /* Configure i/f receive parameters */
+int ccp_test __P((int, u_char *, int, int));
+ /* Test support for compression scheme */
+void ccp_flags_set __P((int, int, int));
+ /* Set kernel CCP state */
+int ccp_fatal_error __P((int)); /* Test for fatal decomp error in kernel */
+int get_idle_time __P((int, struct ppp_idle *));
+ /* Find out how long link has been idle */
+int get_ppp_stats __P((int, struct pppd_stats *));
+ /* Return link statistics */
+void netif_set_mtu __P((int, int)); /* Set PPP interface MTU */
+int sifvjcomp __P((int, int, int, int));
+ /* Configure VJ TCP header compression */
+int sifup __P((int)); /* Configure i/f up for one protocol */
+int sifnpmode __P((int u, int proto, enum NPmode mode));
+ /* Set mode for handling packets for proto */
+int sifdown __P((int)); /* Configure i/f down for one protocol */
+int sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t));
+ /* Configure IPv4 addresses for i/f */
+int cifaddr __P((int, u_int32_t, u_int32_t));
+ /* Reset i/f IP addresses */
+#ifdef INET6
+int sif6addr __P((int, eui64_t, eui64_t));
+ /* Configure IPv6 addresses for i/f */
+int cif6addr __P((int, eui64_t, eui64_t));
+ /* Remove an IPv6 address from i/f */
+#endif
+int sifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Create default route through i/f */
+int cifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Delete default route through i/f */
+int sifproxyarp __P((int, u_int32_t));
+ /* Add proxy ARP entry for peer */
+int cifproxyarp __P((int, u_int32_t));
+ /* Delete proxy ARP entry for peer */
+u_int32_t GetMask __P((u_int32_t)); /* Get appropriate netmask for address */
+int lock __P((char *)); /* Create lock file for device */
+int relock __P((int)); /* Rewrite lock file with new pid */
+void unlock __P((void)); /* Delete previously-created lock file */
+int get_host_seed __P((void)); /* Get host-dependent random number seed */
+int have_route_to __P((u_int32_t)); /* Check if route to addr exists */
+#ifdef PPP_FILTER
+int set_filters __P((struct bpf_program *pass, struct bpf_program *active));
+ /* Set filter programs in kernel */
+#endif
+#ifdef IPX_CHANGE
+int sipxfaddr __P((int, unsigned long, unsigned char *));
+int cipxfaddr __P((int));
+#endif
+int get_if_hwaddr __P((u_char *addr, char *name));
+char *get_first_ethernet __P((void));
+
+/* Procedures exported from options.c */
+int parse_args __P((int argc, char **argv));
+ /* Parse options from arguments given */
+int options_from_file __P((char *filename, int must_exist, int check_prot,
+ int privileged));
+ /* Parse options from an options file */
+int options_from_user __P((void)); /* Parse options from user's .ppprc */
+int options_for_tty __P((void)); /* Parse options from /etc/ppp/options.tty */
+int options_from_list __P((struct wordlist *, int privileged));
+ /* Parse options from a wordlist */
+int getword __P((FILE *f, char *word, int *newlinep, char *filename));
+ /* Read a word from a file */
+void option_error __P((char *fmt, ...));
+ /* Print an error message about an option */
+int int_option __P((char *, int *));
+ /* Simplified number_option for decimal ints */
+void add_options __P((option_t *)); /* Add extra options */
+void check_options __P((void)); /* check values after all options parsed */
+int override_value __P((const char *, int, const char *));
+ /* override value if permitted by priority */
+void print_options __P((void (*) __P((void *, char *, ...)), void *));
+ /* print out values of all options */
+
+int parse_dotted_ip __P((char *, u_int32_t *));
+
+/*
+ * Hooks to enable plugins to change various things.
+ */
+extern int (*new_phase_hook) __P((int));
+extern int (*idle_time_hook) __P((struct ppp_idle *));
+extern int (*holdoff_hook) __P((void));
+extern int (*pap_check_hook) __P((void));
+extern int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+ struct wordlist **paddrs,
+ struct wordlist **popts));
+extern void (*pap_logout_hook) __P((void));
+extern int (*pap_passwd_hook) __P((char *user, char *passwd));
+extern void (*ip_up_hook) __P((void));
+extern void (*ip_down_hook) __P((void));
+extern void (*ip_choose_hook) __P((u_int32_t *));
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+ (c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+ *(cp)++ = (u_char) (c); \
+}
+
+
+#define GETSHORT(s, cp) { \
+ (s) = *(cp)++ << 8; \
+ (s) |= *(cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+ *(cp)++ = (u_char) ((s) >> 8); \
+ *(cp)++ = (u_char) (s); \
+}
+
+#define GETLONG(l, cp) { \
+ (l) = *(cp)++ << 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; \
+}
+#define PUTLONG(l, cp) { \
+ *(cp)++ = (u_char) ((l) >> 24); \
+ *(cp)++ = (u_char) ((l) >> 16); \
+ *(cp)++ = (u_char) ((l) >> 8); \
+ *(cp)++ = (u_char) (l); \
+}
+
+#define INCPTR(n, cp) ((cp) += (n))
+#define DECPTR(n, cp) ((cp) -= (n))
+
+/*
+ * System dependent definitions for user-level 4.3BSD UNIX implementation.
+ */
+
+#define TIMEOUT(r, f, t) timeout((r), (f), (t), 0)
+#define UNTIMEOUT(r, f) untimeout((r), (f))
+
+#define BCOPY(s, d, l) memcpy(d, s, l)
+#define BZERO(s, n) memset(s, 0, n)
+
+#define PRINTMSG(m, l) { info("Remote message: %0.*v", l, m); }
+
+/*
+ * MAKEHEADER - Add Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+ PUTCHAR(PPP_ALLSTATIONS, p); \
+ PUTCHAR(PPP_UI, p); \
+ PUTSHORT(t, p); }
+
+/*
+ * Exit status values.
+ */
+#define EXIT_OK 0
+#define EXIT_FATAL_ERROR 1
+#define EXIT_OPTION_ERROR 2
+#define EXIT_NOT_ROOT 3
+#define EXIT_NO_KERNEL_SUPPORT 4
+#define EXIT_USER_REQUEST 5
+#define EXIT_LOCK_FAILED 6
+#define EXIT_OPEN_FAILED 7
+#define EXIT_CONNECT_FAILED 8
+#define EXIT_PTYCMD_FAILED 9
+#define EXIT_NEGOTIATION_FAILED 10
+#define EXIT_PEER_AUTH_FAILED 11
+#define EXIT_IDLE_TIMEOUT 12
+#define EXIT_CONNECT_TIME 13
+#define EXIT_CALLBACK 14
+#define EXIT_PEER_DEAD 15
+#define EXIT_HANGUP 16
+#define EXIT_LOOPBACK 17
+#define EXIT_INIT_FAILED 18
+#define EXIT_AUTH_TOPEER_FAILED 19
+
+/*
+ * Debug macros. Slightly useful for finding bugs in pppd, not particularly
+ * useful for finding out why your connection isn't being established.
+ */
+#ifdef DEBUGALL
+#define DEBUGMAIN 1
+#define DEBUGFSM 1
+#define DEBUGLCP 1
+#define DEBUGIPCP 1
+#define DEBUGIPV6CP 1
+#define DEBUGUPAP 1
+#define DEBUGCHAP 1
+#endif
+
+#ifndef LOG_PPP /* we use LOG_LOCAL2 for syslog by default */
+#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUGSYS) \
+ || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \
+ || defined(DEBUGCHAP) || defined(DEBUG) || defined(DEBUGIPV6CP)
+#define LOG_PPP LOG_LOCAL2
+#else
+#define LOG_PPP LOG_DAEMON
+#endif
+#endif /* LOG_PPP */
+
+#ifdef DEBUGMAIN
+#define MAINDEBUG(x) if (debug) dbglog x
+#else
+#define MAINDEBUG(x)
+#endif
+
+#ifdef DEBUGSYS
+#define SYSDEBUG(x) if (debug) dbglog x
+#else
+#define SYSDEBUG(x)
+#endif
+
+#ifdef DEBUGFSM
+#define FSMDEBUG(x) if (debug) dbglog x
+#else
+#define FSMDEBUG(x)
+#endif
+
+#ifdef DEBUGLCP
+#define LCPDEBUG(x) if (debug) dbglog x
+#else
+#define LCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPCP
+#define IPCPDEBUG(x) if (debug) dbglog x
+#else
+#define IPCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPV6CP
+#define IPV6CPDEBUG(x) if (debug) dbglog x
+#else
+#define IPV6CPDEBUG(x)
+#endif
+
+#ifdef DEBUGUPAP
+#define UPAPDEBUG(x) if (debug) dbglog x
+#else
+#define UPAPDEBUG(x)
+#endif
+
+#ifdef DEBUGCHAP
+#define CHAPDEBUG(x) if (debug) dbglog x
+#else
+#define CHAPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPXCP
+#define IPXCPDEBUG(x) if (debug) dbglog x
+#else
+#define IPXCPDEBUG(x)
+#endif
+
+#ifndef SIGTYPE
+#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE)
+#define SIGTYPE void
+#else
+#define SIGTYPE int
+#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */
+#endif /* SIGTYPE */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b)? (a): (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) ((a) > (b)? (a): (b))
+#endif
+
+#endif /* __PPP_H__ */
diff --git a/mdk-stage1/ppp/pppd/pppd.h.wtmp b/mdk-stage1/ppp/pppd/pppd.h.wtmp
new file mode 100644
index 000000000..4d440be06
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/pppd.h.wtmp
@@ -0,0 +1,789 @@
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * TODO:
+ */
+
+#ifndef __PPPD_H__
+#define __PPPD_H__
+
+#include <stdio.h> /* for FILE */
+#include <limits.h> /* for NGROUPS_MAX */
+#include <sys/param.h> /* for MAXPATHLEN and BSD4_4, if defined */
+#include <sys/types.h> /* for u_int32_t, if defined */
+#include <sys/time.h> /* for struct timeval */
+#include <net/ppp_defs.h>
+#include "patchlevel.h"
+
+#if defined(__STDC__)
+#include <stdarg.h>
+#define __V(x) x
+#else
+#include <varargs.h>
+#define __V(x) (va_alist) va_dcl
+#define const
+#define volatile
+#endif
+
+#ifdef INET6
+#include "eui64.h"
+#endif
+
+/*
+ * Limits.
+ */
+
+#define NUM_PPP 1 /* One PPP interface supported (per process) */
+#define MAXWORDLEN 1024 /* max length of word in file (incl null) */
+#define MAXARGS 1 /* max # args to a command */
+#define MAXNAMELEN 256 /* max length of hostname or name for auth */
+#define MAXSECRETLEN 256 /* max length of password or secret */
+
+/*
+ * Option descriptor structure.
+ */
+
+typedef unsigned char bool;
+
+enum opt_type {
+ o_special_noarg = 0,
+ o_special = 1,
+ o_bool,
+ o_int,
+ o_uint32,
+ o_string,
+ o_wild,
+};
+
+typedef struct {
+ char *name; /* name of the option */
+ enum opt_type type;
+ void *addr;
+ char *description;
+ int flags;
+ void *addr2;
+ int upper_limit;
+ int lower_limit;
+ const char *source;
+ short int priority;
+ short int winner;
+} option_t;
+
+/* Values for flags */
+#define OPT_VALUE 0xff /* mask for presupplied value */
+#define OPT_HEX 0x100 /* int option is in hex */
+#define OPT_NOARG 0x200 /* option doesn't take argument */
+#define OPT_OR 0x400 /* OR in argument to value */
+#define OPT_INC 0x800 /* increment value */
+#define OPT_PRIV 0x1000 /* privileged option */
+#define OPT_STATIC 0x2000 /* string option goes into static array */
+#define OPT_LLIMIT 0x4000 /* check value against lower limit */
+#define OPT_ULIMIT 0x8000 /* check value against upper limit */
+#define OPT_LIMITS (OPT_LLIMIT|OPT_ULIMIT)
+#define OPT_ZEROOK 0x10000 /* 0 value is OK even if not within limits */
+#define OPT_HIDE 0x10000 /* for o_string, print value as ?????? */
+#define OPT_A2LIST 0x10000 /* for o_special, keep list of values */
+#define OPT_NOINCR 0x20000 /* value mustn't be increased */
+#define OPT_ZEROINF 0x40000 /* with OPT_NOINCR, 0 == infinity */
+#define OPT_PRIO 0x80000 /* process option priorities for this option */
+#define OPT_PRIOSUB 0x100000 /* subsidiary member of priority group */
+#define OPT_ALIAS 0x200000 /* option is alias for previous option */
+#define OPT_A2COPY 0x400000 /* addr2 -> second location to rcv value */
+#define OPT_ENABLE 0x800000 /* use *addr2 as enable for option */
+#define OPT_A2CLR 0x1000000 /* clear *(bool *)addr2 */
+#define OPT_PRIVFIX 0x2000000 /* user can't override if set by root */
+#define OPT_INITONLY 0x4000000 /* option can only be set in init phase */
+#define OPT_DEVEQUIV 0x8000000 /* equiv to device name */
+#define OPT_DEVNAM (OPT_INITONLY | OPT_DEVEQUIV)
+#define OPT_A2PRINTER 0x10000000 /* *addr2 is a fn for printing option */
+#define OPT_A2STRVAL 0x20000000 /* *addr2 points to current string value */
+#define OPT_NOPRINT 0x40000000 /* don't print this option at all */
+
+#define OPT_VAL(x) ((x) & OPT_VALUE)
+
+/* Values for priority */
+#define OPRIO_DEFAULT 0 /* a default value */
+#define OPRIO_CFGFILE 1 /* value from a configuration file */
+#define OPRIO_CMDLINE 2 /* value from the command line */
+#define OPRIO_SECFILE 3 /* value from options in a secrets file */
+#define OPRIO_ROOT 100 /* added to priority if OPT_PRIVFIX && root */
+
+#ifndef GIDSET_TYPE
+#define GIDSET_TYPE gid_t
+#endif
+
+/* Structure representing a list of permitted IP addresses. */
+struct permitted_ip {
+ int permit; /* 1 = permit, 0 = forbid */
+ u_int32_t base; /* match if (addr & mask) == base */
+ u_int32_t mask; /* base and mask are in network byte order */
+};
+
+/*
+ * Unfortunately, the linux kernel driver uses a different structure
+ * for statistics from the rest of the ports.
+ * This structure serves as a common representation for the bits
+ * pppd needs.
+ */
+struct pppd_stats {
+ unsigned int bytes_in;
+ unsigned int bytes_out;
+};
+
+/* Used for storing a sequence of words. Usually malloced. */
+struct wordlist {
+ struct wordlist *next;
+ char *word;
+};
+
+/* An endpoint discriminator, used with multilink. */
+#define MAX_ENDP_LEN 20 /* maximum length of discriminator value */
+struct epdisc {
+ unsigned char class;
+ unsigned char length;
+ unsigned char value[MAX_ENDP_LEN];
+};
+
+/* values for epdisc.class */
+#define EPD_NULL 0 /* null discriminator, no data */
+#define EPD_LOCAL 1
+#define EPD_IP 2
+#define EPD_MAC 3
+#define EPD_MAGIC 4
+#define EPD_PHONENUM 5
+
+typedef void (*notify_func) __P((void *, int));
+
+struct notifier {
+ struct notifier *next;
+ notify_func func;
+ void *arg;
+};
+
+/*
+ * Global variables.
+ */
+
+extern int hungup; /* Physical layer has disconnected */
+extern int ifunit; /* Interface unit number */
+extern char ifname[]; /* Interface name */
+extern char hostname[]; /* Our hostname */
+extern u_char outpacket_buf[]; /* Buffer for outgoing packets */
+extern int phase; /* Current state of link - see values below */
+extern int baud_rate; /* Current link speed in bits/sec */
+extern char *progname; /* Name of this program */
+extern int redirect_stderr;/* Connector's stderr should go to file */
+extern char peer_authname[];/* Authenticated name of peer */
+extern int privileged; /* We were run by real-uid root */
+extern int need_holdoff; /* Need holdoff period after link terminates */
+extern char **script_env; /* Environment variables for scripts */
+extern int detached; /* Have detached from controlling tty */
+extern GIDSET_TYPE groups[NGROUPS_MAX]; /* groups the user is in */
+extern int ngroups; /* How many groups valid in groups */
+extern struct pppd_stats link_stats; /* byte/packet counts etc. for link */
+extern int link_stats_valid; /* set if link_stats is valid */
+extern int link_connect_time; /* time the link was up for */
+extern int using_pty; /* using pty as device (notty or pty opt.) */
+extern int log_to_fd; /* logging to this fd as well as syslog */
+extern bool log_default; /* log_to_fd is default (stdout) */
+extern char *no_ppp_msg; /* message to print if ppp not in kernel */
+extern volatile int status; /* exit status for pppd */
+extern bool devnam_fixed; /* can no longer change devnam */
+extern int unsuccess; /* # unsuccessful connection attempts */
+extern int do_callback; /* set if we want to do callback next */
+extern int doing_callback; /* set if this is a callback */
+extern char ppp_devnam[MAXPATHLEN];
+extern struct notifier *pidchange; /* for notifications of pid changing */
+extern struct notifier *phasechange; /* for notifications of phase changes */
+extern struct notifier *exitnotify; /* for notification that we're exiting */
+extern struct notifier *sigreceived; /* notification of received signal */
+extern int listen_time; /* time to listen first (ms) */
+
+/* Values for do_callback and doing_callback */
+#define CALLBACK_DIALIN 1 /* we are expecting the call back */
+#define CALLBACK_DIALOUT 2 /* we are dialling out to call back */
+
+/*
+ * Variables set by command-line options.
+ */
+
+extern int debug; /* Debug flag */
+extern int kdebugflag; /* Tell kernel to print debug messages */
+extern int default_device; /* Using /dev/tty or equivalent */
+extern char devnam[MAXPATHLEN]; /* Device name */
+extern int crtscts; /* Use hardware flow control */
+extern bool modem; /* Use modem control lines */
+extern int inspeed; /* Input/Output speed requested */
+extern u_int32_t netmask; /* IP netmask to set on interface */
+extern bool lockflag; /* Create lock file to lock the serial dev */
+extern bool nodetach; /* Don't detach from controlling tty */
+extern bool updetach; /* Detach from controlling tty when link up */
+extern char *initializer; /* Script to initialize physical link */
+extern char *connect_script; /* Script to establish physical link */
+extern char *disconnect_script; /* Script to disestablish physical link */
+extern char *welcomer; /* Script to welcome client after connection */
+extern char *ptycommand; /* Command to run on other side of pty */
+extern int maxconnect; /* Maximum connect time (seconds) */
+extern char user[MAXNAMELEN];/* Our name for authenticating ourselves */
+extern char passwd[MAXSECRETLEN]; /* Password for PAP or CHAP */
+extern bool auth_required; /* Peer is required to authenticate */
+extern bool persist; /* Reopen link after it goes down */
+extern bool uselogin; /* Use /etc/passwd for checking PAP */
+extern char our_name[MAXNAMELEN];/* Our name for authentication purposes */
+extern char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+extern bool explicit_remote;/* remote_name specified with remotename opt */
+extern bool demand; /* Do dial-on-demand */
+extern char *ipparam; /* Extra parameter for ip up/down scripts */
+extern bool cryptpap; /* Others' PAP passwords are encrypted */
+extern int idle_time_limit;/* Shut down link if idle for this long */
+extern int holdoff; /* Dead time before restarting */
+extern bool holdoff_specified; /* true if user gave a holdoff value */
+extern bool notty; /* Stdin/out is not a tty */
+extern char *pty_socket; /* Socket to connect to pty */
+extern char *record_file; /* File to record chars sent/received */
+extern bool sync_serial; /* Device is synchronous serial device */
+extern int maxfail; /* Max # of unsuccessful connection attempts */
+extern char linkname[MAXPATHLEN]; /* logical name for link */
+extern bool tune_kernel; /* May alter kernel settings as necessary */
+extern int connect_delay; /* Time to delay after connect script */
+extern int max_data_rate; /* max bytes/sec through charshunt */
+extern int req_unit; /* interface unit number to use */
+extern bool multilink; /* enable multilink operation */
+extern bool noendpoint; /* don't send or accept endpt. discrim. */
+extern char *bundle_name; /* bundle name for multilink */
+extern bool dump_options; /* print out option values */
+extern bool dryrun; /* check everything, print options, exit */
+
+#ifdef PPP_FILTER
+extern struct bpf_program pass_filter; /* Filter for pkts to pass */
+extern struct bpf_program active_filter; /* Filter for link-active pkts */
+#endif
+
+#ifdef MSLANMAN
+extern bool ms_lanman; /* Use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+extern char *current_option; /* the name of the option being parsed */
+extern int privileged_option; /* set iff the current option came from root */
+extern char *option_source; /* string saying where the option came from */
+extern int option_priority; /* priority of current options */
+
+/*
+ * Values for phase.
+ */
+#define PHASE_DEAD 0
+#define PHASE_INITIALIZE 1
+#define PHASE_SERIALCONN 2
+#define PHASE_DORMANT 3
+#define PHASE_ESTABLISH 4
+#define PHASE_AUTHENTICATE 5
+#define PHASE_CALLBACK 6
+#define PHASE_NETWORK 7
+#define PHASE_RUNNING 8
+#define PHASE_TERMINATE 9
+#define PHASE_DISCONNECT 10
+#define PHASE_HOLDOFF 11
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular protocol.
+ */
+struct protent {
+ u_short protocol; /* PPP protocol number */
+ /* Initialization procedure */
+ void (*init) __P((int unit));
+ /* Process a received packet */
+ void (*input) __P((int unit, u_char *pkt, int len));
+ /* Process a received protocol-reject */
+ void (*protrej) __P((int unit));
+ /* Lower layer has come up */
+ void (*lowerup) __P((int unit));
+ /* Lower layer has gone down */
+ void (*lowerdown) __P((int unit));
+ /* Open the protocol */
+ void (*open) __P((int unit));
+ /* Close the protocol */
+ void (*close) __P((int unit, char *reason));
+ /* Print a packet in readable form */
+ int (*printpkt) __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+ /* Process a received data packet */
+ void (*datainput) __P((int unit, u_char *pkt, int len));
+ bool enabled_flag; /* 0 iff protocol is disabled */
+ char *name; /* Text name of protocol */
+ char *data_name; /* Text name of corresponding data protocol */
+ option_t *options; /* List of command-line options */
+ /* Check requested options, assign defaults */
+ void (*check_options) __P((void));
+ /* Configure interface for demand-dial */
+ int (*demand_conf) __P((int unit));
+ /* Say whether to bring up link for this pkt */
+ int (*active_pkt) __P((u_char *pkt, int len));
+};
+
+/* Table of pointers to supported protocols */
+extern struct protent *protocols[];
+
+/*
+ * This struct contains pointers to a set of procedures for
+ * doing operations on a "channel". A channel provides a way
+ * to send and receive PPP packets - the canonical example is
+ * a serial port device in PPP line discipline (or equivalently
+ * with PPP STREAMS modules pushed onto it).
+ */
+struct channel {
+ /* set of options for this channel */
+ option_t *options;
+ /* find and process a per-channel options file */
+ void (*process_extra_options) __P((void));
+ /* check all the options that have been given */
+ void (*check_options) __P((void));
+ /* get the channel ready to do PPP, return a file descriptor */
+ int (*connect) __P((void));
+ /* we're finished with the channel */
+ void (*disconnect) __P((void));
+ /* put the channel into PPP `mode' */
+ int (*establish_ppp) __P((int));
+ /* take the channel out of PPP `mode', restore loopback if demand */
+ void (*disestablish_ppp) __P((int));
+ /* set the transmit-side PPP parameters of the channel */
+ void (*send_config) __P((int, u_int32_t, int, int));
+ /* set the receive-side PPP parameters of the channel */
+ void (*recv_config) __P((int, u_int32_t, int, int));
+ /* cleanup on error or normal exit */
+ void (*cleanup) __P((void));
+ /* close the device, called in children after fork */
+ void (*close) __P((void));
+};
+
+extern struct channel *the_channel;
+
+#define ppp_send_config(unit, mtu, accm, pc, acc) \
+do { \
+ if (the_channel->send_config) \
+ (*the_channel->send_config)((mtu), (accm), (pc), (acc)); \
+} while (0)
+
+#define ppp_recv_config(unit, mtu, accm, pc, acc) \
+do { \
+ if (the_channel->send_config) \
+ (*the_channel->recv_config)((mtu), (accm), (pc), (acc)); \
+} while (0)
+
+/*
+ * Prototypes.
+ */
+
+/* Procedures exported from main.c. */
+void set_ifunit __P((int)); /* set stuff that depends on ifunit */
+void detach __P((void)); /* Detach from controlling tty */
+void die __P((int)); /* Cleanup and exit */
+void quit __P((void)); /* like die(1) */
+void novm __P((char *)); /* Say we ran out of memory, and die */
+void timeout __P((void (*func)(void *), void *arg, int s, int us));
+ /* Call func(arg) after s.us seconds */
+void untimeout __P((void (*func)(void *), void *arg));
+ /* Cancel call to func(arg) */
+void record_child __P((int, char *, void (*) (void *), void *));
+int device_script __P((char *cmd, int in, int out, int dont_wait));
+ /* Run `cmd' with given stdin and stdout */
+pid_t run_program __P((char *prog, char **args, int must_exist,
+ void (*done)(void *), void *arg));
+ /* Run program prog with args in child */
+void reopen_log __P((void)); /* (re)open the connection to syslog */
+void update_link_stats __P((int)); /* Get stats at link termination */
+void script_setenv __P((char *, char *, int)); /* set script env var */
+void script_unsetenv __P((char *)); /* unset script env var */
+void new_phase __P((int)); /* signal start of new phase */
+void add_notifier __P((struct notifier **, notify_func, void *));
+void remove_notifier __P((struct notifier **, notify_func, void *));
+void notify __P((struct notifier *, int));
+
+/* Procedures exported from tty.c. */
+void tty_init __P((void));
+
+/* Procedures exported from utils.c. */
+void log_packet __P((u_char *, int, char *, int));
+ /* Format a packet and log it with syslog */
+void print_string __P((char *, int, void (*) (void *, char *, ...),
+ void *)); /* Format a string for output */
+int slprintf __P((char *, int, char *, ...)); /* sprintf++ */
+int vslprintf __P((char *, int, char *, va_list)); /* vsprintf++ */
+size_t strlcpy __P((char *, const char *, size_t)); /* safe strcpy */
+size_t strlcat __P((char *, const char *, size_t)); /* safe strncpy */
+void dbglog __P((char *, ...)); /* log a debug message */
+void info __P((char *, ...)); /* log an informational message */
+void notice __P((char *, ...)); /* log a notice-level message */
+void warn __P((char *, ...)); /* log a warning message */
+void error __P((char *, ...)); /* log an error message */
+void fatal __P((char *, ...)); /* log an error message and die(1) */
+void init_pr_log __P((char *, int)); /* initialize for using pr_log */
+void pr_log __P((void *, char *, ...)); /* printer fn, output to syslog */
+void end_pr_log __P((void)); /* finish up after using pr_log */
+
+/* Procedures exported from auth.c */
+void link_required __P((int)); /* we are starting to use the link */
+void link_terminated __P((int)); /* we are finished with the link */
+void link_down __P((int)); /* the LCP layer has left the Opened state */
+void link_established __P((int)); /* the link is up; authenticate now */
+void start_networks __P((void)); /* start all the network control protos */
+void np_up __P((int, int)); /* a network protocol has come up */
+void np_down __P((int, int)); /* a network protocol has gone down */
+void np_finished __P((int, int)); /* a network protocol no longer needs link */
+void auth_peer_fail __P((int, int));
+ /* peer failed to authenticate itself */
+void auth_peer_success __P((int, int, char *, int));
+ /* peer successfully authenticated itself */
+void auth_withpeer_fail __P((int, int));
+ /* we failed to authenticate ourselves */
+void auth_withpeer_success __P((int, int));
+ /* we successfully authenticated ourselves */
+void auth_check_options __P((void));
+ /* check authentication options supplied */
+void auth_reset __P((int)); /* check what secrets we have */
+int check_passwd __P((int, char *, int, char *, int, char **));
+ /* Check peer-supplied username/password */
+int get_secret __P((int, char *, char *, char *, int *, int));
+ /* get "secret" for chap */
+int auth_ip_addr __P((int, u_int32_t));
+ /* check if IP address is authorized */
+int bad_ip_adrs __P((u_int32_t));
+ /* check if IP address is unreasonable */
+
+/* Procedures exported from demand.c */
+void demand_conf __P((void)); /* config interface(s) for demand-dial */
+void demand_block __P((void)); /* set all NPs to queue up packets */
+void demand_unblock __P((void)); /* set all NPs to pass packets */
+void demand_discard __P((void)); /* set all NPs to discard packets */
+void demand_rexmit __P((int)); /* retransmit saved frames for an NP */
+int loop_chars __P((unsigned char *, int)); /* process chars from loopback */
+int loop_frame __P((unsigned char *, int)); /* should we bring link up? */
+
+/* Procedures exported from multilink.c */
+void mp_check_options __P((void)); /* Check multilink-related options */
+int mp_join_bundle __P((void)); /* join our link to an appropriate bundle */
+char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */
+int str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */
+
+/* Procedures exported from sys-*.c */
+void sys_init __P((void)); /* Do system-dependent initialization */
+void sys_cleanup __P((void)); /* Restore system state before exiting */
+int sys_check_options __P((void)); /* Check options specified */
+void sys_close __P((void)); /* Clean up in a child before execing */
+int ppp_available __P((void)); /* Test whether ppp kernel support exists */
+int get_pty __P((int *, int *, char *, int)); /* Get pty master/slave */
+int open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */
+int tty_establish_ppp __P((int)); /* Turn serial port into a ppp interface */
+void tty_disestablish_ppp __P((int)); /* Restore port to normal operation */
+void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
+int bundle_attach __P((int)); /* Attach link to existing bundle */
+void cfg_bundle __P((int, int, int, int)); /* Configure existing bundle */
+void clean_check __P((void)); /* Check if line was 8-bit clean */
+void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */
+void restore_tty __P((int)); /* Restore port's original parameters */
+void setdtr __P((int, int)); /* Raise or lower port's DTR line */
+void output __P((int, u_char *, int)); /* Output a PPP packet */
+void wait_input __P((struct timeval *));
+ /* Wait for input, with timeout */
+void add_fd __P((int)); /* Add fd to set to wait for */
+void remove_fd __P((int)); /* Remove fd from set to wait for */
+int read_packet __P((u_char *)); /* Read PPP packet */
+int get_loop_output __P((void)); /* Read pkts from loopback */
+void tty_send_config __P((int, u_int32_t, int, int));
+ /* Configure i/f transmit parameters */
+void tty_set_xaccm __P((ext_accm));
+ /* Set extended transmit ACCM */
+void tty_recv_config __P((int, u_int32_t, int, int));
+ /* Configure i/f receive parameters */
+int ccp_test __P((int, u_char *, int, int));
+ /* Test support for compression scheme */
+void ccp_flags_set __P((int, int, int));
+ /* Set kernel CCP state */
+int ccp_fatal_error __P((int)); /* Test for fatal decomp error in kernel */
+int get_idle_time __P((int, struct ppp_idle *));
+ /* Find out how long link has been idle */
+int get_ppp_stats __P((int, struct pppd_stats *));
+ /* Return link statistics */
+void netif_set_mtu __P((int, int)); /* Set PPP interface MTU */
+int sifvjcomp __P((int, int, int, int));
+ /* Configure VJ TCP header compression */
+int sifup __P((int)); /* Configure i/f up for one protocol */
+int sifnpmode __P((int u, int proto, enum NPmode mode));
+ /* Set mode for handling packets for proto */
+int sifdown __P((int)); /* Configure i/f down for one protocol */
+int sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t));
+ /* Configure IPv4 addresses for i/f */
+int cifaddr __P((int, u_int32_t, u_int32_t));
+ /* Reset i/f IP addresses */
+#ifdef INET6
+int sif6addr __P((int, eui64_t, eui64_t));
+ /* Configure IPv6 addresses for i/f */
+int cif6addr __P((int, eui64_t, eui64_t));
+ /* Remove an IPv6 address from i/f */
+#endif
+int sifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Create default route through i/f */
+int cifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Delete default route through i/f */
+int sifproxyarp __P((int, u_int32_t));
+ /* Add proxy ARP entry for peer */
+int cifproxyarp __P((int, u_int32_t));
+ /* Delete proxy ARP entry for peer */
+u_int32_t GetMask __P((u_int32_t)); /* Get appropriate netmask for address */
+int lock __P((char *)); /* Create lock file for device */
+int relock __P((int)); /* Rewrite lock file with new pid */
+void unlock __P((void)); /* Delete previously-created lock file */
+void logwtmp __P((const char *, const char *, const char *));
+ /* Write entry to wtmp file */
+int get_host_seed __P((void)); /* Get host-dependent random number seed */
+int have_route_to __P((u_int32_t)); /* Check if route to addr exists */
+#ifdef PPP_FILTER
+int set_filters __P((struct bpf_program *pass, struct bpf_program *active));
+ /* Set filter programs in kernel */
+#endif
+#ifdef IPX_CHANGE
+int sipxfaddr __P((int, unsigned long, unsigned char *));
+int cipxfaddr __P((int));
+#endif
+int get_if_hwaddr __P((u_char *addr, char *name));
+char *get_first_ethernet __P((void));
+
+/* Procedures exported from options.c */
+int parse_args __P((int argc, char **argv));
+ /* Parse options from arguments given */
+int options_from_file __P((char *filename, int must_exist, int check_prot,
+ int privileged));
+ /* Parse options from an options file */
+int options_from_user __P((void)); /* Parse options from user's .ppprc */
+int options_for_tty __P((void)); /* Parse options from /etc/ppp/options.tty */
+int options_from_list __P((struct wordlist *, int privileged));
+ /* Parse options from a wordlist */
+int getword __P((FILE *f, char *word, int *newlinep, char *filename));
+ /* Read a word from a file */
+void option_error __P((char *fmt, ...));
+ /* Print an error message about an option */
+int int_option __P((char *, int *));
+ /* Simplified number_option for decimal ints */
+void add_options __P((option_t *)); /* Add extra options */
+void check_options __P((void)); /* check values after all options parsed */
+int override_value __P((const char *, int, const char *));
+ /* override value if permitted by priority */
+void print_options __P((void (*) __P((void *, char *, ...)), void *));
+ /* print out values of all options */
+
+int parse_dotted_ip __P((char *, u_int32_t *));
+
+/*
+ * Hooks to enable plugins to change various things.
+ */
+extern int (*new_phase_hook) __P((int));
+extern int (*idle_time_hook) __P((struct ppp_idle *));
+extern int (*holdoff_hook) __P((void));
+extern int (*pap_check_hook) __P((void));
+extern int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+ struct wordlist **paddrs,
+ struct wordlist **popts));
+extern void (*pap_logout_hook) __P((void));
+extern int (*pap_passwd_hook) __P((char *user, char *passwd));
+extern void (*ip_up_hook) __P((void));
+extern void (*ip_down_hook) __P((void));
+extern void (*ip_choose_hook) __P((u_int32_t *));
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+ (c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+ *(cp)++ = (u_char) (c); \
+}
+
+
+#define GETSHORT(s, cp) { \
+ (s) = *(cp)++ << 8; \
+ (s) |= *(cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+ *(cp)++ = (u_char) ((s) >> 8); \
+ *(cp)++ = (u_char) (s); \
+}
+
+#define GETLONG(l, cp) { \
+ (l) = *(cp)++ << 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; \
+}
+#define PUTLONG(l, cp) { \
+ *(cp)++ = (u_char) ((l) >> 24); \
+ *(cp)++ = (u_char) ((l) >> 16); \
+ *(cp)++ = (u_char) ((l) >> 8); \
+ *(cp)++ = (u_char) (l); \
+}
+
+#define INCPTR(n, cp) ((cp) += (n))
+#define DECPTR(n, cp) ((cp) -= (n))
+
+/*
+ * System dependent definitions for user-level 4.3BSD UNIX implementation.
+ */
+
+#define TIMEOUT(r, f, t) timeout((r), (f), (t), 0)
+#define UNTIMEOUT(r, f) untimeout((r), (f))
+
+#define BCOPY(s, d, l) memcpy(d, s, l)
+#define BZERO(s, n) memset(s, 0, n)
+
+#define PRINTMSG(m, l) { info("Remote message: %0.*v", l, m); }
+
+/*
+ * MAKEHEADER - Add Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+ PUTCHAR(PPP_ALLSTATIONS, p); \
+ PUTCHAR(PPP_UI, p); \
+ PUTSHORT(t, p); }
+
+/*
+ * Exit status values.
+ */
+#define EXIT_OK 0
+#define EXIT_FATAL_ERROR 1
+#define EXIT_OPTION_ERROR 2
+#define EXIT_NOT_ROOT 3
+#define EXIT_NO_KERNEL_SUPPORT 4
+#define EXIT_USER_REQUEST 5
+#define EXIT_LOCK_FAILED 6
+#define EXIT_OPEN_FAILED 7
+#define EXIT_CONNECT_FAILED 8
+#define EXIT_PTYCMD_FAILED 9
+#define EXIT_NEGOTIATION_FAILED 10
+#define EXIT_PEER_AUTH_FAILED 11
+#define EXIT_IDLE_TIMEOUT 12
+#define EXIT_CONNECT_TIME 13
+#define EXIT_CALLBACK 14
+#define EXIT_PEER_DEAD 15
+#define EXIT_HANGUP 16
+#define EXIT_LOOPBACK 17
+#define EXIT_INIT_FAILED 18
+#define EXIT_AUTH_TOPEER_FAILED 19
+
+/*
+ * Debug macros. Slightly useful for finding bugs in pppd, not particularly
+ * useful for finding out why your connection isn't being established.
+ */
+#ifdef DEBUGALL
+#define DEBUGMAIN 1
+#define DEBUGFSM 1
+#define DEBUGLCP 1
+#define DEBUGIPCP 1
+#define DEBUGIPV6CP 1
+#define DEBUGUPAP 1
+#define DEBUGCHAP 1
+#endif
+
+#ifndef LOG_PPP /* we use LOG_LOCAL2 for syslog by default */
+#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUGSYS) \
+ || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \
+ || defined(DEBUGCHAP) || defined(DEBUG) || defined(DEBUGIPV6CP)
+#define LOG_PPP LOG_LOCAL2
+#else
+#define LOG_PPP LOG_DAEMON
+#endif
+#endif /* LOG_PPP */
+
+#ifdef DEBUGMAIN
+#define MAINDEBUG(x) if (debug) dbglog x
+#else
+#define MAINDEBUG(x)
+#endif
+
+#ifdef DEBUGSYS
+#define SYSDEBUG(x) if (debug) dbglog x
+#else
+#define SYSDEBUG(x)
+#endif
+
+#ifdef DEBUGFSM
+#define FSMDEBUG(x) if (debug) dbglog x
+#else
+#define FSMDEBUG(x)
+#endif
+
+#ifdef DEBUGLCP
+#define LCPDEBUG(x) if (debug) dbglog x
+#else
+#define LCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPCP
+#define IPCPDEBUG(x) if (debug) dbglog x
+#else
+#define IPCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPV6CP
+#define IPV6CPDEBUG(x) if (debug) dbglog x
+#else
+#define IPV6CPDEBUG(x)
+#endif
+
+#ifdef DEBUGUPAP
+#define UPAPDEBUG(x) if (debug) dbglog x
+#else
+#define UPAPDEBUG(x)
+#endif
+
+#ifdef DEBUGCHAP
+#define CHAPDEBUG(x) if (debug) dbglog x
+#else
+#define CHAPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPXCP
+#define IPXCPDEBUG(x) if (debug) dbglog x
+#else
+#define IPXCPDEBUG(x)
+#endif
+
+#ifndef SIGTYPE
+#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE)
+#define SIGTYPE void
+#else
+#define SIGTYPE int
+#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */
+#endif /* SIGTYPE */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b)? (a): (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) ((a) > (b)? (a): (b))
+#endif
+
+#endif /* __PPP_H__ */
diff --git a/mdk-stage1/ppp/pppd/sys-linux.c b/mdk-stage1/ppp/pppd/sys-linux.c
new file mode 100644
index 000000000..d341bb3de
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/sys-linux.c
@@ -0,0 +1,2672 @@
+/*
+ * sys-linux.c - System-dependent procedures for setting up
+ * PPP interfaces on Linux systems
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/sysmacros.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <time.h>
+#include <memory.h>
+#include <utmp.h>
+#include <mntent.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+
+/* This is in netdevice.h. However, this compile will fail miserably if
+ you attempt to include netdevice.h because it has so many references
+ to __memcpy functions which it should not attempt to do. So, since I
+ really don't use it, but it must be defined, define it now. */
+
+#ifndef MAX_ADDR_LEN
+#define MAX_ADDR_LEN 7
+#endif
+
+#if __GLIBC__ >= 2
+#include <asm/types.h> /* glibc 2 conflicts with linux/types.h */
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/route.h>
+#include <netinet/if_ether.h>
+#else
+#include <linux/types.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/route.h>
+#include <linux/if_ether.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#if __GLIBC__ >= 2 && \
+ !(defined(__powerpc__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
+#include <netipx/ipx.h>
+#else
+#include <linux/ipx.h>
+#endif
+#endif /* IPX_CHANGE */
+
+#ifdef PPP_FILTER
+#include <net/bpf.h>
+#include <linux/filter.h>
+#endif /* PPP_FILTER */
+
+#ifdef LOCKLIB
+#include <sys/locks.h>
+#endif
+
+#ifdef INET6
+#ifndef _LINUX_IN6_H
+/*
+ * This is in linux/include/net/ipv6.h.
+ */
+
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ __u32 ifr6_prefixlen;
+ unsigned int ifr6_ifindex;
+};
+#endif
+
+#define IN6_LLADDR_FROM_EUI64(sin6, eui64) do { \
+ memset(&sin6.s6_addr, 0, sizeof(struct in6_addr)); \
+ sin6.s6_addr16[0] = htons(0xfe80); \
+ eui64_copy(eui64, sin6.s6_addr32[2]); \
+ } while (0)
+
+#endif /* INET6 */
+
+/* We can get an EIO error on an ioctl if the modem has hung up */
+#define ok_error(num) ((num)==EIO)
+
+static int tty_disc = N_TTY; /* The TTY discipline */
+static int ppp_disc = N_PPP; /* The PPP discpline */
+static int initfdflags = -1; /* Initial file descriptor flags for fd */
+static int ppp_fd = -1; /* fd which is set to PPP discipline */
+static int sock_fd = -1; /* socket for doing interface ioctls */
+static int slave_fd = -1;
+static int master_fd = -1;
+#ifdef INET6
+static int sock6_fd = -1;
+#endif /* INET6 */
+static int ppp_dev_fd = -1; /* fd for /dev/ppp (new style driver) */
+static int chindex; /* channel index (new style driver) */
+
+static fd_set in_fds; /* set of fds that wait_input waits for */
+static int max_in_fd; /* highest fd set in in_fds */
+
+static int has_proxy_arp = 0;
+static int driver_version = 0;
+static int driver_modification = 0;
+static int driver_patch = 0;
+static int driver_is_old = 0;
+static int restore_term = 0; /* 1 => we've munged the terminal */
+static struct termios inittermios; /* Initial TTY termios */
+
+static int new_style_driver = 0;
+
+static char loop_name[20];
+static unsigned char inbuf[512]; /* buffer for chars read from loopback */
+
+static int if_is_up; /* Interface has been marked up */
+static u_int32_t default_route_gateway; /* Gateway for default route added */
+static u_int32_t proxy_arp_addr; /* Addr for proxy arp entry added */
+static char proxy_arp_dev[16]; /* Device for proxy arp entry */
+static u_int32_t our_old_addr; /* for detecting address changes */
+static int dynaddr_set; /* 1 if ip_dynaddr set */
+static int looped; /* 1 if using loop */
+static int link_mtu; /* mtu for the link (not bundle) */
+
+static struct utsname utsname; /* for the kernel version */
+static int kernel_version;
+#define KVERSION(j,n,p) ((j)*1000000 + (n)*1000 + (p))
+
+#define MAX_IFS 100
+
+#define FLAGS_GOOD (IFF_UP | IFF_BROADCAST)
+#define FLAGS_MASK (IFF_UP | IFF_BROADCAST | \
+ IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP)
+
+#define SIN_ADDR(x) (((struct sockaddr_in *) (&(x)))->sin_addr.s_addr)
+
+/* Prototypes for procedures local to this file. */
+static int get_flags (int fd);
+static void set_flags (int fd, int flags);
+static int translate_speed (int bps);
+static int baud_rate_of (int speed);
+static void close_route_table (void);
+static int open_route_table (void);
+static int read_route_table (struct rtentry *rt);
+static int defaultroute_exists (struct rtentry *rt);
+static int get_ether_addr (u_int32_t ipaddr, struct sockaddr *hwaddr,
+ char *name, int namelen);
+static void decode_version (char *buf, int *version, int *mod, int *patch);
+static int set_kdebugflag(int level);
+static int ppp_registered(void);
+static int make_ppp_unit(void);
+static void restore_loop(void); /* Transfer ppp unit back to loopback */
+
+extern u_char inpacket_buf[]; /* borrowed from main.c */
+
+/*
+ * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
+ * if it exists.
+ */
+
+#define SET_SA_FAMILY(addr, family) \
+ memset ((char *) &(addr), '\0', sizeof(addr)); \
+ addr.sa_family = (family);
+
+/*
+ * Determine if the PPP connection should still be present.
+ */
+
+extern int hungup;
+
+/* new_fd is the fd of a tty */
+static void set_ppp_fd (int new_fd)
+{
+ SYSDEBUG ((LOG_DEBUG, "setting ppp_fd to %d\n", new_fd));
+ ppp_fd = new_fd;
+ if (!new_style_driver)
+ ppp_dev_fd = new_fd;
+}
+
+static int still_ppp(void)
+{
+ if (new_style_driver)
+ return !hungup && ppp_fd >= 0;
+ if (!hungup || ppp_fd == slave_fd)
+ return 1;
+ if (slave_fd >= 0) {
+ set_ppp_fd(slave_fd);
+ return 1;
+ }
+ return 0;
+}
+
+/********************************************************************
+ *
+ * Functions to read and set the flags value in the device driver
+ */
+
+static int get_flags (int fd)
+{
+ int flags;
+
+ if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) {
+ if ( ok_error (errno) )
+ flags = 0;
+ else
+ fatal("ioctl(PPPIOCGFLAGS): %m");
+ }
+
+ SYSDEBUG ((LOG_DEBUG, "get flags = %x\n", flags));
+ return flags;
+}
+
+/********************************************************************/
+
+static void set_flags (int fd, int flags)
+{
+ SYSDEBUG ((LOG_DEBUG, "set flags = %x\n", flags));
+
+ if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &flags) < 0) {
+ if (! ok_error (errno) )
+ fatal("ioctl(PPPIOCSFLAGS, %x): %m", flags, errno);
+ }
+}
+
+/********************************************************************
+ *
+ * sys_init - System-dependent initialization.
+ */
+
+void sys_init(void)
+{
+ int flags;
+
+ if (new_style_driver) {
+ ppp_dev_fd = open("/dev/ppp", O_RDWR);
+ if (ppp_dev_fd < 0)
+ fatal("Couldn't open /dev/ppp: %m");
+ flags = fcntl(ppp_dev_fd, F_GETFL);
+ if (flags == -1
+ || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("Couldn't set /dev/ppp to nonblock: %m");
+ }
+
+ /* Get an internet socket for doing socket ioctls. */
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0)
+ fatal("Couldn't create IP socket: %m(%d)", errno);
+
+#ifdef INET6
+ sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock6_fd < 0)
+ sock6_fd = -errno; /* save errno for later */
+#endif
+
+ FD_ZERO(&in_fds);
+ max_in_fd = 0;
+}
+
+/********************************************************************
+ *
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This shouldn't call die() because it's called from die().
+ */
+
+void sys_cleanup(void)
+{
+/*
+ * Take down the device
+ */
+ if (if_is_up) {
+ if_is_up = 0;
+ sifdown(0);
+ }
+/*
+ * Delete any routes through the device.
+ */
+ if (default_route_gateway != 0)
+ cifdefaultroute(0, 0, default_route_gateway);
+
+ if (has_proxy_arp)
+ cifproxyarp(0, proxy_arp_addr);
+}
+
+/********************************************************************
+ *
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close(void)
+{
+ if (new_style_driver)
+ close(ppp_dev_fd);
+ if (sock_fd >= 0)
+ close(sock_fd);
+ if (slave_fd >= 0)
+ close(slave_fd);
+ if (master_fd >= 0)
+ close(master_fd);
+ closelog();
+}
+
+/********************************************************************
+ *
+ * set_kdebugflag - Define the debugging level for the kernel
+ */
+
+static int set_kdebugflag (int requested_level)
+{
+ if (new_style_driver && ifunit < 0)
+ return 1;
+ if (ioctl(ppp_dev_fd, PPPIOCSDEBUG, &requested_level) < 0) {
+ if ( ! ok_error (errno) )
+ error("ioctl(PPPIOCSDEBUG): %m");
+ return (0);
+ }
+ SYSDEBUG ((LOG_INFO, "set kernel debugging level to %d",
+ requested_level));
+ return (1);
+}
+
+/********************************************************************
+ *
+ * tty_establish_ppp - Turn the serial port into a ppp interface.
+ */
+
+int tty_establish_ppp (int tty_fd)
+{
+ int x;
+ int fd = -1;
+
+/*
+ * Ensure that the tty device is in exclusive mode.
+ */
+ if (ioctl(tty_fd, TIOCEXCL, 0) < 0) {
+ if ( ! ok_error ( errno ))
+ warn("Couldn't make tty exclusive: %m");
+ }
+/*
+ * Demand mode - prime the old ppp device to relinquish the unit.
+ */
+ if (!new_style_driver && looped
+ && ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
+ error("ioctl(transfer ppp unit): %m");
+ return -1;
+ }
+/*
+ * Set the current tty to the PPP discpline
+ */
+
+#ifndef N_SYNC_PPP
+#define N_SYNC_PPP 14
+#endif
+ ppp_disc = (new_style_driver && sync_serial)? N_SYNC_PPP: N_PPP;
+ if (ioctl(tty_fd, TIOCSETD, &ppp_disc) < 0) {
+ if ( ! ok_error (errno) ) {
+ error("Couldn't set tty to PPP discipline: %m");
+ return -1;
+ }
+ }
+
+ if (new_style_driver) {
+ /* Open another instance of /dev/ppp and connect the channel to it */
+ int flags;
+
+ if (ioctl(tty_fd, PPPIOCGCHAN, &chindex) == -1) {
+ error("Couldn't get channel number: %m");
+ goto err;
+ }
+ dbglog("using channel %d", chindex);
+ fd = open("/dev/ppp", O_RDWR);
+ if (fd < 0) {
+ error("Couldn't reopen /dev/ppp: %m");
+ goto err;
+ }
+ if (ioctl(fd, PPPIOCATTCHAN, &chindex) < 0) {
+ error("Couldn't attach to channel %d: %m", chindex);
+ goto err_close;
+ }
+ flags = fcntl(fd, F_GETFL);
+ if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("Couldn't set /dev/ppp (channel) to nonblock: %m");
+ set_ppp_fd(fd);
+
+ if (!looped)
+ ifunit = -1;
+ if (!looped && !multilink) {
+ /*
+ * Create a new PPP unit.
+ */
+ if (make_ppp_unit() < 0)
+ goto err_close;
+ }
+
+ if (looped)
+ set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) & ~SC_LOOP_TRAFFIC);
+
+ if (!multilink) {
+ add_fd(ppp_dev_fd);
+ if (ioctl(fd, PPPIOCCONNECT, &ifunit) < 0) {
+ error("Couldn't attach to PPP unit %d: %m", ifunit);
+ goto err_close;
+ }
+ }
+
+ } else {
+ /*
+ * Old-style driver: find out which interface we were given.
+ */
+ set_ppp_fd (tty_fd);
+ if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) {
+ if (ok_error (errno))
+ goto err;
+ fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+ }
+ /* Check that we got the same unit again. */
+ if (looped && x != ifunit)
+ fatal("transfer_ppp failed: wanted unit %d, got %d", ifunit, x);
+ ifunit = x;
+
+ /*
+ * Fetch the initial file flags and reset blocking mode on the file.
+ */
+ initfdflags = fcntl(tty_fd, F_GETFL);
+ if (initfdflags == -1 ||
+ fcntl(tty_fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+ if ( ! ok_error (errno))
+ warn("Couldn't set device to non-blocking mode: %m");
+ }
+ }
+
+ looped = 0;
+
+ /*
+ * Enable debug in the driver if requested.
+ */
+ if (!looped)
+ set_kdebugflag (kdebugflag);
+
+#define SC_RCVB (SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
+#define SC_LOGB (SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
+ | SC_LOG_FLUSH)
+
+ set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB))
+ | ((kdebugflag * SC_DEBUG) & SC_LOGB)));
+
+ SYSDEBUG ((LOG_NOTICE, "Using version %d.%d.%d of PPP driver",
+ driver_version, driver_modification, driver_patch));
+
+ return ppp_fd;
+
+ err_close:
+ close(fd);
+ err:
+ if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
+ warn("Couldn't reset tty to normal line discipline: %m");
+ return -1;
+}
+
+/********************************************************************
+ *
+ * tty_disestablish_ppp - Restore the serial port to normal operation,
+ * and reconnect the ppp unit to the loopback if in demand mode.
+ * This shouldn't call die() because it's called from die().
+ */
+
+void tty_disestablish_ppp(int tty_fd)
+{
+ if (demand)
+ restore_loop();
+ if (!hungup) {
+/*
+ * Flush the tty output buffer so that the TIOCSETD doesn't hang.
+ */
+ if (tcflush(tty_fd, TCIOFLUSH) < 0)
+ warn("tcflush failed: %m");
+/*
+ * Restore the previous line discipline
+ */
+ if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0) {
+ if ( ! ok_error (errno))
+ error("ioctl(TIOCSETD, N_TTY): %m");
+ }
+
+ if (ioctl(tty_fd, TIOCNXCL, 0) < 0) {
+ if ( ! ok_error (errno))
+ warn("ioctl(TIOCNXCL): %m(%d)", errno);
+ }
+
+ /* Reset non-blocking mode on fd. */
+ if (initfdflags != -1 && fcntl(tty_fd, F_SETFL, initfdflags) < 0) {
+ if ( ! ok_error (errno))
+ warn("Couldn't restore device fd flags: %m");
+ }
+ }
+ initfdflags = -1;
+
+ if (new_style_driver) {
+ close(ppp_fd);
+ ppp_fd = -1;
+ if (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
+ error("Couldn't release PPP unit: %m");
+ if (!multilink)
+ remove_fd(ppp_dev_fd);
+ }
+}
+
+/*
+ * make_ppp_unit - make a new ppp unit for ppp_dev_fd.
+ * Assumes new_style_driver.
+ */
+static int make_ppp_unit()
+{
+ int x;
+
+ ifunit = req_unit;
+ x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+ if (x < 0 && req_unit >= 0 && errno == EEXIST) {
+ warn("Couldn't allocate PPP unit %d as it is already in use");
+ ifunit = -1;
+ x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+ }
+ if (x < 0)
+ error("Couldn't create new ppp unit: %m");
+ return x;
+}
+
+/*
+ * cfg_bundle - configure the existing bundle.
+ * Used in demand mode.
+ */
+void cfg_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+ int flags;
+
+ if (!new_style_driver)
+ return;
+
+ /* set the mrru, mtu and flags */
+ if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
+ error("Couldn't set MRRU: %m");
+ flags = get_flags(ppp_dev_fd);
+ flags &= ~(SC_MP_SHORTSEQ | SC_MP_XSHORTSEQ);
+ flags |= (rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0)
+ | (mrru? SC_MULTILINK: 0);
+
+ set_flags(ppp_dev_fd, flags);
+
+ /* connect up the channel */
+ if (ioctl(ppp_fd, PPPIOCCONNECT, &ifunit) < 0)
+ fatal("Couldn't attach to PPP unit %d: %m", ifunit);
+ add_fd(ppp_dev_fd);
+}
+
+/*
+ * make_new_bundle - create a new PPP unit (i.e. a bundle)
+ * and connect our channel to it. This should only get called
+ * if `multilink' was set at the time establish_ppp was called.
+ * In demand mode this uses our existing bundle instead of making
+ * a new one.
+ */
+void make_new_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+ if (!new_style_driver)
+ return;
+
+ /* make us a ppp unit */
+ if (make_ppp_unit() < 0)
+ die(1);
+
+ /* set the mrru and flags */
+ cfg_bundle(mrru, mtru, rssn, tssn);
+}
+
+/*
+ * bundle_attach - attach our link to a given PPP unit.
+ * We assume the unit is controlled by another pppd.
+ */
+int bundle_attach(int ifnum)
+{
+ if (!new_style_driver)
+ return -1;
+
+ if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
+ if (errno == ENXIO)
+ return 0; /* doesn't still exist */
+ fatal("Couldn't attach to interface unit %d: %m\n", ifnum);
+ }
+ if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0)
+ fatal("Couldn't connect to interface unit %d: %m", ifnum);
+ set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
+
+ ifunit = ifnum;
+ return 1;
+}
+
+/********************************************************************
+ *
+ * clean_check - Fetch the flags for the device and generate
+ * appropriate error messages.
+ */
+void clean_check(void)
+{
+ int x;
+ char *s;
+
+ if (still_ppp()) {
+ if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) {
+ s = NULL;
+ switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) {
+ case SC_RCV_B7_0:
+ s = "all had bit 7 set to 1";
+ break;
+
+ case SC_RCV_B7_1:
+ s = "all had bit 7 set to 0";
+ break;
+
+ case SC_RCV_EVNP:
+ s = "all had odd parity";
+ break;
+
+ case SC_RCV_ODDP:
+ s = "all had even parity";
+ break;
+ }
+
+ if (s != NULL) {
+ warn("Receive serial link is not 8-bit clean:");
+ warn("Problem: %s", s);
+ }
+ }
+ }
+}
+
+
+/*
+ * List of valid speeds.
+ */
+
+struct speed {
+ int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+ { 50, B50 },
+#endif
+#ifdef B75
+ { 75, B75 },
+#endif
+#ifdef B110
+ { 110, B110 },
+#endif
+#ifdef B134
+ { 134, B134 },
+#endif
+#ifdef B150
+ { 150, B150 },
+#endif
+#ifdef B200
+ { 200, B200 },
+#endif
+#ifdef B300
+ { 300, B300 },
+#endif
+#ifdef B600
+ { 600, B600 },
+#endif
+#ifdef B1200
+ { 1200, B1200 },
+#endif
+#ifdef B1800
+ { 1800, B1800 },
+#endif
+#ifdef B2000
+ { 2000, B2000 },
+#endif
+#ifdef B2400
+ { 2400, B2400 },
+#endif
+#ifdef B3600
+ { 3600, B3600 },
+#endif
+#ifdef B4800
+ { 4800, B4800 },
+#endif
+#ifdef B7200
+ { 7200, B7200 },
+#endif
+#ifdef B9600
+ { 9600, B9600 },
+#endif
+#ifdef B19200
+ { 19200, B19200 },
+#endif
+#ifdef B38400
+ { 38400, B38400 },
+#endif
+#ifdef B57600
+ { 57600, B57600 },
+#endif
+#ifdef B76800
+ { 76800, B76800 },
+#endif
+#ifdef B115200
+ { 115200, B115200 },
+#endif
+#ifdef EXTA
+ { 19200, EXTA },
+#endif
+#ifdef EXTB
+ { 38400, EXTB },
+#endif
+#ifdef B230400
+ { 230400, B230400 },
+#endif
+#ifdef B460800
+ { 460800, B460800 },
+#endif
+#ifdef B921600
+ { 921600, B921600 },
+#endif
+ { 0, 0 }
+};
+
+/********************************************************************
+ *
+ * Translate from bits/second to a speed_t.
+ */
+
+static int translate_speed (int bps)
+{
+ struct speed *speedp;
+
+ if (bps != 0) {
+ for (speedp = speeds; speedp->speed_int; speedp++) {
+ if (bps == speedp->speed_int)
+ return speedp->speed_val;
+ }
+ warn("speed %d not supported", bps);
+ }
+ return 0;
+}
+
+/********************************************************************
+ *
+ * Translate from a speed_t to bits/second.
+ */
+
+static int baud_rate_of (int speed)
+{
+ struct speed *speedp;
+
+ if (speed != 0) {
+ for (speedp = speeds; speedp->speed_int; speedp++) {
+ if (speed == speedp->speed_val)
+ return speedp->speed_int;
+ }
+ }
+ return 0;
+}
+
+/********************************************************************
+ *
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc. If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+
+void set_up_tty(int tty_fd, int local)
+{
+ int speed;
+ struct termios tios;
+
+ setdtr(tty_fd, 1);
+ if (tcgetattr(tty_fd, &tios) < 0) {
+ if (!ok_error(errno))
+ fatal("tcgetattr: %m(%d)", errno);
+ return;
+ }
+
+ if (!restore_term)
+ inittermios = tios;
+
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+ tios.c_cflag |= CS8 | CREAD | HUPCL;
+
+ tios.c_iflag = IGNBRK | IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+
+ if (local || !modem)
+ tios.c_cflag ^= (CLOCAL | HUPCL);
+
+ switch (crtscts) {
+ case 1:
+ tios.c_cflag |= CRTSCTS;
+ break;
+
+ case -2:
+ tios.c_iflag |= IXON | IXOFF;
+ tios.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */
+ tios.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */
+ break;
+
+ case -1:
+ tios.c_cflag &= ~CRTSCTS;
+ break;
+
+ default:
+ break;
+ }
+
+ speed = translate_speed(inspeed);
+ if (speed) {
+ cfsetospeed (&tios, speed);
+ cfsetispeed (&tios, speed);
+ }
+/*
+ * We can't proceed if the serial port speed is B0,
+ * since that implies that the serial port is disabled.
+ */
+ else {
+ speed = cfgetospeed(&tios);
+ if (speed == B0)
+ fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
+ }
+
+ if (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0)
+ if (!ok_error(errno))
+ fatal("tcsetattr: %m");
+
+ baud_rate = baud_rate_of(speed);
+ restore_term = 1;
+}
+
+/********************************************************************
+ *
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+
+void setdtr (int tty_fd, int on)
+{
+ int modembits = TIOCM_DTR;
+
+ ioctl(tty_fd, (on ? TIOCMBIS : TIOCMBIC), &modembits);
+}
+
+/********************************************************************
+ *
+ * restore_tty - restore the terminal to the saved settings.
+ */
+
+void restore_tty (int tty_fd)
+{
+ if (restore_term) {
+ restore_term = 0;
+/*
+ * Turn off echoing, because otherwise we can get into
+ * a loop with the tty and the modem echoing to each other.
+ * We presume we are the sole user of this tty device, so
+ * when we close it, it will revert to its defaults anyway.
+ */
+ if (!default_device)
+ inittermios.c_lflag &= ~(ECHO | ECHONL);
+
+ if (tcsetattr(tty_fd, TCSAFLUSH, &inittermios) < 0) {
+ if (! ok_error (errno))
+ warn("tcsetattr: %m");
+ }
+ }
+}
+
+/********************************************************************
+ *
+ * output - Output PPP packet.
+ */
+
+void output (int unit, unsigned char *p, int len)
+{
+ int fd = ppp_fd;
+ int proto;
+
+ if (debug)
+ dbglog("sent %P", p, len);
+
+ if (len < PPP_HDRLEN)
+ return;
+ if (new_style_driver) {
+ p += 2;
+ len -= 2;
+ proto = (p[0] << 8) + p[1];
+ if (ifunit >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
+ fd = ppp_dev_fd;
+ }
+ if (write(fd, p, len) < 0) {
+ if (errno == EWOULDBLOCK || errno == ENOBUFS
+ || errno == ENXIO || errno == EIO || errno == EINTR)
+ warn("write: warning: %m (%d)", errno);
+ else
+ error("write: %m (%d)", errno);
+ }
+}
+
+/********************************************************************
+ *
+ * wait_input - wait until there is data available,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+
+void wait_input(struct timeval *timo)
+{
+ fd_set ready, exc;
+ int n;
+
+ ready = in_fds;
+ exc = in_fds;
+ n = select(max_in_fd + 1, &ready, NULL, &exc, timo);
+ if (n < 0 && errno != EINTR)
+ fatal("select: %m(%d)", errno);
+}
+
+/*
+ * add_fd - add an fd to the set that wait_input waits for.
+ */
+void add_fd(int fd)
+{
+ FD_SET(fd, &in_fds);
+ if (fd > max_in_fd)
+ max_in_fd = fd;
+}
+
+/*
+ * remove_fd - remove an fd from the set that wait_input waits for.
+ */
+void remove_fd(int fd)
+{
+ FD_CLR(fd, &in_fds);
+}
+
+
+/********************************************************************
+ *
+ * read_packet - get a PPP packet from the serial device.
+ */
+
+int read_packet (unsigned char *buf)
+{
+ int len, nr;
+
+ len = PPP_MRU + PPP_HDRLEN;
+ if (new_style_driver) {
+ *buf++ = PPP_ALLSTATIONS;
+ *buf++ = PPP_UI;
+ len -= 2;
+ }
+ nr = -1;
+ if (ppp_fd >= 0) {
+ nr = read(ppp_fd, buf, len);
+ if (nr < 0 && errno != EWOULDBLOCK && errno != EIO && errno != EINTR)
+ error("read: %m");
+ if (nr < 0 && errno == ENXIO)
+ return 0;
+ }
+ if (nr < 0 && new_style_driver && ifunit >= 0) {
+ /* N.B. we read ppp_fd first since LCP packets come in there. */
+ nr = read(ppp_dev_fd, buf, len);
+ if (nr < 0 && errno != EWOULDBLOCK && errno != EIO && errno != EINTR)
+ error("read /dev/ppp: %m");
+ if (nr < 0 && errno == ENXIO)
+ return 0;
+ }
+ return (new_style_driver && nr > 0)? nr+2: nr;
+}
+
+/********************************************************************
+ *
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output(void)
+{
+ int rv = 0;
+ int n;
+
+ if (new_style_driver) {
+ while ((n = read_packet(inpacket_buf)) > 0)
+ if (loop_frame(inpacket_buf, n))
+ rv = 1;
+ return rv;
+ }
+
+ while ((n = read(master_fd, inbuf, sizeof(inbuf))) > 0)
+ if (loop_chars(inbuf, n))
+ rv = 1;
+
+ if (n == 0)
+ fatal("eof on loopback");
+
+ if (errno != EWOULDBLOCK)
+ fatal("read from loopback: %m(%d)", errno);
+
+ return rv;
+}
+
+/*
+ * netif_set_mtu - set the MTU on the PPP network interface.
+ */
+void
+netif_set_mtu(int unit, int mtu)
+{
+ struct ifreq ifr;
+
+ SYSDEBUG ((LOG_DEBUG, "netif_set_mtu: mtu = %d\n", mtu));
+
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_mtu = mtu;
+
+ if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
+ fatal("ioctl(SIOCSIFMTU): %m");
+}
+
+/********************************************************************
+ *
+ * tty_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+
+void tty_send_config (int mtu,u_int32_t asyncmap,int pcomp,int accomp)
+{
+ u_int x;
+
+/*
+ * Set the asyncmap and other parameters for the ppp device
+ */
+ if (!still_ppp())
+ return;
+ link_mtu = mtu;
+ SYSDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap));
+ if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
+ if (!ok_error(errno))
+ fatal("ioctl(PPPIOCSASYNCMAP): %m(%d)", errno);
+ return;
+ }
+
+ x = get_flags(ppp_fd);
+ x = pcomp ? x | SC_COMP_PROT : x & ~SC_COMP_PROT;
+ x = accomp ? x | SC_COMP_AC : x & ~SC_COMP_AC;
+ x = sync_serial ? x | SC_SYNC : x & ~SC_SYNC;
+ set_flags(ppp_fd, x);
+}
+
+/********************************************************************
+ *
+ * tty_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+
+void tty_set_xaccm (ext_accm accm)
+{
+ SYSDEBUG ((LOG_DEBUG, "set_xaccm: %08lx %08lx %08lx %08lx\n",
+ accm[0], accm[1], accm[2], accm[3]));
+
+ if (!still_ppp())
+ return;
+ if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) {
+ if ( ! ok_error (errno))
+ warn("ioctl(set extended ACCM): %m(%d)", errno);
+ }
+}
+
+/********************************************************************
+ *
+ * tty_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+
+void tty_recv_config (int mru,u_int32_t asyncmap,int pcomp,int accomp)
+{
+ SYSDEBUG ((LOG_DEBUG, "recv_config: mru = %d\n", mru));
+/*
+ * If we were called because the link has gone down then there is nothing
+ * which may be done. Just return without incident.
+ */
+ if (!still_ppp())
+ return;
+/*
+ * Set the receiver parameters
+ */
+ if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
+ if ( ! ok_error (errno))
+ error("ioctl(PPPIOCSMRU): %m(%d)", errno);
+ }
+ if (new_style_driver && ifunit >= 0
+ && ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0)
+ error("Couldn't set MRU in generic PPP layer: %m");
+
+ SYSDEBUG ((LOG_DEBUG, "recv_config: asyncmap = %lx\n", asyncmap));
+ if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
+ if (!ok_error(errno))
+ error("ioctl(PPPIOCSRASYNCMAP): %m(%d)", errno);
+ }
+}
+
+/********************************************************************
+ *
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+
+int ccp_test (int unit, u_char *opt_ptr, int opt_len, int for_transmit)
+{
+ struct ppp_option_data data;
+
+ memset (&data, '\0', sizeof (data));
+ data.ptr = opt_ptr;
+ data.length = opt_len;
+ data.transmit = for_transmit;
+
+ if (ioctl(ppp_dev_fd, PPPIOCSCOMPRESS, (caddr_t) &data) >= 0)
+ return 1;
+
+ return (errno == ENOBUFS)? 0: -1;
+}
+
+/********************************************************************
+ *
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+
+void ccp_flags_set (int unit, int isopen, int isup)
+{
+ if (still_ppp()) {
+ int x = get_flags(ppp_dev_fd);
+ x = isopen? x | SC_CCP_OPEN : x &~ SC_CCP_OPEN;
+ x = isup? x | SC_CCP_UP : x &~ SC_CCP_UP;
+ set_flags (ppp_dev_fd, x);
+ }
+}
+
+#ifdef PPP_FILTER
+/*
+ * set_filters - set the active and pass filters in the kernel driver.
+ */
+int set_filters(struct bpf_program *pass, struct bpf_program *active)
+{
+ struct sock_fprog fp;
+
+ fp.len = pass->bf_len;
+ fp.filter = (struct sock_filter *) pass->bf_insns;
+ if (ioctl(ppp_dev_fd, PPPIOCSPASS, &fp) < 0) {
+ if (errno == ENOTTY)
+ warn("kernel does not support PPP filtering");
+ else
+ error("Couldn't set pass-filter in kernel: %m");
+ return 0;
+ }
+ fp.len = active->bf_len;
+ fp.filter = (struct sock_filter *) active->bf_insns;
+ if (ioctl(ppp_dev_fd, PPPIOCSACTIVE, &fp) < 0) {
+ error("Couldn't set active-filter in kernel: %m");
+ return 0;
+ }
+ return 1;
+}
+#endif /* PPP_FILTER */
+
+/********************************************************************
+ *
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+ int u;
+ struct ppp_idle *ip;
+{
+ return ioctl(ppp_dev_fd, PPPIOCGIDLE, ip) >= 0;
+}
+
+/********************************************************************
+ *
+ * get_ppp_stats - return statistics for the link.
+ */
+int
+get_ppp_stats(u, stats)
+ int u;
+ struct pppd_stats *stats;
+{
+ struct ifpppstatsreq req;
+
+ memset (&req, 0, sizeof (req));
+
+ req.stats_ptr = (caddr_t) &req.stats;
+ strlcpy(req.ifr__name, ifname, sizeof(req.ifr__name));
+ if (ioctl(sock_fd, SIOCGPPPSTATS, &req) < 0) {
+ error("Couldn't get PPP statistics: %m");
+ return 0;
+ }
+ stats->bytes_in = req.stats.p.ppp_ibytes;
+ stats->bytes_out = req.stats.p.ppp_obytes;
+ return 1;
+}
+
+/********************************************************************
+ *
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise. This is necessary because of patent nonsense.
+ */
+
+int ccp_fatal_error (int unit)
+{
+ int x = get_flags(ppp_dev_fd);
+
+ return x & SC_DC_FERROR;
+}
+
+/********************************************************************
+ *
+ * path_to_procfs - find the path to the proc file system mount point
+ */
+static char proc_path[MAXPATHLEN];
+static int proc_path_len;
+
+static char *path_to_procfs(const char *tail)
+{
+ struct mntent *mntent;
+ FILE *fp;
+
+ if (proc_path_len == 0) {
+ /* Default the mount location of /proc */
+ strlcpy (proc_path, "/proc", sizeof(proc_path));
+ proc_path_len = 5;
+ fp = fopen(MOUNTED, "r");
+ if (fp != NULL) {
+ while ((mntent = getmntent(fp)) != NULL) {
+ if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0)
+ continue;
+ if (strcmp(mntent->mnt_type, "proc") == 0) {
+ strlcpy(proc_path, mntent->mnt_dir, sizeof(proc_path));
+ proc_path_len = strlen(proc_path);
+ break;
+ }
+ }
+ fclose (fp);
+ }
+ }
+
+ strlcpy(proc_path + proc_path_len, tail,
+ sizeof(proc_path) - proc_path_len);
+ return proc_path;
+}
+
+/*
+ * /proc/net/route parsing stuff.
+ */
+#define ROUTE_MAX_COLS 12
+FILE *route_fd = (FILE *) 0;
+static char route_buffer[512];
+static int route_dev_col, route_dest_col, route_gw_col;
+static int route_flags_col, route_mask_col;
+static int route_num_cols;
+
+static int open_route_table (void);
+static void close_route_table (void);
+static int read_route_table (struct rtentry *rt);
+
+/********************************************************************
+ *
+ * close_route_table - close the interface to the route table
+ */
+
+static void close_route_table (void)
+{
+ if (route_fd != (FILE *) 0) {
+ fclose (route_fd);
+ route_fd = (FILE *) 0;
+ }
+}
+
+/********************************************************************
+ *
+ * open_route_table - open the interface to the route table
+ */
+static char route_delims[] = " \t\n";
+
+static int open_route_table (void)
+{
+ char *path;
+
+ close_route_table();
+
+ path = path_to_procfs("/net/route");
+ route_fd = fopen (path, "r");
+ if (route_fd == NULL) {
+ error("can't open routing table %s: %m", path);
+ return 0;
+ }
+
+ route_dev_col = 0; /* default to usual columns */
+ route_dest_col = 1;
+ route_gw_col = 2;
+ route_flags_col = 3;
+ route_mask_col = 7;
+ route_num_cols = 8;
+
+ /* parse header line */
+ if (fgets(route_buffer, sizeof(route_buffer), route_fd) != 0) {
+ char *p = route_buffer, *q;
+ int col;
+ for (col = 0; col < ROUTE_MAX_COLS; ++col) {
+ int used = 1;
+ if ((q = strtok(p, route_delims)) == 0)
+ break;
+ if (strcasecmp(q, "iface") == 0)
+ route_dev_col = col;
+ else if (strcasecmp(q, "destination") == 0)
+ route_dest_col = col;
+ else if (strcasecmp(q, "gateway") == 0)
+ route_gw_col = col;
+ else if (strcasecmp(q, "flags") == 0)
+ route_flags_col = col;
+ else if (strcasecmp(q, "mask") == 0)
+ route_mask_col = col;
+ else
+ used = 0;
+ if (used && col >= route_num_cols)
+ route_num_cols = col + 1;
+ p = NULL;
+ }
+ }
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * read_route_table - read the next entry from the route table
+ */
+
+static int read_route_table(struct rtentry *rt)
+{
+ char *cols[ROUTE_MAX_COLS], *p;
+ int col;
+
+ memset (rt, '\0', sizeof (struct rtentry));
+
+ if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0)
+ return 0;
+
+ p = route_buffer;
+ for (col = 0; col < route_num_cols; ++col) {
+ cols[col] = strtok(p, route_delims);
+ if (cols[col] == NULL)
+ return 0; /* didn't get enough columns */
+ p = NULL;
+ }
+
+ SIN_ADDR(rt->rt_dst) = strtoul(cols[route_dest_col], NULL, 16);
+ SIN_ADDR(rt->rt_gateway) = strtoul(cols[route_gw_col], NULL, 16);
+ SIN_ADDR(rt->rt_genmask) = strtoul(cols[route_mask_col], NULL, 16);
+
+ rt->rt_flags = (short) strtoul(cols[route_flags_col], NULL, 16);
+ rt->rt_dev = cols[route_dev_col];
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * defaultroute_exists - determine if there is a default route
+ */
+
+static int defaultroute_exists (struct rtentry *rt)
+{
+ int result = 0;
+
+ if (!open_route_table())
+ return 0;
+
+ while (read_route_table(rt) != 0) {
+ if ((rt->rt_flags & RTF_UP) == 0)
+ continue;
+
+ if (kernel_version > KVERSION(2,1,0) && SIN_ADDR(rt->rt_genmask) != 0)
+ continue;
+ if (SIN_ADDR(rt->rt_dst) == 0L) {
+ result = 1;
+ break;
+ }
+ }
+
+ close_route_table();
+ return result;
+}
+
+/*
+ * have_route_to - determine if the system has any route to
+ * a given IP address. `addr' is in network byte order.
+ * Return value is 1 if yes, 0 if no, -1 if don't know.
+ * For demand mode to work properly, we have to ignore routes
+ * through our own interface.
+ */
+int have_route_to(u_int32_t addr)
+{
+ struct rtentry rt;
+ int result = 0;
+
+ if (!open_route_table())
+ return -1; /* don't know */
+
+ while (read_route_table(&rt)) {
+ if ((rt.rt_flags & RTF_UP) == 0 || strcmp(rt.rt_dev, ifname) == 0)
+ continue;
+ if ((addr & SIN_ADDR(rt.rt_genmask)) == SIN_ADDR(rt.rt_dst)) {
+ result = 1;
+ break;
+ }
+ }
+
+ close_route_table();
+ return result;
+}
+
+/********************************************************************
+ *
+ * sifdefaultroute - assign a default route through the address given.
+ */
+
+int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
+{
+ struct rtentry rt;
+
+ if (defaultroute_exists(&rt) && strcmp(rt.rt_dev, ifname) != 0) {
+ u_int32_t old_gateway = SIN_ADDR(rt.rt_gateway);
+
+ if (old_gateway != gateway)
+ error("not replacing existing default route to %s [%I]",
+ rt.rt_dev, old_gateway);
+ return 0;
+ }
+
+ memset (&rt, '\0', sizeof (rt));
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = 0L;
+ }
+
+ SIN_ADDR(rt.rt_gateway) = gateway;
+
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+ if ( ! ok_error ( errno ))
+ error("default route ioctl(SIOCADDRT): %m(%d)", errno);
+ return 0;
+ }
+
+ default_route_gateway = gateway;
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifdefaultroute - delete a default route through the address given.
+ */
+
+int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
+{
+ struct rtentry rt;
+
+ default_route_gateway = 0;
+
+ memset (&rt, '\0', sizeof (rt));
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = 0L;
+ }
+
+ SIN_ADDR(rt.rt_gateway) = gateway;
+
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+ if (still_ppp()) {
+ if ( ! ok_error ( errno ))
+ error("default route ioctl(SIOCDELRT): %m (%d)", errno);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+
+int sifproxyarp (int unit, u_int32_t his_adr)
+{
+ struct arpreq arpreq;
+ char *forw_path;
+
+ if (has_proxy_arp == 0) {
+ memset (&arpreq, '\0', sizeof(arpreq));
+
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ SIN_ADDR(arpreq.arp_pa) = his_adr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+/*
+ * Get the hardware address of an interface on the same subnet
+ * as our local address.
+ */
+ if (!get_ether_addr(his_adr, &arpreq.arp_ha, proxy_arp_dev,
+ sizeof(proxy_arp_dev))) {
+ error("Cannot determine ethernet address for proxy ARP");
+ return 0;
+ }
+ strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev));
+
+ if (ioctl(sock_fd, SIOCSARP, (caddr_t)&arpreq) < 0) {
+ if ( ! ok_error ( errno ))
+ error("ioctl(SIOCSARP): %m(%d)", errno);
+ return 0;
+ }
+ proxy_arp_addr = his_adr;
+ has_proxy_arp = 1;
+
+ if (tune_kernel) {
+ forw_path = path_to_procfs("/sys/net/ipv4/ip_forward");
+ if (forw_path != 0) {
+ int fd = open(forw_path, O_WRONLY);
+ if (fd >= 0) {
+ if (write(fd, "1", 1) != 1)
+ error("Couldn't enable IP forwarding: %m");
+ close(fd);
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+
+int cifproxyarp (int unit, u_int32_t his_adr)
+{
+ struct arpreq arpreq;
+
+ if (has_proxy_arp) {
+ has_proxy_arp = 0;
+ memset (&arpreq, '\0', sizeof(arpreq));
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ SIN_ADDR(arpreq.arp_pa) = his_adr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev));
+
+ if (ioctl(sock_fd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+ if ( ! ok_error ( errno ))
+ warn("ioctl(SIOCDARP): %m(%d)", errno);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/********************************************************************
+ *
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+
+static int get_ether_addr (u_int32_t ipaddr,
+ struct sockaddr *hwaddr,
+ char *name, int namelen)
+{
+ struct ifreq *ifr, *ifend;
+ u_int32_t ina, mask;
+ char *aliasp;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
+ if ( ! ok_error ( errno ))
+ error("ioctl(SIOCGIFCONF): %m(%d)", errno);
+ return 0;
+ }
+
+ SYSDEBUG ((LOG_DEBUG, "proxy arp: scanning %d interfaces for IP %s",
+ ifc.ifc_len / sizeof(struct ifreq), ip_ntoa(ipaddr)));
+/*
+ * Scan through looking for an interface with an Internet
+ * address on the same subnet as `ipaddr'.
+ */
+ ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ ina = SIN_ADDR(ifr->ifr_addr);
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ SYSDEBUG ((LOG_DEBUG, "proxy arp: examining interface %s",
+ ifreq.ifr_name));
+/*
+ * Check that the interface is up, and not point-to-point
+ * nor loopback.
+ */
+ if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+
+ if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
+ continue;
+/*
+ * Get its netmask and check that it's on the right subnet.
+ */
+ if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+
+ mask = SIN_ADDR(ifreq.ifr_addr);
+ SYSDEBUG ((LOG_DEBUG, "proxy arp: interface addr %s mask %lx",
+ ip_ntoa(ina), ntohl(mask)));
+
+ if (((ipaddr ^ ina) & mask) != 0)
+ continue;
+ break;
+ }
+ }
+
+ if (ifr >= ifend)
+ return 0;
+
+ strlcpy(name, ifreq.ifr_name, namelen);
+
+ /* trim off the :1 in eth0:1 */
+ aliasp = strchr(name, ':');
+ if (aliasp != 0)
+ *aliasp = 0;
+
+ info("found interface %s for proxy arp", name);
+/*
+ * Now get the hardware address.
+ */
+ memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+ if (ioctl (sock_fd, SIOCGIFHWADDR, &ifreq) < 0) {
+ error("SIOCGIFHWADDR(%s): %m(%d)", ifreq.ifr_name, errno);
+ return 0;
+ }
+
+ memcpy (hwaddr,
+ &ifreq.ifr_hwaddr,
+ sizeof (struct sockaddr));
+
+ SYSDEBUG ((LOG_DEBUG,
+ "proxy arp: found hwaddr %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ (int) ((unsigned char *) &hwaddr->sa_data)[0],
+ (int) ((unsigned char *) &hwaddr->sa_data)[1],
+ (int) ((unsigned char *) &hwaddr->sa_data)[2],
+ (int) ((unsigned char *) &hwaddr->sa_data)[3],
+ (int) ((unsigned char *) &hwaddr->sa_data)[4],
+ (int) ((unsigned char *) &hwaddr->sa_data)[5],
+ (int) ((unsigned char *) &hwaddr->sa_data)[6],
+ (int) ((unsigned char *) &hwaddr->sa_data)[7]));
+ return 1;
+}
+
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *name)
+{
+ struct ifreq ifreq;
+ int ret, sock_fd;
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0)
+ return 0;
+ memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr));
+ strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
+ ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq);
+ close(sock_fd);
+ if (ret >= 0)
+ memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6);
+ return ret;
+}
+
+/*
+ * get_first_ethernet - return the name of the first ethernet-style
+ * interface on this system.
+ */
+char *
+get_first_ethernet()
+{
+ return "eth0";
+}
+
+/********************************************************************
+ *
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'. If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+
+u_int32_t GetMask (u_int32_t addr)
+{
+ u_int32_t mask, nmask, ina;
+ struct ifreq *ifr, *ifend, ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ addr = ntohl(addr);
+
+ if (IN_CLASSA(addr)) /* determine network mask for address class */
+ nmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(addr))
+ nmask = IN_CLASSB_NET;
+ else
+ nmask = IN_CLASSC_NET;
+
+ /* class D nets are disallowed by bad_ip_adrs */
+ mask = netmask | htonl(nmask);
+/*
+ * Scan through the system's network interfaces.
+ */
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
+ if ( ! ok_error ( errno ))
+ warn("ioctl(SIOCGIFCONF): %m(%d)", errno);
+ return mask;
+ }
+
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
+/*
+ * Check the interface's internet address.
+ */
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ ina = SIN_ADDR(ifr->ifr_addr);
+ if (((ntohl(ina) ^ addr) & nmask) != 0)
+ continue;
+/*
+ * Check that the interface is up, and not point-to-point nor loopback.
+ */
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+
+ if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
+ continue;
+/*
+ * Get its netmask and OR it into our mask.
+ */
+ if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask |= SIN_ADDR(ifreq.ifr_addr);
+ break;
+ }
+ return mask;
+}
+
+/********************************************************************
+ *
+ * Internal routine to decode the version.modification.patch level
+ */
+
+static void decode_version (char *buf, int *version,
+ int *modification, int *patch)
+{
+ char *endp;
+
+ *version = (int) strtoul (buf, &endp, 10);
+ *modification = 0;
+ *patch = 0;
+
+ if (endp != buf && *endp == '.') {
+ buf = endp + 1;
+ *modification = (int) strtoul (buf, &endp, 10);
+ if (endp != buf && *endp == '.') {
+ buf = endp + 1;
+ *patch = (int) strtoul (buf, &buf, 10);
+ }
+ }
+}
+
+/********************************************************************
+ *
+ * Procedure to determine if the PPP line discipline is registered.
+ */
+
+static int
+ppp_registered(void)
+{
+ int local_fd;
+ int mfd = -1;
+ int ret = 0;
+ char slave[16];
+
+ /*
+ * We used to open the serial device and set it to the ppp line
+ * discipline here, in order to create a ppp unit. But that is
+ * not a good idea - the user might have specified a device that
+ * they can't open (permission, or maybe it doesn't really exist).
+ * So we grab a pty master/slave pair and use that.
+ */
+ if (!get_pty(&mfd, &local_fd, slave, 0)) {
+ no_ppp_msg = "Couldn't determine if PPP is supported (no free ptys)";
+ return 0;
+ }
+
+ /*
+ * Try to put the device into the PPP discipline.
+ */
+ if (ioctl(local_fd, TIOCSETD, &ppp_disc) < 0) {
+ error("ioctl(TIOCSETD(PPP)): %m(%d)", errno);
+ } else
+ ret = 1;
+
+ close(local_fd);
+ close(mfd);
+ return ret;
+}
+
+/********************************************************************
+ *
+ * ppp_available - check whether the system has any ppp interfaces
+ * (in fact we check whether we can do an ioctl on ppp0).
+ */
+
+int ppp_available(void)
+{
+ int s, ok, fd;
+ struct ifreq ifr;
+ int size;
+ int my_version, my_modification, my_patch;
+ int osmaj, osmin, ospatch;
+
+ no_ppp_msg =
+ "This system lacks kernel support for PPP. This could be because\n"
+ "the PPP kernel module could not be loaded, or because PPP was not\n"
+ "included in the kernel configuration. If PPP was included as a\n"
+ "module, try `/sbin/modprobe -v ppp'. If that fails, check that\n"
+ "ppp.o exists in /lib/modules/`uname -r`/net.\n"
+ "See README.linux file in the ppp distribution for more details.\n";
+
+ /* get the kernel version now, since we are called before sys_init */
+ uname(&utsname);
+ osmaj = osmin = ospatch = 0;
+ sscanf(utsname.release, "%d.%d.%d", &osmaj, &osmin, &ospatch);
+ kernel_version = KVERSION(osmaj, osmin, ospatch);
+
+ fd = open("/dev/ppp", O_RDWR);
+#if 0
+ if (fd < 0 && errno == ENOENT) {
+ /* try making it and see if that helps. */
+ if (mknod("/dev/ppp", S_IFCHR | S_IRUSR | S_IWUSR,
+ makedev(108, 0)) >= 0) {
+ fd = open("/dev/ppp", O_RDWR);
+ if (fd >= 0)
+ info("Created /dev/ppp device node");
+ else
+ unlink("/dev/ppp"); /* didn't work, undo the mknod */
+ } else if (errno == EEXIST) {
+ fd = open("/dev/ppp", O_RDWR);
+ }
+ }
+#endif /* 0 */
+ if (fd >= 0) {
+ new_style_driver = 1;
+
+ /* XXX should get from driver */
+ driver_version = 2;
+ driver_modification = 4;
+ driver_patch = 0;
+ close(fd);
+ return 1;
+ }
+ if (kernel_version >= KVERSION(2,3,13)) {
+ if (errno == ENOENT)
+ no_ppp_msg =
+ "pppd is unable to open the /dev/ppp device.\n"
+ "You need to create the /dev/ppp device node by\n"
+ "executing the following command as root:\n"
+ " mknod /dev/ppp c 108 0\n";
+ return 0;
+ }
+
+/*
+ * Open a socket for doing the ioctl operations.
+ */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ return 0;
+
+ strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+ ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+/*
+ * If the device did not exist then attempt to create one by putting the
+ * current tty into the PPP discipline. If this works then obtain the
+ * flags for the device again.
+ */
+ if (!ok) {
+ if (ppp_registered()) {
+ strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+ ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+ }
+ }
+/*
+ * Ensure that the hardware address is for PPP and not something else
+ */
+ if (ok)
+ ok = ioctl (s, SIOCGIFHWADDR, (caddr_t) &ifr) >= 0;
+
+ if (ok && ((ifr.ifr_hwaddr.sa_family & ~0xFF) != ARPHRD_PPP))
+ ok = 0;
+
+/*
+ * This is the PPP device. Validate the version of the driver at this
+ * point to ensure that this program will work with the driver.
+ */
+ if (ok) {
+ char abBuffer [1024];
+
+ ifr.ifr_data = abBuffer;
+ size = ioctl (s, SIOCGPPPVER, (caddr_t) &ifr);
+ if (size < 0) {
+ error("Couldn't read driver version: %m");
+ ok = 0;
+ no_ppp_msg = "Sorry, couldn't verify kernel driver version\n";
+
+ } else {
+ decode_version(abBuffer,
+ &driver_version,
+ &driver_modification,
+ &driver_patch);
+/*
+ * Validate the version of the driver against the version that we used.
+ */
+ decode_version(VERSION,
+ &my_version,
+ &my_modification,
+ &my_patch);
+
+ /* The version numbers must match */
+ if (driver_version != my_version)
+ ok = 0;
+
+ /* The modification levels must be legal */
+ if (driver_modification < 3) {
+ if (driver_modification >= 2) {
+ /* we can cope with 2.2.0 and above */
+ driver_is_old = 1;
+ } else {
+ ok = 0;
+ }
+ }
+
+ close (s);
+ if (!ok) {
+ slprintf(route_buffer, sizeof(route_buffer),
+ "Sorry - PPP driver version %d.%d.%d is out of date\n",
+ driver_version, driver_modification, driver_patch);
+
+ no_ppp_msg = route_buffer;
+ }
+ }
+ }
+ return ok;
+}
+
+/********************************************************************
+ *
+ * sifvjcomp - config tcp header compression
+ */
+
+int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid)
+{
+ u_int x = get_flags(ppp_dev_fd);
+
+ if (vjcomp) {
+ if (ioctl (ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(PPPIOCSMAXCID): %m(%d)", errno);
+ vjcomp = 0;
+ }
+ }
+
+ x = vjcomp ? x | SC_COMP_TCP : x &~ SC_COMP_TCP;
+ x = cidcomp ? x & ~SC_NO_TCP_CCID : x | SC_NO_TCP_CCID;
+ set_flags (ppp_dev_fd, x);
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+
+int sifup(int u)
+{
+ struct ifreq ifr;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl (SIOCGIFFLAGS): %m(%d)", errno);
+ return 0;
+ }
+
+ ifr.ifr_flags |= (IFF_UP | IFF_POINTOPOINT);
+ if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFFLAGS): %m(%d)", errno);
+ return 0;
+ }
+ if_is_up++;
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifdown - Disable the indicated protocol and config the interface
+ * down if there are no remaining protocols.
+ */
+
+int sifdown (int u)
+{
+ struct ifreq ifr;
+
+ if (if_is_up && --if_is_up > 0)
+ return 1;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl (SIOCGIFFLAGS): %m(%d)", errno);
+ return 0;
+ }
+
+ ifr.ifr_flags &= ~IFF_UP;
+ ifr.ifr_flags |= IFF_POINTOPOINT;
+ if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFFLAGS): %m(%d)", errno);
+ return 0;
+ }
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+
+int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
+ u_int32_t net_mask)
+{
+ struct ifreq ifr;
+ struct rtentry rt;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ memset (&rt, '\0', sizeof (rt));
+
+ SET_SA_FAMILY (ifr.ifr_addr, AF_INET);
+ SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET);
+ SET_SA_FAMILY (ifr.ifr_netmask, AF_INET);
+
+ strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+/*
+ * Set our IP address
+ */
+ SIN_ADDR(ifr.ifr_addr) = our_adr;
+ if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ if (errno != EEXIST) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFADDR): %m(%d)", errno);
+ }
+ else {
+ warn("ioctl(SIOCSIFADDR): Address already exists");
+ }
+ return (0);
+ }
+/*
+ * Set the gateway address
+ */
+ SIN_ADDR(ifr.ifr_dstaddr) = his_adr;
+ if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFDSTADDR): %m(%d)", errno);
+ return (0);
+ }
+/*
+ * Set the netmask.
+ * For recent kernels, force the netmask to 255.255.255.255.
+ */
+ if (kernel_version >= KVERSION(2,1,16))
+ net_mask = ~0L;
+ if (net_mask != 0) {
+ SIN_ADDR(ifr.ifr_netmask) = net_mask;
+ if (ioctl(sock_fd, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFNETMASK): %m(%d)", errno);
+ return (0);
+ }
+ }
+/*
+ * Add the device route
+ */
+ if (kernel_version < KVERSION(2,1,16)) {
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+ rt.rt_dev = ifname;
+
+ SIN_ADDR(rt.rt_gateway) = 0L;
+ SIN_ADDR(rt.rt_dst) = his_adr;
+ rt.rt_flags = RTF_UP | RTF_HOST;
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = -1L;
+ }
+
+ if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCADDRT) device route: %m(%d)", errno);
+ return (0);
+ }
+ }
+
+ /* set ip_dynaddr in demand mode if address changes */
+ if (demand && tune_kernel && !dynaddr_set
+ && our_old_addr && our_old_addr != our_adr) {
+ /* set ip_dynaddr if possible */
+ char *path;
+ int fd;
+
+ path = path_to_procfs("/sys/net/ipv4/ip_dynaddr");
+ if (path != 0 && (fd = open(path, O_WRONLY)) >= 0) {
+ if (write(fd, "1", 1) != 1)
+ error("Couldn't enable dynamic IP addressing: %m");
+ close(fd);
+ }
+ dynaddr_set = 1; /* only 1 attempt */
+ }
+ our_old_addr = 0;
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+
+int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr)
+{
+ struct ifreq ifr;
+
+ if (kernel_version < KVERSION(2,1,16)) {
+/*
+ * Delete the route through the device
+ */
+ struct rtentry rt;
+ memset (&rt, '\0', sizeof (rt));
+
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+ rt.rt_dev = ifname;
+
+ SIN_ADDR(rt.rt_gateway) = 0;
+ SIN_ADDR(rt.rt_dst) = his_adr;
+ rt.rt_flags = RTF_UP | RTF_HOST;
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = -1L;
+ }
+
+ if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+ if (still_ppp() && ! ok_error (errno))
+ error("ioctl(SIOCDELRT) device route: %m(%d)", errno);
+ return (0);
+ }
+ }
+
+ /* This way it is possible to have an IPX-only or IPv6-only interface */
+ memset(&ifr, 0, sizeof(ifr));
+ SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno)) {
+ error("ioctl(SIOCSIFADDR): %m(%d)", errno);
+ return 0;
+ }
+ }
+
+ our_old_addr = our_adr;
+
+ return 1;
+}
+
+#ifdef INET6
+/********************************************************************
+ *
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+ struct in6_ifreq ifr6;
+ struct ifreq ifr;
+ struct in6_rtmsg rt6;
+
+ if (sock6_fd < 0) {
+ errno = -sock6_fd;
+ error("IPv6 socket creation failed: %m");
+ return 0;
+ }
+ memset(&ifr, 0, sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
+ error("sif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
+ return 0;
+ }
+
+ /* Local interface */
+ memset(&ifr6, 0, sizeof(ifr6));
+ IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = 10;
+
+ if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6) < 0) {
+ error("sif6addr: ioctl(SIOCSIFADDR): %m (%d)", errno);
+ return 0;
+ }
+
+ /* Route to remote host */
+ memset(&rt6, 0, sizeof(rt6));
+ IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64);
+ rt6.rtmsg_flags = RTF_UP;
+ rt6.rtmsg_dst_len = 10;
+ rt6.rtmsg_ifindex = ifr.ifr_ifindex;
+ rt6.rtmsg_metric = 1;
+
+ if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) {
+ error("sif6addr: ioctl(SIOCADDRT): %m (%d)", errno);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/********************************************************************
+ *
+ * cif6addr - Remove IPv6 address from interface
+ */
+int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+ struct ifreq ifr;
+ struct in6_ifreq ifr6;
+
+ if (sock6_fd < 0) {
+ errno = -sock6_fd;
+ error("IPv6 socket creation failed: %m");
+ return 0;
+ }
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
+ error("cif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
+ return 0;
+ }
+
+ memset(&ifr6, 0, sizeof(ifr6));
+ IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = 10;
+
+ if (ioctl(sock6_fd, SIOCDIFADDR, &ifr6) < 0) {
+ if (errno != EADDRNOTAVAIL) {
+ if (! ok_error (errno))
+ error("cif6addr: ioctl(SIOCDIFADDR): %m (%d)", errno);
+ }
+ else {
+ warn("cif6addr: ioctl(SIOCDIFADDR): No such address");
+ }
+ return (0);
+ }
+ return 1;
+}
+#endif /* INET6 */
+
+/*
+ * get_pty - get a pty master/slave pair and chown the slave side
+ * to the uid given. Assumes slave_name points to >= 16 bytes of space.
+ */
+int
+get_pty(master_fdp, slave_fdp, slave_name, uid)
+ int *master_fdp;
+ int *slave_fdp;
+ char *slave_name;
+ int uid;
+{
+ int i, mfd, sfd = -1;
+ char pty_name[16];
+ struct termios tios;
+
+#ifdef TIOCGPTN
+ /*
+ * Try the unix98 way first.
+ */
+ mfd = open("/dev/ptmx", O_RDWR);
+ if (mfd >= 0) {
+ int ptn;
+ if (ioctl(mfd, TIOCGPTN, &ptn) >= 0) {
+ slprintf(pty_name, sizeof(pty_name), "/dev/pts/%d", ptn);
+ chmod(pty_name, S_IRUSR | S_IWUSR);
+#ifdef TIOCSPTLCK
+ ptn = 0;
+ if (ioctl(mfd, TIOCSPTLCK, &ptn) < 0)
+ warn("Couldn't unlock pty slave %s: %m", pty_name);
+#endif
+ if ((sfd = open(pty_name, O_RDWR | O_NOCTTY)) < 0)
+ warn("Couldn't open pty slave %s: %m", pty_name);
+ }
+ }
+#endif /* TIOCGPTN */
+
+ if (sfd < 0) {
+ /* the old way - scan through the pty name space */
+ for (i = 0; i < 64; ++i) {
+ slprintf(pty_name, sizeof(pty_name), "/dev/pty%c%x",
+ 'p' + i / 16, i % 16);
+ mfd = open(pty_name, O_RDWR, 0);
+ if (mfd >= 0) {
+ pty_name[5] = 't';
+ sfd = open(pty_name, O_RDWR | O_NOCTTY, 0);
+ if (sfd >= 0) {
+ fchown(sfd, uid, -1);
+ fchmod(sfd, S_IRUSR | S_IWUSR);
+ break;
+ }
+ close(mfd);
+ }
+ }
+ }
+
+ if (sfd < 0)
+ return 0;
+
+ strlcpy(slave_name, pty_name, 16);
+ *master_fdp = mfd;
+ *slave_fdp = sfd;
+ if (tcgetattr(sfd, &tios) == 0) {
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+ tios.c_cflag |= CS8 | CREAD | CLOCAL;
+ tios.c_iflag = IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0)
+ warn("couldn't set attributes on pty: %m");
+ } else
+ warn("couldn't get attributes on pty: %m");
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * open_loopback - open the device we use for getting packets
+ * in demand mode. Under Linux, we use a pty master/slave pair.
+ */
+int
+open_ppp_loopback(void)
+{
+ int flags;
+
+ looped = 1;
+ if (new_style_driver) {
+ /* allocate ourselves a ppp unit */
+ if (make_ppp_unit() < 0)
+ die(1);
+ set_flags(ppp_dev_fd, SC_LOOP_TRAFFIC);
+ set_kdebugflag(kdebugflag);
+ ppp_fd = -1;
+ return ppp_dev_fd;
+ }
+
+ if (!get_pty(&master_fd, &slave_fd, loop_name, 0))
+ fatal("No free pty for loopback");
+ SYSDEBUG(("using %s for loopback", loop_name));
+
+ set_ppp_fd(slave_fd);
+
+ flags = fcntl(master_fd, F_GETFL);
+ if (flags == -1 ||
+ fcntl(master_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set master loopback to nonblock: %m(%d)", errno);
+
+ flags = fcntl(ppp_fd, F_GETFL);
+ if (flags == -1 ||
+ fcntl(ppp_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set slave loopback to nonblock: %m(%d)", errno);
+
+ if (ioctl(ppp_fd, TIOCSETD, &ppp_disc) < 0)
+ fatal("ioctl(TIOCSETD): %m(%d)", errno);
+/*
+ * Find out which interface we were given.
+ */
+ if (ioctl(ppp_fd, PPPIOCGUNIT, &ifunit) < 0)
+ fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+/*
+ * Enable debug in the driver if requested.
+ */
+ set_kdebugflag (kdebugflag);
+
+ return master_fd;
+}
+
+/********************************************************************
+ *
+ * restore_loop - reattach the ppp unit to the loopback.
+ *
+ * The kernel ppp driver automatically reattaches the ppp unit to
+ * the loopback if the serial port is set to a line discipline other
+ * than ppp, or if it detects a modem hangup. The former will happen
+ * in disestablish_ppp if the latter hasn't already happened, so we
+ * shouldn't need to do anything.
+ *
+ * Just to be sure, set the real serial port to the normal discipline.
+ */
+
+static void
+restore_loop(void)
+{
+ looped = 1;
+ if (new_style_driver) {
+ set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_LOOP_TRAFFIC);
+ return;
+ }
+ if (ppp_fd != slave_fd) {
+ (void) ioctl(ppp_fd, TIOCSETD, &tty_disc);
+ set_ppp_fd(slave_fd);
+ }
+}
+
+/********************************************************************
+ *
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+
+int
+sifnpmode(u, proto, mode)
+ int u;
+ int proto;
+ enum NPmode mode;
+{
+ struct npioctl npi;
+
+ npi.protocol = proto;
+ npi.mode = mode;
+ if (ioctl(ppp_dev_fd, PPPIOCSNPMODE, (caddr_t) &npi) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(PPPIOCSNPMODE, %d, %d): %m (%d)",
+ proto, mode, errno);
+ return 0;
+ }
+ return 1;
+}
+
+
+/********************************************************************
+ *
+ * sipxfaddr - Config the interface IPX networknumber
+ */
+
+int sipxfaddr (int unit, unsigned long int network, unsigned char * node )
+{
+ int result = 1;
+
+#ifdef IPX_CHANGE
+ int skfd;
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+
+ skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+ if (skfd < 0) {
+ if (! ok_error (errno))
+ dbglog("socket(AF_IPX): %m (%d)", errno);
+ result = 0;
+ }
+ else {
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ memcpy (sipx->sipx_node, node, IPX_NODE_LEN);
+ sipx->sipx_family = AF_IPX;
+ sipx->sipx_port = 0;
+ sipx->sipx_network = htonl (network);
+ sipx->sipx_type = IPX_FRAME_ETHERII;
+ sipx->sipx_action = IPX_CRTITF;
+/*
+ * Set the IPX device
+ */
+ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ result = 0;
+ if (errno != EEXIST) {
+ if (! ok_error (errno))
+ dbglog("ioctl(SIOCSIFADDR, CRTITF): %m (%d)", errno);
+ }
+ else {
+ warn("ioctl(SIOCSIFADDR, CRTITF): Address already exists");
+ }
+ }
+ close (skfd);
+ }
+#endif
+ return result;
+}
+
+/********************************************************************
+ *
+ * cipxfaddr - Clear the information for the IPX network. The IPX routes
+ * are removed and the device is no longer able to pass IPX
+ * frames.
+ */
+
+int cipxfaddr (int unit)
+{
+ int result = 1;
+
+#ifdef IPX_CHANGE
+ int skfd;
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+
+ skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+ if (skfd < 0) {
+ if (! ok_error (errno))
+ dbglog("socket(AF_IPX): %m (%d)", errno);
+ result = 0;
+ }
+ else {
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ sipx->sipx_type = IPX_FRAME_ETHERII;
+ sipx->sipx_action = IPX_DLTITF;
+ sipx->sipx_family = AF_IPX;
+/*
+ * Set the IPX device
+ */
+ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ info("ioctl(SIOCSIFADDR, IPX_DLTITF): %m (%d)", errno);
+ result = 0;
+ }
+ close (skfd);
+ }
+#endif
+ return result;
+}
+
+/*
+ * Use the hostname as part of the random number seed.
+ */
+int
+get_host_seed()
+{
+ int h;
+ char *p = hostname;
+
+ h = 407;
+ for (p = hostname; *p != 0; ++p)
+ h = h * 37 + *p;
+ return h;
+}
+
+/********************************************************************
+ *
+ * sys_check_options - check the options that the user specified
+ */
+
+int
+sys_check_options(void)
+{
+#ifdef IPX_CHANGE
+/*
+ * Disable the IPX protocol if the support is not present in the kernel.
+ */
+ char *path;
+
+ if (ipxcp_protent.enabled_flag) {
+ struct stat stat_buf;
+ if ((path = path_to_procfs("/net/ipx_interface")) == 0
+ || lstat(path, &stat_buf) < 0) {
+ error("IPX support is not present in the kernel\n");
+ ipxcp_protent.enabled_flag = 0;
+ }
+ }
+#endif
+ if (demand && driver_is_old) {
+ option_error("demand dialling is not supported by kernel driver "
+ "version %d.%d.%d", driver_version, driver_modification,
+ driver_patch);
+ return 0;
+ }
+ if (multilink && !new_style_driver) {
+ warn("Warning: multilink is not supported by the kernel driver");
+ multilink = 0;
+ }
+ return 1;
+}
diff --git a/mdk-stage1/ppp/pppd/sys-linux.c.wtmp b/mdk-stage1/ppp/pppd/sys-linux.c.wtmp
new file mode 100644
index 000000000..f1b48423e
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/sys-linux.c.wtmp
@@ -0,0 +1,2750 @@
+/*
+ * sys-linux.c - System-dependent procedures for setting up
+ * PPP interfaces on Linux systems
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/sysmacros.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <time.h>
+#include <memory.h>
+#include <utmp.h>
+#include <mntent.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+
+/* This is in netdevice.h. However, this compile will fail miserably if
+ you attempt to include netdevice.h because it has so many references
+ to __memcpy functions which it should not attempt to do. So, since I
+ really don't use it, but it must be defined, define it now. */
+
+#ifndef MAX_ADDR_LEN
+#define MAX_ADDR_LEN 7
+#endif
+
+#if __GLIBC__ >= 2
+#include <asm/types.h> /* glibc 2 conflicts with linux/types.h */
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/route.h>
+#include <netinet/if_ether.h>
+#else
+#include <linux/types.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/route.h>
+#include <linux/if_ether.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#if __GLIBC__ >= 2 && \
+ !(defined(__powerpc__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
+#include <netipx/ipx.h>
+#else
+#include <linux/ipx.h>
+#endif
+#endif /* IPX_CHANGE */
+
+#ifdef PPP_FILTER
+#include <net/bpf.h>
+#include <linux/filter.h>
+#endif /* PPP_FILTER */
+
+#ifdef LOCKLIB
+#include <sys/locks.h>
+#endif
+
+#ifdef INET6
+#ifndef _LINUX_IN6_H
+/*
+ * This is in linux/include/net/ipv6.h.
+ */
+
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ __u32 ifr6_prefixlen;
+ unsigned int ifr6_ifindex;
+};
+#endif
+
+#define IN6_LLADDR_FROM_EUI64(sin6, eui64) do { \
+ memset(&sin6.s6_addr, 0, sizeof(struct in6_addr)); \
+ sin6.s6_addr16[0] = htons(0xfe80); \
+ eui64_copy(eui64, sin6.s6_addr32[2]); \
+ } while (0)
+
+#endif /* INET6 */
+
+/* We can get an EIO error on an ioctl if the modem has hung up */
+#define ok_error(num) ((num)==EIO)
+
+static int tty_disc = N_TTY; /* The TTY discipline */
+static int ppp_disc = N_PPP; /* The PPP discpline */
+static int initfdflags = -1; /* Initial file descriptor flags for fd */
+static int ppp_fd = -1; /* fd which is set to PPP discipline */
+static int sock_fd = -1; /* socket for doing interface ioctls */
+static int slave_fd = -1;
+static int master_fd = -1;
+#ifdef INET6
+static int sock6_fd = -1;
+#endif /* INET6 */
+static int ppp_dev_fd = -1; /* fd for /dev/ppp (new style driver) */
+static int chindex; /* channel index (new style driver) */
+
+static fd_set in_fds; /* set of fds that wait_input waits for */
+static int max_in_fd; /* highest fd set in in_fds */
+
+static int has_proxy_arp = 0;
+static int driver_version = 0;
+static int driver_modification = 0;
+static int driver_patch = 0;
+static int driver_is_old = 0;
+static int restore_term = 0; /* 1 => we've munged the terminal */
+static struct termios inittermios; /* Initial TTY termios */
+
+static int new_style_driver = 0;
+
+static char loop_name[20];
+static unsigned char inbuf[512]; /* buffer for chars read from loopback */
+
+static int if_is_up; /* Interface has been marked up */
+static u_int32_t default_route_gateway; /* Gateway for default route added */
+static u_int32_t proxy_arp_addr; /* Addr for proxy arp entry added */
+static char proxy_arp_dev[16]; /* Device for proxy arp entry */
+static u_int32_t our_old_addr; /* for detecting address changes */
+static int dynaddr_set; /* 1 if ip_dynaddr set */
+static int looped; /* 1 if using loop */
+static int link_mtu; /* mtu for the link (not bundle) */
+
+static struct utsname utsname; /* for the kernel version */
+static int kernel_version;
+#define KVERSION(j,n,p) ((j)*1000000 + (n)*1000 + (p))
+
+#define MAX_IFS 100
+
+#define FLAGS_GOOD (IFF_UP | IFF_BROADCAST)
+#define FLAGS_MASK (IFF_UP | IFF_BROADCAST | \
+ IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP)
+
+#define SIN_ADDR(x) (((struct sockaddr_in *) (&(x)))->sin_addr.s_addr)
+
+/* Prototypes for procedures local to this file. */
+static int get_flags (int fd);
+static void set_flags (int fd, int flags);
+static int translate_speed (int bps);
+static int baud_rate_of (int speed);
+static void close_route_table (void);
+static int open_route_table (void);
+static int read_route_table (struct rtentry *rt);
+static int defaultroute_exists (struct rtentry *rt);
+static int get_ether_addr (u_int32_t ipaddr, struct sockaddr *hwaddr,
+ char *name, int namelen);
+static void decode_version (char *buf, int *version, int *mod, int *patch);
+static int set_kdebugflag(int level);
+static int ppp_registered(void);
+static int make_ppp_unit(void);
+static void restore_loop(void); /* Transfer ppp unit back to loopback */
+
+extern u_char inpacket_buf[]; /* borrowed from main.c */
+
+/*
+ * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
+ * if it exists.
+ */
+
+#define SET_SA_FAMILY(addr, family) \
+ memset ((char *) &(addr), '\0', sizeof(addr)); \
+ addr.sa_family = (family);
+
+/*
+ * Determine if the PPP connection should still be present.
+ */
+
+extern int hungup;
+
+/* new_fd is the fd of a tty */
+static void set_ppp_fd (int new_fd)
+{
+ SYSDEBUG ((LOG_DEBUG, "setting ppp_fd to %d\n", new_fd));
+ ppp_fd = new_fd;
+ if (!new_style_driver)
+ ppp_dev_fd = new_fd;
+}
+
+static int still_ppp(void)
+{
+ if (new_style_driver)
+ return !hungup && ppp_fd >= 0;
+ if (!hungup || ppp_fd == slave_fd)
+ return 1;
+ if (slave_fd >= 0) {
+ set_ppp_fd(slave_fd);
+ return 1;
+ }
+ return 0;
+}
+
+/********************************************************************
+ *
+ * Functions to read and set the flags value in the device driver
+ */
+
+static int get_flags (int fd)
+{
+ int flags;
+
+ if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) {
+ if ( ok_error (errno) )
+ flags = 0;
+ else
+ fatal("ioctl(PPPIOCGFLAGS): %m");
+ }
+
+ SYSDEBUG ((LOG_DEBUG, "get flags = %x\n", flags));
+ return flags;
+}
+
+/********************************************************************/
+
+static void set_flags (int fd, int flags)
+{
+ SYSDEBUG ((LOG_DEBUG, "set flags = %x\n", flags));
+
+ if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &flags) < 0) {
+ if (! ok_error (errno) )
+ fatal("ioctl(PPPIOCSFLAGS, %x): %m", flags, errno);
+ }
+}
+
+/********************************************************************
+ *
+ * sys_init - System-dependent initialization.
+ */
+
+void sys_init(void)
+{
+ int flags;
+
+ if (new_style_driver) {
+ ppp_dev_fd = open("/dev/ppp", O_RDWR);
+ if (ppp_dev_fd < 0)
+ fatal("Couldn't open /dev/ppp: %m");
+ flags = fcntl(ppp_dev_fd, F_GETFL);
+ if (flags == -1
+ || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("Couldn't set /dev/ppp to nonblock: %m");
+ }
+
+ /* Get an internet socket for doing socket ioctls. */
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0)
+ fatal("Couldn't create IP socket: %m(%d)", errno);
+
+#ifdef INET6
+ sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock6_fd < 0)
+ sock6_fd = -errno; /* save errno for later */
+#endif
+
+ FD_ZERO(&in_fds);
+ max_in_fd = 0;
+}
+
+/********************************************************************
+ *
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This shouldn't call die() because it's called from die().
+ */
+
+void sys_cleanup(void)
+{
+/*
+ * Take down the device
+ */
+ if (if_is_up) {
+ if_is_up = 0;
+ sifdown(0);
+ }
+/*
+ * Delete any routes through the device.
+ */
+ if (default_route_gateway != 0)
+ cifdefaultroute(0, 0, default_route_gateway);
+
+ if (has_proxy_arp)
+ cifproxyarp(0, proxy_arp_addr);
+}
+
+/********************************************************************
+ *
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close(void)
+{
+ if (new_style_driver)
+ close(ppp_dev_fd);
+ if (sock_fd >= 0)
+ close(sock_fd);
+ if (slave_fd >= 0)
+ close(slave_fd);
+ if (master_fd >= 0)
+ close(master_fd);
+ closelog();
+}
+
+/********************************************************************
+ *
+ * set_kdebugflag - Define the debugging level for the kernel
+ */
+
+static int set_kdebugflag (int requested_level)
+{
+ if (new_style_driver && ifunit < 0)
+ return 1;
+ if (ioctl(ppp_dev_fd, PPPIOCSDEBUG, &requested_level) < 0) {
+ if ( ! ok_error (errno) )
+ error("ioctl(PPPIOCSDEBUG): %m");
+ return (0);
+ }
+ SYSDEBUG ((LOG_INFO, "set kernel debugging level to %d",
+ requested_level));
+ return (1);
+}
+
+/********************************************************************
+ *
+ * tty_establish_ppp - Turn the serial port into a ppp interface.
+ */
+
+int tty_establish_ppp (int tty_fd)
+{
+ int x;
+ int fd = -1;
+
+/*
+ * Ensure that the tty device is in exclusive mode.
+ */
+ if (ioctl(tty_fd, TIOCEXCL, 0) < 0) {
+ if ( ! ok_error ( errno ))
+ warn("Couldn't make tty exclusive: %m");
+ }
+/*
+ * Demand mode - prime the old ppp device to relinquish the unit.
+ */
+ if (!new_style_driver && looped
+ && ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
+ error("ioctl(transfer ppp unit): %m");
+ return -1;
+ }
+/*
+ * Set the current tty to the PPP discpline
+ */
+
+#ifndef N_SYNC_PPP
+#define N_SYNC_PPP 14
+#endif
+ ppp_disc = (new_style_driver && sync_serial)? N_SYNC_PPP: N_PPP;
+ if (ioctl(tty_fd, TIOCSETD, &ppp_disc) < 0) {
+ if ( ! ok_error (errno) ) {
+ error("Couldn't set tty to PPP discipline: %m");
+ return -1;
+ }
+ }
+
+ if (new_style_driver) {
+ /* Open another instance of /dev/ppp and connect the channel to it */
+ int flags;
+
+ if (ioctl(tty_fd, PPPIOCGCHAN, &chindex) == -1) {
+ error("Couldn't get channel number: %m");
+ goto err;
+ }
+ dbglog("using channel %d", chindex);
+ fd = open("/dev/ppp", O_RDWR);
+ if (fd < 0) {
+ error("Couldn't reopen /dev/ppp: %m");
+ goto err;
+ }
+ if (ioctl(fd, PPPIOCATTCHAN, &chindex) < 0) {
+ error("Couldn't attach to channel %d: %m", chindex);
+ goto err_close;
+ }
+ flags = fcntl(fd, F_GETFL);
+ if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("Couldn't set /dev/ppp (channel) to nonblock: %m");
+ set_ppp_fd(fd);
+
+ if (!looped)
+ ifunit = -1;
+ if (!looped && !multilink) {
+ /*
+ * Create a new PPP unit.
+ */
+ if (make_ppp_unit() < 0)
+ goto err_close;
+ }
+
+ if (looped)
+ set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) & ~SC_LOOP_TRAFFIC);
+
+ if (!multilink) {
+ add_fd(ppp_dev_fd);
+ if (ioctl(fd, PPPIOCCONNECT, &ifunit) < 0) {
+ error("Couldn't attach to PPP unit %d: %m", ifunit);
+ goto err_close;
+ }
+ }
+
+ } else {
+ /*
+ * Old-style driver: find out which interface we were given.
+ */
+ set_ppp_fd (tty_fd);
+ if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) {
+ if (ok_error (errno))
+ goto err;
+ fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+ }
+ /* Check that we got the same unit again. */
+ if (looped && x != ifunit)
+ fatal("transfer_ppp failed: wanted unit %d, got %d", ifunit, x);
+ ifunit = x;
+
+ /*
+ * Fetch the initial file flags and reset blocking mode on the file.
+ */
+ initfdflags = fcntl(tty_fd, F_GETFL);
+ if (initfdflags == -1 ||
+ fcntl(tty_fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+ if ( ! ok_error (errno))
+ warn("Couldn't set device to non-blocking mode: %m");
+ }
+ }
+
+ looped = 0;
+
+ /*
+ * Enable debug in the driver if requested.
+ */
+ if (!looped)
+ set_kdebugflag (kdebugflag);
+
+#define SC_RCVB (SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
+#define SC_LOGB (SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
+ | SC_LOG_FLUSH)
+
+ set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB))
+ | ((kdebugflag * SC_DEBUG) & SC_LOGB)));
+
+ SYSDEBUG ((LOG_NOTICE, "Using version %d.%d.%d of PPP driver",
+ driver_version, driver_modification, driver_patch));
+
+ return ppp_fd;
+
+ err_close:
+ close(fd);
+ err:
+ if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
+ warn("Couldn't reset tty to normal line discipline: %m");
+ return -1;
+}
+
+/********************************************************************
+ *
+ * tty_disestablish_ppp - Restore the serial port to normal operation,
+ * and reconnect the ppp unit to the loopback if in demand mode.
+ * This shouldn't call die() because it's called from die().
+ */
+
+void tty_disestablish_ppp(int tty_fd)
+{
+ if (demand)
+ restore_loop();
+ if (!hungup) {
+/*
+ * Flush the tty output buffer so that the TIOCSETD doesn't hang.
+ */
+ if (tcflush(tty_fd, TCIOFLUSH) < 0)
+ warn("tcflush failed: %m");
+/*
+ * Restore the previous line discipline
+ */
+ if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0) {
+ if ( ! ok_error (errno))
+ error("ioctl(TIOCSETD, N_TTY): %m");
+ }
+
+ if (ioctl(tty_fd, TIOCNXCL, 0) < 0) {
+ if ( ! ok_error (errno))
+ warn("ioctl(TIOCNXCL): %m(%d)", errno);
+ }
+
+ /* Reset non-blocking mode on fd. */
+ if (initfdflags != -1 && fcntl(tty_fd, F_SETFL, initfdflags) < 0) {
+ if ( ! ok_error (errno))
+ warn("Couldn't restore device fd flags: %m");
+ }
+ }
+ initfdflags = -1;
+
+ if (new_style_driver) {
+ close(ppp_fd);
+ ppp_fd = -1;
+ if (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
+ error("Couldn't release PPP unit: %m");
+ if (!multilink)
+ remove_fd(ppp_dev_fd);
+ }
+}
+
+/*
+ * make_ppp_unit - make a new ppp unit for ppp_dev_fd.
+ * Assumes new_style_driver.
+ */
+static int make_ppp_unit()
+{
+ int x;
+
+ ifunit = req_unit;
+ x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+ if (x < 0 && req_unit >= 0 && errno == EEXIST) {
+ warn("Couldn't allocate PPP unit %d as it is already in use");
+ ifunit = -1;
+ x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+ }
+ if (x < 0)
+ error("Couldn't create new ppp unit: %m");
+ return x;
+}
+
+/*
+ * cfg_bundle - configure the existing bundle.
+ * Used in demand mode.
+ */
+void cfg_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+ int flags;
+
+ if (!new_style_driver)
+ return;
+
+ /* set the mrru, mtu and flags */
+ if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
+ error("Couldn't set MRRU: %m");
+ flags = get_flags(ppp_dev_fd);
+ flags &= ~(SC_MP_SHORTSEQ | SC_MP_XSHORTSEQ);
+ flags |= (rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0)
+ | (mrru? SC_MULTILINK: 0);
+
+ set_flags(ppp_dev_fd, flags);
+
+ /* connect up the channel */
+ if (ioctl(ppp_fd, PPPIOCCONNECT, &ifunit) < 0)
+ fatal("Couldn't attach to PPP unit %d: %m", ifunit);
+ add_fd(ppp_dev_fd);
+}
+
+/*
+ * make_new_bundle - create a new PPP unit (i.e. a bundle)
+ * and connect our channel to it. This should only get called
+ * if `multilink' was set at the time establish_ppp was called.
+ * In demand mode this uses our existing bundle instead of making
+ * a new one.
+ */
+void make_new_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+ if (!new_style_driver)
+ return;
+
+ /* make us a ppp unit */
+ if (make_ppp_unit() < 0)
+ die(1);
+
+ /* set the mrru and flags */
+ cfg_bundle(mrru, mtru, rssn, tssn);
+}
+
+/*
+ * bundle_attach - attach our link to a given PPP unit.
+ * We assume the unit is controlled by another pppd.
+ */
+int bundle_attach(int ifnum)
+{
+ if (!new_style_driver)
+ return -1;
+
+ if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
+ if (errno == ENXIO)
+ return 0; /* doesn't still exist */
+ fatal("Couldn't attach to interface unit %d: %m\n", ifnum);
+ }
+ if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0)
+ fatal("Couldn't connect to interface unit %d: %m", ifnum);
+ set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
+
+ ifunit = ifnum;
+ return 1;
+}
+
+/********************************************************************
+ *
+ * clean_check - Fetch the flags for the device and generate
+ * appropriate error messages.
+ */
+void clean_check(void)
+{
+ int x;
+ char *s;
+
+ if (still_ppp()) {
+ if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) {
+ s = NULL;
+ switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) {
+ case SC_RCV_B7_0:
+ s = "all had bit 7 set to 1";
+ break;
+
+ case SC_RCV_B7_1:
+ s = "all had bit 7 set to 0";
+ break;
+
+ case SC_RCV_EVNP:
+ s = "all had odd parity";
+ break;
+
+ case SC_RCV_ODDP:
+ s = "all had even parity";
+ break;
+ }
+
+ if (s != NULL) {
+ warn("Receive serial link is not 8-bit clean:");
+ warn("Problem: %s", s);
+ }
+ }
+ }
+}
+
+
+/*
+ * List of valid speeds.
+ */
+
+struct speed {
+ int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+ { 50, B50 },
+#endif
+#ifdef B75
+ { 75, B75 },
+#endif
+#ifdef B110
+ { 110, B110 },
+#endif
+#ifdef B134
+ { 134, B134 },
+#endif
+#ifdef B150
+ { 150, B150 },
+#endif
+#ifdef B200
+ { 200, B200 },
+#endif
+#ifdef B300
+ { 300, B300 },
+#endif
+#ifdef B600
+ { 600, B600 },
+#endif
+#ifdef B1200
+ { 1200, B1200 },
+#endif
+#ifdef B1800
+ { 1800, B1800 },
+#endif
+#ifdef B2000
+ { 2000, B2000 },
+#endif
+#ifdef B2400
+ { 2400, B2400 },
+#endif
+#ifdef B3600
+ { 3600, B3600 },
+#endif
+#ifdef B4800
+ { 4800, B4800 },
+#endif
+#ifdef B7200
+ { 7200, B7200 },
+#endif
+#ifdef B9600
+ { 9600, B9600 },
+#endif
+#ifdef B19200
+ { 19200, B19200 },
+#endif
+#ifdef B38400
+ { 38400, B38400 },
+#endif
+#ifdef B57600
+ { 57600, B57600 },
+#endif
+#ifdef B76800
+ { 76800, B76800 },
+#endif
+#ifdef B115200
+ { 115200, B115200 },
+#endif
+#ifdef EXTA
+ { 19200, EXTA },
+#endif
+#ifdef EXTB
+ { 38400, EXTB },
+#endif
+#ifdef B230400
+ { 230400, B230400 },
+#endif
+#ifdef B460800
+ { 460800, B460800 },
+#endif
+#ifdef B921600
+ { 921600, B921600 },
+#endif
+ { 0, 0 }
+};
+
+/********************************************************************
+ *
+ * Translate from bits/second to a speed_t.
+ */
+
+static int translate_speed (int bps)
+{
+ struct speed *speedp;
+
+ if (bps != 0) {
+ for (speedp = speeds; speedp->speed_int; speedp++) {
+ if (bps == speedp->speed_int)
+ return speedp->speed_val;
+ }
+ warn("speed %d not supported", bps);
+ }
+ return 0;
+}
+
+/********************************************************************
+ *
+ * Translate from a speed_t to bits/second.
+ */
+
+static int baud_rate_of (int speed)
+{
+ struct speed *speedp;
+
+ if (speed != 0) {
+ for (speedp = speeds; speedp->speed_int; speedp++) {
+ if (speed == speedp->speed_val)
+ return speedp->speed_int;
+ }
+ }
+ return 0;
+}
+
+/********************************************************************
+ *
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc. If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+
+void set_up_tty(int tty_fd, int local)
+{
+ int speed;
+ struct termios tios;
+
+ setdtr(tty_fd, 1);
+ if (tcgetattr(tty_fd, &tios) < 0) {
+ if (!ok_error(errno))
+ fatal("tcgetattr: %m(%d)", errno);
+ return;
+ }
+
+ if (!restore_term)
+ inittermios = tios;
+
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+ tios.c_cflag |= CS8 | CREAD | HUPCL;
+
+ tios.c_iflag = IGNBRK | IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+
+ if (local || !modem)
+ tios.c_cflag ^= (CLOCAL | HUPCL);
+
+ switch (crtscts) {
+ case 1:
+ tios.c_cflag |= CRTSCTS;
+ break;
+
+ case -2:
+ tios.c_iflag |= IXON | IXOFF;
+ tios.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */
+ tios.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */
+ break;
+
+ case -1:
+ tios.c_cflag &= ~CRTSCTS;
+ break;
+
+ default:
+ break;
+ }
+
+ speed = translate_speed(inspeed);
+ if (speed) {
+ cfsetospeed (&tios, speed);
+ cfsetispeed (&tios, speed);
+ }
+/*
+ * We can't proceed if the serial port speed is B0,
+ * since that implies that the serial port is disabled.
+ */
+ else {
+ speed = cfgetospeed(&tios);
+ if (speed == B0)
+ fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
+ }
+
+ if (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0)
+ if (!ok_error(errno))
+ fatal("tcsetattr: %m");
+
+ baud_rate = baud_rate_of(speed);
+ restore_term = 1;
+}
+
+/********************************************************************
+ *
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+
+void setdtr (int tty_fd, int on)
+{
+ int modembits = TIOCM_DTR;
+
+ ioctl(tty_fd, (on ? TIOCMBIS : TIOCMBIC), &modembits);
+}
+
+/********************************************************************
+ *
+ * restore_tty - restore the terminal to the saved settings.
+ */
+
+void restore_tty (int tty_fd)
+{
+ if (restore_term) {
+ restore_term = 0;
+/*
+ * Turn off echoing, because otherwise we can get into
+ * a loop with the tty and the modem echoing to each other.
+ * We presume we are the sole user of this tty device, so
+ * when we close it, it will revert to its defaults anyway.
+ */
+ if (!default_device)
+ inittermios.c_lflag &= ~(ECHO | ECHONL);
+
+ if (tcsetattr(tty_fd, TCSAFLUSH, &inittermios) < 0) {
+ if (! ok_error (errno))
+ warn("tcsetattr: %m");
+ }
+ }
+}
+
+/********************************************************************
+ *
+ * output - Output PPP packet.
+ */
+
+void output (int unit, unsigned char *p, int len)
+{
+ int fd = ppp_fd;
+ int proto;
+
+ if (debug)
+ dbglog("sent %P", p, len);
+
+ if (len < PPP_HDRLEN)
+ return;
+ if (new_style_driver) {
+ p += 2;
+ len -= 2;
+ proto = (p[0] << 8) + p[1];
+ if (ifunit >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
+ fd = ppp_dev_fd;
+ }
+ if (write(fd, p, len) < 0) {
+ if (errno == EWOULDBLOCK || errno == ENOBUFS
+ || errno == ENXIO || errno == EIO || errno == EINTR)
+ warn("write: warning: %m (%d)", errno);
+ else
+ error("write: %m (%d)", errno);
+ }
+}
+
+/********************************************************************
+ *
+ * wait_input - wait until there is data available,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+
+void wait_input(struct timeval *timo)
+{
+ fd_set ready, exc;
+ int n;
+
+ ready = in_fds;
+ exc = in_fds;
+ n = select(max_in_fd + 1, &ready, NULL, &exc, timo);
+ if (n < 0 && errno != EINTR)
+ fatal("select: %m(%d)", errno);
+}
+
+/*
+ * add_fd - add an fd to the set that wait_input waits for.
+ */
+void add_fd(int fd)
+{
+ FD_SET(fd, &in_fds);
+ if (fd > max_in_fd)
+ max_in_fd = fd;
+}
+
+/*
+ * remove_fd - remove an fd from the set that wait_input waits for.
+ */
+void remove_fd(int fd)
+{
+ FD_CLR(fd, &in_fds);
+}
+
+
+/********************************************************************
+ *
+ * read_packet - get a PPP packet from the serial device.
+ */
+
+int read_packet (unsigned char *buf)
+{
+ int len, nr;
+
+ len = PPP_MRU + PPP_HDRLEN;
+ if (new_style_driver) {
+ *buf++ = PPP_ALLSTATIONS;
+ *buf++ = PPP_UI;
+ len -= 2;
+ }
+ nr = -1;
+ if (ppp_fd >= 0) {
+ nr = read(ppp_fd, buf, len);
+ if (nr < 0 && errno != EWOULDBLOCK && errno != EIO && errno != EINTR)
+ error("read: %m");
+ if (nr < 0 && errno == ENXIO)
+ return 0;
+ }
+ if (nr < 0 && new_style_driver && ifunit >= 0) {
+ /* N.B. we read ppp_fd first since LCP packets come in there. */
+ nr = read(ppp_dev_fd, buf, len);
+ if (nr < 0 && errno != EWOULDBLOCK && errno != EIO && errno != EINTR)
+ error("read /dev/ppp: %m");
+ if (nr < 0 && errno == ENXIO)
+ return 0;
+ }
+ return (new_style_driver && nr > 0)? nr+2: nr;
+}
+
+/********************************************************************
+ *
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output(void)
+{
+ int rv = 0;
+ int n;
+
+ if (new_style_driver) {
+ while ((n = read_packet(inpacket_buf)) > 0)
+ if (loop_frame(inpacket_buf, n))
+ rv = 1;
+ return rv;
+ }
+
+ while ((n = read(master_fd, inbuf, sizeof(inbuf))) > 0)
+ if (loop_chars(inbuf, n))
+ rv = 1;
+
+ if (n == 0)
+ fatal("eof on loopback");
+
+ if (errno != EWOULDBLOCK)
+ fatal("read from loopback: %m(%d)", errno);
+
+ return rv;
+}
+
+/*
+ * netif_set_mtu - set the MTU on the PPP network interface.
+ */
+void
+netif_set_mtu(int unit, int mtu)
+{
+ struct ifreq ifr;
+
+ SYSDEBUG ((LOG_DEBUG, "netif_set_mtu: mtu = %d\n", mtu));
+
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_mtu = mtu;
+
+ if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
+ fatal("ioctl(SIOCSIFMTU): %m");
+}
+
+/********************************************************************
+ *
+ * tty_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+
+void tty_send_config (int mtu,u_int32_t asyncmap,int pcomp,int accomp)
+{
+ u_int x;
+
+/*
+ * Set the asyncmap and other parameters for the ppp device
+ */
+ if (!still_ppp())
+ return;
+ link_mtu = mtu;
+ SYSDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap));
+ if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
+ if (!ok_error(errno))
+ fatal("ioctl(PPPIOCSASYNCMAP): %m(%d)", errno);
+ return;
+ }
+
+ x = get_flags(ppp_fd);
+ x = pcomp ? x | SC_COMP_PROT : x & ~SC_COMP_PROT;
+ x = accomp ? x | SC_COMP_AC : x & ~SC_COMP_AC;
+ x = sync_serial ? x | SC_SYNC : x & ~SC_SYNC;
+ set_flags(ppp_fd, x);
+}
+
+/********************************************************************
+ *
+ * tty_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+
+void tty_set_xaccm (ext_accm accm)
+{
+ SYSDEBUG ((LOG_DEBUG, "set_xaccm: %08lx %08lx %08lx %08lx\n",
+ accm[0], accm[1], accm[2], accm[3]));
+
+ if (!still_ppp())
+ return;
+ if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) {
+ if ( ! ok_error (errno))
+ warn("ioctl(set extended ACCM): %m(%d)", errno);
+ }
+}
+
+/********************************************************************
+ *
+ * tty_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+
+void tty_recv_config (int mru,u_int32_t asyncmap,int pcomp,int accomp)
+{
+ SYSDEBUG ((LOG_DEBUG, "recv_config: mru = %d\n", mru));
+/*
+ * If we were called because the link has gone down then there is nothing
+ * which may be done. Just return without incident.
+ */
+ if (!still_ppp())
+ return;
+/*
+ * Set the receiver parameters
+ */
+ if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
+ if ( ! ok_error (errno))
+ error("ioctl(PPPIOCSMRU): %m(%d)", errno);
+ }
+ if (new_style_driver && ifunit >= 0
+ && ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0)
+ error("Couldn't set MRU in generic PPP layer: %m");
+
+ SYSDEBUG ((LOG_DEBUG, "recv_config: asyncmap = %lx\n", asyncmap));
+ if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
+ if (!ok_error(errno))
+ error("ioctl(PPPIOCSRASYNCMAP): %m(%d)", errno);
+ }
+}
+
+/********************************************************************
+ *
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+
+int ccp_test (int unit, u_char *opt_ptr, int opt_len, int for_transmit)
+{
+ struct ppp_option_data data;
+
+ memset (&data, '\0', sizeof (data));
+ data.ptr = opt_ptr;
+ data.length = opt_len;
+ data.transmit = for_transmit;
+
+ if (ioctl(ppp_dev_fd, PPPIOCSCOMPRESS, (caddr_t) &data) >= 0)
+ return 1;
+
+ return (errno == ENOBUFS)? 0: -1;
+}
+
+/********************************************************************
+ *
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+
+void ccp_flags_set (int unit, int isopen, int isup)
+{
+ if (still_ppp()) {
+ int x = get_flags(ppp_dev_fd);
+ x = isopen? x | SC_CCP_OPEN : x &~ SC_CCP_OPEN;
+ x = isup? x | SC_CCP_UP : x &~ SC_CCP_UP;
+ set_flags (ppp_dev_fd, x);
+ }
+}
+
+#ifdef PPP_FILTER
+/*
+ * set_filters - set the active and pass filters in the kernel driver.
+ */
+int set_filters(struct bpf_program *pass, struct bpf_program *active)
+{
+ struct sock_fprog fp;
+
+ fp.len = pass->bf_len;
+ fp.filter = (struct sock_filter *) pass->bf_insns;
+ if (ioctl(ppp_dev_fd, PPPIOCSPASS, &fp) < 0) {
+ if (errno == ENOTTY)
+ warn("kernel does not support PPP filtering");
+ else
+ error("Couldn't set pass-filter in kernel: %m");
+ return 0;
+ }
+ fp.len = active->bf_len;
+ fp.filter = (struct sock_filter *) active->bf_insns;
+ if (ioctl(ppp_dev_fd, PPPIOCSACTIVE, &fp) < 0) {
+ error("Couldn't set active-filter in kernel: %m");
+ return 0;
+ }
+ return 1;
+}
+#endif /* PPP_FILTER */
+
+/********************************************************************
+ *
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+ int u;
+ struct ppp_idle *ip;
+{
+ return ioctl(ppp_dev_fd, PPPIOCGIDLE, ip) >= 0;
+}
+
+/********************************************************************
+ *
+ * get_ppp_stats - return statistics for the link.
+ */
+int
+get_ppp_stats(u, stats)
+ int u;
+ struct pppd_stats *stats;
+{
+ struct ifpppstatsreq req;
+
+ memset (&req, 0, sizeof (req));
+
+ req.stats_ptr = (caddr_t) &req.stats;
+ strlcpy(req.ifr__name, ifname, sizeof(req.ifr__name));
+ if (ioctl(sock_fd, SIOCGPPPSTATS, &req) < 0) {
+ error("Couldn't get PPP statistics: %m");
+ return 0;
+ }
+ stats->bytes_in = req.stats.p.ppp_ibytes;
+ stats->bytes_out = req.stats.p.ppp_obytes;
+ return 1;
+}
+
+/********************************************************************
+ *
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise. This is necessary because of patent nonsense.
+ */
+
+int ccp_fatal_error (int unit)
+{
+ int x = get_flags(ppp_dev_fd);
+
+ return x & SC_DC_FERROR;
+}
+
+/********************************************************************
+ *
+ * path_to_procfs - find the path to the proc file system mount point
+ */
+static char proc_path[MAXPATHLEN];
+static int proc_path_len;
+
+static char *path_to_procfs(const char *tail)
+{
+ struct mntent *mntent;
+ FILE *fp;
+
+ if (proc_path_len == 0) {
+ /* Default the mount location of /proc */
+ strlcpy (proc_path, "/proc", sizeof(proc_path));
+ proc_path_len = 5;
+ fp = fopen(MOUNTED, "r");
+ if (fp != NULL) {
+ while ((mntent = getmntent(fp)) != NULL) {
+ if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0)
+ continue;
+ if (strcmp(mntent->mnt_type, "proc") == 0) {
+ strlcpy(proc_path, mntent->mnt_dir, sizeof(proc_path));
+ proc_path_len = strlen(proc_path);
+ break;
+ }
+ }
+ fclose (fp);
+ }
+ }
+
+ strlcpy(proc_path + proc_path_len, tail,
+ sizeof(proc_path) - proc_path_len);
+ return proc_path;
+}
+
+/*
+ * /proc/net/route parsing stuff.
+ */
+#define ROUTE_MAX_COLS 12
+FILE *route_fd = (FILE *) 0;
+static char route_buffer[512];
+static int route_dev_col, route_dest_col, route_gw_col;
+static int route_flags_col, route_mask_col;
+static int route_num_cols;
+
+static int open_route_table (void);
+static void close_route_table (void);
+static int read_route_table (struct rtentry *rt);
+
+/********************************************************************
+ *
+ * close_route_table - close the interface to the route table
+ */
+
+static void close_route_table (void)
+{
+ if (route_fd != (FILE *) 0) {
+ fclose (route_fd);
+ route_fd = (FILE *) 0;
+ }
+}
+
+/********************************************************************
+ *
+ * open_route_table - open the interface to the route table
+ */
+static char route_delims[] = " \t\n";
+
+static int open_route_table (void)
+{
+ char *path;
+
+ close_route_table();
+
+ path = path_to_procfs("/net/route");
+ route_fd = fopen (path, "r");
+ if (route_fd == NULL) {
+ error("can't open routing table %s: %m", path);
+ return 0;
+ }
+
+ route_dev_col = 0; /* default to usual columns */
+ route_dest_col = 1;
+ route_gw_col = 2;
+ route_flags_col = 3;
+ route_mask_col = 7;
+ route_num_cols = 8;
+
+ /* parse header line */
+ if (fgets(route_buffer, sizeof(route_buffer), route_fd) != 0) {
+ char *p = route_buffer, *q;
+ int col;
+ for (col = 0; col < ROUTE_MAX_COLS; ++col) {
+ int used = 1;
+ if ((q = strtok(p, route_delims)) == 0)
+ break;
+ if (strcasecmp(q, "iface") == 0)
+ route_dev_col = col;
+ else if (strcasecmp(q, "destination") == 0)
+ route_dest_col = col;
+ else if (strcasecmp(q, "gateway") == 0)
+ route_gw_col = col;
+ else if (strcasecmp(q, "flags") == 0)
+ route_flags_col = col;
+ else if (strcasecmp(q, "mask") == 0)
+ route_mask_col = col;
+ else
+ used = 0;
+ if (used && col >= route_num_cols)
+ route_num_cols = col + 1;
+ p = NULL;
+ }
+ }
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * read_route_table - read the next entry from the route table
+ */
+
+static int read_route_table(struct rtentry *rt)
+{
+ char *cols[ROUTE_MAX_COLS], *p;
+ int col;
+
+ memset (rt, '\0', sizeof (struct rtentry));
+
+ if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0)
+ return 0;
+
+ p = route_buffer;
+ for (col = 0; col < route_num_cols; ++col) {
+ cols[col] = strtok(p, route_delims);
+ if (cols[col] == NULL)
+ return 0; /* didn't get enough columns */
+ p = NULL;
+ }
+
+ SIN_ADDR(rt->rt_dst) = strtoul(cols[route_dest_col], NULL, 16);
+ SIN_ADDR(rt->rt_gateway) = strtoul(cols[route_gw_col], NULL, 16);
+ SIN_ADDR(rt->rt_genmask) = strtoul(cols[route_mask_col], NULL, 16);
+
+ rt->rt_flags = (short) strtoul(cols[route_flags_col], NULL, 16);
+ rt->rt_dev = cols[route_dev_col];
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * defaultroute_exists - determine if there is a default route
+ */
+
+static int defaultroute_exists (struct rtentry *rt)
+{
+ int result = 0;
+
+ if (!open_route_table())
+ return 0;
+
+ while (read_route_table(rt) != 0) {
+ if ((rt->rt_flags & RTF_UP) == 0)
+ continue;
+
+ if (kernel_version > KVERSION(2,1,0) && SIN_ADDR(rt->rt_genmask) != 0)
+ continue;
+ if (SIN_ADDR(rt->rt_dst) == 0L) {
+ result = 1;
+ break;
+ }
+ }
+
+ close_route_table();
+ return result;
+}
+
+/*
+ * have_route_to - determine if the system has any route to
+ * a given IP address. `addr' is in network byte order.
+ * Return value is 1 if yes, 0 if no, -1 if don't know.
+ * For demand mode to work properly, we have to ignore routes
+ * through our own interface.
+ */
+int have_route_to(u_int32_t addr)
+{
+ struct rtentry rt;
+ int result = 0;
+
+ if (!open_route_table())
+ return -1; /* don't know */
+
+ while (read_route_table(&rt)) {
+ if ((rt.rt_flags & RTF_UP) == 0 || strcmp(rt.rt_dev, ifname) == 0)
+ continue;
+ if ((addr & SIN_ADDR(rt.rt_genmask)) == SIN_ADDR(rt.rt_dst)) {
+ result = 1;
+ break;
+ }
+ }
+
+ close_route_table();
+ return result;
+}
+
+/********************************************************************
+ *
+ * sifdefaultroute - assign a default route through the address given.
+ */
+
+int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
+{
+ struct rtentry rt;
+
+ if (defaultroute_exists(&rt) && strcmp(rt.rt_dev, ifname) != 0) {
+ u_int32_t old_gateway = SIN_ADDR(rt.rt_gateway);
+
+ if (old_gateway != gateway)
+ error("not replacing existing default route to %s [%I]",
+ rt.rt_dev, old_gateway);
+ return 0;
+ }
+
+ memset (&rt, '\0', sizeof (rt));
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = 0L;
+ }
+
+ SIN_ADDR(rt.rt_gateway) = gateway;
+
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+ if ( ! ok_error ( errno ))
+ error("default route ioctl(SIOCADDRT): %m(%d)", errno);
+ return 0;
+ }
+
+ default_route_gateway = gateway;
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifdefaultroute - delete a default route through the address given.
+ */
+
+int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
+{
+ struct rtentry rt;
+
+ default_route_gateway = 0;
+
+ memset (&rt, '\0', sizeof (rt));
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = 0L;
+ }
+
+ SIN_ADDR(rt.rt_gateway) = gateway;
+
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+ if (still_ppp()) {
+ if ( ! ok_error ( errno ))
+ error("default route ioctl(SIOCDELRT): %m (%d)", errno);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+
+int sifproxyarp (int unit, u_int32_t his_adr)
+{
+ struct arpreq arpreq;
+ char *forw_path;
+
+ if (has_proxy_arp == 0) {
+ memset (&arpreq, '\0', sizeof(arpreq));
+
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ SIN_ADDR(arpreq.arp_pa) = his_adr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+/*
+ * Get the hardware address of an interface on the same subnet
+ * as our local address.
+ */
+ if (!get_ether_addr(his_adr, &arpreq.arp_ha, proxy_arp_dev,
+ sizeof(proxy_arp_dev))) {
+ error("Cannot determine ethernet address for proxy ARP");
+ return 0;
+ }
+ strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev));
+
+ if (ioctl(sock_fd, SIOCSARP, (caddr_t)&arpreq) < 0) {
+ if ( ! ok_error ( errno ))
+ error("ioctl(SIOCSARP): %m(%d)", errno);
+ return 0;
+ }
+ proxy_arp_addr = his_adr;
+ has_proxy_arp = 1;
+
+ if (tune_kernel) {
+ forw_path = path_to_procfs("/sys/net/ipv4/ip_forward");
+ if (forw_path != 0) {
+ int fd = open(forw_path, O_WRONLY);
+ if (fd >= 0) {
+ if (write(fd, "1", 1) != 1)
+ error("Couldn't enable IP forwarding: %m");
+ close(fd);
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+
+int cifproxyarp (int unit, u_int32_t his_adr)
+{
+ struct arpreq arpreq;
+
+ if (has_proxy_arp) {
+ has_proxy_arp = 0;
+ memset (&arpreq, '\0', sizeof(arpreq));
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ SIN_ADDR(arpreq.arp_pa) = his_adr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev));
+
+ if (ioctl(sock_fd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+ if ( ! ok_error ( errno ))
+ warn("ioctl(SIOCDARP): %m(%d)", errno);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/********************************************************************
+ *
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+
+static int get_ether_addr (u_int32_t ipaddr,
+ struct sockaddr *hwaddr,
+ char *name, int namelen)
+{
+ struct ifreq *ifr, *ifend;
+ u_int32_t ina, mask;
+ char *aliasp;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
+ if ( ! ok_error ( errno ))
+ error("ioctl(SIOCGIFCONF): %m(%d)", errno);
+ return 0;
+ }
+
+ SYSDEBUG ((LOG_DEBUG, "proxy arp: scanning %d interfaces for IP %s",
+ ifc.ifc_len / sizeof(struct ifreq), ip_ntoa(ipaddr)));
+/*
+ * Scan through looking for an interface with an Internet
+ * address on the same subnet as `ipaddr'.
+ */
+ ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ ina = SIN_ADDR(ifr->ifr_addr);
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ SYSDEBUG ((LOG_DEBUG, "proxy arp: examining interface %s",
+ ifreq.ifr_name));
+/*
+ * Check that the interface is up, and not point-to-point
+ * nor loopback.
+ */
+ if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+
+ if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
+ continue;
+/*
+ * Get its netmask and check that it's on the right subnet.
+ */
+ if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+
+ mask = SIN_ADDR(ifreq.ifr_addr);
+ SYSDEBUG ((LOG_DEBUG, "proxy arp: interface addr %s mask %lx",
+ ip_ntoa(ina), ntohl(mask)));
+
+ if (((ipaddr ^ ina) & mask) != 0)
+ continue;
+ break;
+ }
+ }
+
+ if (ifr >= ifend)
+ return 0;
+
+ strlcpy(name, ifreq.ifr_name, namelen);
+
+ /* trim off the :1 in eth0:1 */
+ aliasp = strchr(name, ':');
+ if (aliasp != 0)
+ *aliasp = 0;
+
+ info("found interface %s for proxy arp", name);
+/*
+ * Now get the hardware address.
+ */
+ memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+ if (ioctl (sock_fd, SIOCGIFHWADDR, &ifreq) < 0) {
+ error("SIOCGIFHWADDR(%s): %m(%d)", ifreq.ifr_name, errno);
+ return 0;
+ }
+
+ memcpy (hwaddr,
+ &ifreq.ifr_hwaddr,
+ sizeof (struct sockaddr));
+
+ SYSDEBUG ((LOG_DEBUG,
+ "proxy arp: found hwaddr %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ (int) ((unsigned char *) &hwaddr->sa_data)[0],
+ (int) ((unsigned char *) &hwaddr->sa_data)[1],
+ (int) ((unsigned char *) &hwaddr->sa_data)[2],
+ (int) ((unsigned char *) &hwaddr->sa_data)[3],
+ (int) ((unsigned char *) &hwaddr->sa_data)[4],
+ (int) ((unsigned char *) &hwaddr->sa_data)[5],
+ (int) ((unsigned char *) &hwaddr->sa_data)[6],
+ (int) ((unsigned char *) &hwaddr->sa_data)[7]));
+ return 1;
+}
+
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *name)
+{
+ struct ifreq ifreq;
+ int ret, sock_fd;
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0)
+ return 0;
+ memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr));
+ strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
+ ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq);
+ close(sock_fd);
+ if (ret >= 0)
+ memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6);
+ return ret;
+}
+
+/*
+ * get_first_ethernet - return the name of the first ethernet-style
+ * interface on this system.
+ */
+char *
+get_first_ethernet()
+{
+ return "eth0";
+}
+
+/********************************************************************
+ *
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'. If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+
+u_int32_t GetMask (u_int32_t addr)
+{
+ u_int32_t mask, nmask, ina;
+ struct ifreq *ifr, *ifend, ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ addr = ntohl(addr);
+
+ if (IN_CLASSA(addr)) /* determine network mask for address class */
+ nmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(addr))
+ nmask = IN_CLASSB_NET;
+ else
+ nmask = IN_CLASSC_NET;
+
+ /* class D nets are disallowed by bad_ip_adrs */
+ mask = netmask | htonl(nmask);
+/*
+ * Scan through the system's network interfaces.
+ */
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
+ if ( ! ok_error ( errno ))
+ warn("ioctl(SIOCGIFCONF): %m(%d)", errno);
+ return mask;
+ }
+
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
+/*
+ * Check the interface's internet address.
+ */
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ ina = SIN_ADDR(ifr->ifr_addr);
+ if (((ntohl(ina) ^ addr) & nmask) != 0)
+ continue;
+/*
+ * Check that the interface is up, and not point-to-point nor loopback.
+ */
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+
+ if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
+ continue;
+/*
+ * Get its netmask and OR it into our mask.
+ */
+ if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask |= SIN_ADDR(ifreq.ifr_addr);
+ break;
+ }
+ return mask;
+}
+
+/********************************************************************
+ *
+ * Internal routine to decode the version.modification.patch level
+ */
+
+static void decode_version (char *buf, int *version,
+ int *modification, int *patch)
+{
+ char *endp;
+
+ *version = (int) strtoul (buf, &endp, 10);
+ *modification = 0;
+ *patch = 0;
+
+ if (endp != buf && *endp == '.') {
+ buf = endp + 1;
+ *modification = (int) strtoul (buf, &endp, 10);
+ if (endp != buf && *endp == '.') {
+ buf = endp + 1;
+ *patch = (int) strtoul (buf, &buf, 10);
+ }
+ }
+}
+
+/********************************************************************
+ *
+ * Procedure to determine if the PPP line discipline is registered.
+ */
+
+static int
+ppp_registered(void)
+{
+ int local_fd;
+ int mfd = -1;
+ int ret = 0;
+ char slave[16];
+
+ /*
+ * We used to open the serial device and set it to the ppp line
+ * discipline here, in order to create a ppp unit. But that is
+ * not a good idea - the user might have specified a device that
+ * they can't open (permission, or maybe it doesn't really exist).
+ * So we grab a pty master/slave pair and use that.
+ */
+ if (!get_pty(&mfd, &local_fd, slave, 0)) {
+ no_ppp_msg = "Couldn't determine if PPP is supported (no free ptys)";
+ return 0;
+ }
+
+ /*
+ * Try to put the device into the PPP discipline.
+ */
+ if (ioctl(local_fd, TIOCSETD, &ppp_disc) < 0) {
+ error("ioctl(TIOCSETD(PPP)): %m(%d)", errno);
+ } else
+ ret = 1;
+
+ close(local_fd);
+ close(mfd);
+ return ret;
+}
+
+/********************************************************************
+ *
+ * ppp_available - check whether the system has any ppp interfaces
+ * (in fact we check whether we can do an ioctl on ppp0).
+ */
+
+int ppp_available(void)
+{
+ int s, ok, fd;
+ struct ifreq ifr;
+ int size;
+ int my_version, my_modification, my_patch;
+ int osmaj, osmin, ospatch;
+
+ no_ppp_msg =
+ "This system lacks kernel support for PPP. This could be because\n"
+ "the PPP kernel module could not be loaded, or because PPP was not\n"
+ "included in the kernel configuration. If PPP was included as a\n"
+ "module, try `/sbin/modprobe -v ppp'. If that fails, check that\n"
+ "ppp.o exists in /lib/modules/`uname -r`/net.\n"
+ "See README.linux file in the ppp distribution for more details.\n";
+
+ /* get the kernel version now, since we are called before sys_init */
+ uname(&utsname);
+ osmaj = osmin = ospatch = 0;
+ sscanf(utsname.release, "%d.%d.%d", &osmaj, &osmin, &ospatch);
+ kernel_version = KVERSION(osmaj, osmin, ospatch);
+
+ fd = open("/dev/ppp", O_RDWR);
+#if 0
+ if (fd < 0 && errno == ENOENT) {
+ /* try making it and see if that helps. */
+ if (mknod("/dev/ppp", S_IFCHR | S_IRUSR | S_IWUSR,
+ makedev(108, 0)) >= 0) {
+ fd = open("/dev/ppp", O_RDWR);
+ if (fd >= 0)
+ info("Created /dev/ppp device node");
+ else
+ unlink("/dev/ppp"); /* didn't work, undo the mknod */
+ } else if (errno == EEXIST) {
+ fd = open("/dev/ppp", O_RDWR);
+ }
+ }
+#endif /* 0 */
+ if (fd >= 0) {
+ new_style_driver = 1;
+
+ /* XXX should get from driver */
+ driver_version = 2;
+ driver_modification = 4;
+ driver_patch = 0;
+ close(fd);
+ return 1;
+ }
+ if (kernel_version >= KVERSION(2,3,13)) {
+ if (errno == ENOENT)
+ no_ppp_msg =
+ "pppd is unable to open the /dev/ppp device.\n"
+ "You need to create the /dev/ppp device node by\n"
+ "executing the following command as root:\n"
+ " mknod /dev/ppp c 108 0\n";
+ return 0;
+ }
+
+/*
+ * Open a socket for doing the ioctl operations.
+ */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ return 0;
+
+ strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+ ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+/*
+ * If the device did not exist then attempt to create one by putting the
+ * current tty into the PPP discipline. If this works then obtain the
+ * flags for the device again.
+ */
+ if (!ok) {
+ if (ppp_registered()) {
+ strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+ ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+ }
+ }
+/*
+ * Ensure that the hardware address is for PPP and not something else
+ */
+ if (ok)
+ ok = ioctl (s, SIOCGIFHWADDR, (caddr_t) &ifr) >= 0;
+
+ if (ok && ((ifr.ifr_hwaddr.sa_family & ~0xFF) != ARPHRD_PPP))
+ ok = 0;
+
+/*
+ * This is the PPP device. Validate the version of the driver at this
+ * point to ensure that this program will work with the driver.
+ */
+ if (ok) {
+ char abBuffer [1024];
+
+ ifr.ifr_data = abBuffer;
+ size = ioctl (s, SIOCGPPPVER, (caddr_t) &ifr);
+ if (size < 0) {
+ error("Couldn't read driver version: %m");
+ ok = 0;
+ no_ppp_msg = "Sorry, couldn't verify kernel driver version\n";
+
+ } else {
+ decode_version(abBuffer,
+ &driver_version,
+ &driver_modification,
+ &driver_patch);
+/*
+ * Validate the version of the driver against the version that we used.
+ */
+ decode_version(VERSION,
+ &my_version,
+ &my_modification,
+ &my_patch);
+
+ /* The version numbers must match */
+ if (driver_version != my_version)
+ ok = 0;
+
+ /* The modification levels must be legal */
+ if (driver_modification < 3) {
+ if (driver_modification >= 2) {
+ /* we can cope with 2.2.0 and above */
+ driver_is_old = 1;
+ } else {
+ ok = 0;
+ }
+ }
+
+ close (s);
+ if (!ok) {
+ slprintf(route_buffer, sizeof(route_buffer),
+ "Sorry - PPP driver version %d.%d.%d is out of date\n",
+ driver_version, driver_modification, driver_patch);
+
+ no_ppp_msg = route_buffer;
+ }
+ }
+ }
+ return ok;
+}
+
+/********************************************************************
+ *
+ * Update the wtmp file with the appropriate user name and tty device.
+ */
+
+void logwtmp (const char *line, const char *name, const char *host)
+{
+ struct utmp ut, *utp;
+ pid_t mypid = getpid();
+#if __GLIBC__ < 2
+ int wtmp;
+#endif
+
+/*
+ * Update the signon database for users.
+ * Christoph Lameter: Copied from poeigl-1.36 Jan 3, 1996
+ */
+ utmpname(_PATH_UTMP);
+ setutent();
+ while ((utp = getutent()) && (utp->ut_pid != mypid))
+ /* nothing */;
+
+ /* Is this call really necessary? There is another one after the 'put' */
+ endutent();
+
+ if (utp)
+ memcpy(&ut, utp, sizeof(ut));
+ else
+ /* some gettys/telnetds don't initialize utmp... */
+ memset(&ut, 0, sizeof(ut));
+
+ if (ut.ut_id[0] == 0)
+ strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
+
+ strncpy(ut.ut_user, name, sizeof(ut.ut_user));
+ strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+
+ time(&ut.ut_time);
+
+ ut.ut_type = USER_PROCESS;
+ ut.ut_pid = mypid;
+
+ /* Insert the host name if one is supplied */
+ if (*host)
+ strncpy (ut.ut_host, host, sizeof(ut.ut_host));
+
+ /* Insert the IP address of the remote system if IP is enabled */
+ if (ipcp_protent.enabled_flag && ipcp_hisoptions[0].neg_addr)
+ memcpy(&ut.ut_addr, (char *) &ipcp_hisoptions[0].hisaddr,
+ sizeof(ut.ut_addr));
+
+ /* CL: Makes sure that the logout works */
+ if (*host == 0 && *name==0)
+ ut.ut_host[0]=0;
+
+ pututline(&ut);
+ endutent();
+/*
+ * Update the wtmp file.
+ */
+#if __GLIBC__ >= 2
+ updwtmp(_PATH_WTMP, &ut);
+#else
+ wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY);
+ if (wtmp >= 0) {
+ flock(wtmp, LOCK_EX);
+
+ if (write (wtmp, (char *)&ut, sizeof(ut)) != sizeof(ut))
+ warn("error writing %s: %m", _PATH_WTMP);
+
+ flock(wtmp, LOCK_UN);
+
+ close (wtmp);
+ }
+#endif
+}
+
+
+/********************************************************************
+ *
+ * sifvjcomp - config tcp header compression
+ */
+
+int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid)
+{
+ u_int x = get_flags(ppp_dev_fd);
+
+ if (vjcomp) {
+ if (ioctl (ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(PPPIOCSMAXCID): %m(%d)", errno);
+ vjcomp = 0;
+ }
+ }
+
+ x = vjcomp ? x | SC_COMP_TCP : x &~ SC_COMP_TCP;
+ x = cidcomp ? x & ~SC_NO_TCP_CCID : x | SC_NO_TCP_CCID;
+ set_flags (ppp_dev_fd, x);
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+
+int sifup(int u)
+{
+ struct ifreq ifr;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl (SIOCGIFFLAGS): %m(%d)", errno);
+ return 0;
+ }
+
+ ifr.ifr_flags |= (IFF_UP | IFF_POINTOPOINT);
+ if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFFLAGS): %m(%d)", errno);
+ return 0;
+ }
+ if_is_up++;
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifdown - Disable the indicated protocol and config the interface
+ * down if there are no remaining protocols.
+ */
+
+int sifdown (int u)
+{
+ struct ifreq ifr;
+
+ if (if_is_up && --if_is_up > 0)
+ return 1;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl (SIOCGIFFLAGS): %m(%d)", errno);
+ return 0;
+ }
+
+ ifr.ifr_flags &= ~IFF_UP;
+ ifr.ifr_flags |= IFF_POINTOPOINT;
+ if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFFLAGS): %m(%d)", errno);
+ return 0;
+ }
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+
+int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
+ u_int32_t net_mask)
+{
+ struct ifreq ifr;
+ struct rtentry rt;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ memset (&rt, '\0', sizeof (rt));
+
+ SET_SA_FAMILY (ifr.ifr_addr, AF_INET);
+ SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET);
+ SET_SA_FAMILY (ifr.ifr_netmask, AF_INET);
+
+ strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+/*
+ * Set our IP address
+ */
+ SIN_ADDR(ifr.ifr_addr) = our_adr;
+ if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ if (errno != EEXIST) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFADDR): %m(%d)", errno);
+ }
+ else {
+ warn("ioctl(SIOCSIFADDR): Address already exists");
+ }
+ return (0);
+ }
+/*
+ * Set the gateway address
+ */
+ SIN_ADDR(ifr.ifr_dstaddr) = his_adr;
+ if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFDSTADDR): %m(%d)", errno);
+ return (0);
+ }
+/*
+ * Set the netmask.
+ * For recent kernels, force the netmask to 255.255.255.255.
+ */
+ if (kernel_version >= KVERSION(2,1,16))
+ net_mask = ~0L;
+ if (net_mask != 0) {
+ SIN_ADDR(ifr.ifr_netmask) = net_mask;
+ if (ioctl(sock_fd, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCSIFNETMASK): %m(%d)", errno);
+ return (0);
+ }
+ }
+/*
+ * Add the device route
+ */
+ if (kernel_version < KVERSION(2,1,16)) {
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+ rt.rt_dev = ifname;
+
+ SIN_ADDR(rt.rt_gateway) = 0L;
+ SIN_ADDR(rt.rt_dst) = his_adr;
+ rt.rt_flags = RTF_UP | RTF_HOST;
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = -1L;
+ }
+
+ if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(SIOCADDRT) device route: %m(%d)", errno);
+ return (0);
+ }
+ }
+
+ /* set ip_dynaddr in demand mode if address changes */
+ if (demand && tune_kernel && !dynaddr_set
+ && our_old_addr && our_old_addr != our_adr) {
+ /* set ip_dynaddr if possible */
+ char *path;
+ int fd;
+
+ path = path_to_procfs("/sys/net/ipv4/ip_dynaddr");
+ if (path != 0 && (fd = open(path, O_WRONLY)) >= 0) {
+ if (write(fd, "1", 1) != 1)
+ error("Couldn't enable dynamic IP addressing: %m");
+ close(fd);
+ }
+ dynaddr_set = 1; /* only 1 attempt */
+ }
+ our_old_addr = 0;
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+
+int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr)
+{
+ struct ifreq ifr;
+
+ if (kernel_version < KVERSION(2,1,16)) {
+/*
+ * Delete the route through the device
+ */
+ struct rtentry rt;
+ memset (&rt, '\0', sizeof (rt));
+
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+ rt.rt_dev = ifname;
+
+ SIN_ADDR(rt.rt_gateway) = 0;
+ SIN_ADDR(rt.rt_dst) = his_adr;
+ rt.rt_flags = RTF_UP | RTF_HOST;
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = -1L;
+ }
+
+ if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+ if (still_ppp() && ! ok_error (errno))
+ error("ioctl(SIOCDELRT) device route: %m(%d)", errno);
+ return (0);
+ }
+ }
+
+ /* This way it is possible to have an IPX-only or IPv6-only interface */
+ memset(&ifr, 0, sizeof(ifr));
+ SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno)) {
+ error("ioctl(SIOCSIFADDR): %m(%d)", errno);
+ return 0;
+ }
+ }
+
+ our_old_addr = our_adr;
+
+ return 1;
+}
+
+#ifdef INET6
+/********************************************************************
+ *
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+ struct in6_ifreq ifr6;
+ struct ifreq ifr;
+ struct in6_rtmsg rt6;
+
+ if (sock6_fd < 0) {
+ errno = -sock6_fd;
+ error("IPv6 socket creation failed: %m");
+ return 0;
+ }
+ memset(&ifr, 0, sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
+ error("sif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
+ return 0;
+ }
+
+ /* Local interface */
+ memset(&ifr6, 0, sizeof(ifr6));
+ IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = 10;
+
+ if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6) < 0) {
+ error("sif6addr: ioctl(SIOCSIFADDR): %m (%d)", errno);
+ return 0;
+ }
+
+ /* Route to remote host */
+ memset(&rt6, 0, sizeof(rt6));
+ IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64);
+ rt6.rtmsg_flags = RTF_UP;
+ rt6.rtmsg_dst_len = 10;
+ rt6.rtmsg_ifindex = ifr.ifr_ifindex;
+ rt6.rtmsg_metric = 1;
+
+ if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) {
+ error("sif6addr: ioctl(SIOCADDRT): %m (%d)", errno);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/********************************************************************
+ *
+ * cif6addr - Remove IPv6 address from interface
+ */
+int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+ struct ifreq ifr;
+ struct in6_ifreq ifr6;
+
+ if (sock6_fd < 0) {
+ errno = -sock6_fd;
+ error("IPv6 socket creation failed: %m");
+ return 0;
+ }
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
+ error("cif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
+ return 0;
+ }
+
+ memset(&ifr6, 0, sizeof(ifr6));
+ IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = 10;
+
+ if (ioctl(sock6_fd, SIOCDIFADDR, &ifr6) < 0) {
+ if (errno != EADDRNOTAVAIL) {
+ if (! ok_error (errno))
+ error("cif6addr: ioctl(SIOCDIFADDR): %m (%d)", errno);
+ }
+ else {
+ warn("cif6addr: ioctl(SIOCDIFADDR): No such address");
+ }
+ return (0);
+ }
+ return 1;
+}
+#endif /* INET6 */
+
+/*
+ * get_pty - get a pty master/slave pair and chown the slave side
+ * to the uid given. Assumes slave_name points to >= 16 bytes of space.
+ */
+int
+get_pty(master_fdp, slave_fdp, slave_name, uid)
+ int *master_fdp;
+ int *slave_fdp;
+ char *slave_name;
+ int uid;
+{
+ int i, mfd, sfd = -1;
+ char pty_name[16];
+ struct termios tios;
+
+#ifdef TIOCGPTN
+ /*
+ * Try the unix98 way first.
+ */
+ mfd = open("/dev/ptmx", O_RDWR);
+ if (mfd >= 0) {
+ int ptn;
+ if (ioctl(mfd, TIOCGPTN, &ptn) >= 0) {
+ slprintf(pty_name, sizeof(pty_name), "/dev/pts/%d", ptn);
+ chmod(pty_name, S_IRUSR | S_IWUSR);
+#ifdef TIOCSPTLCK
+ ptn = 0;
+ if (ioctl(mfd, TIOCSPTLCK, &ptn) < 0)
+ warn("Couldn't unlock pty slave %s: %m", pty_name);
+#endif
+ if ((sfd = open(pty_name, O_RDWR | O_NOCTTY)) < 0)
+ warn("Couldn't open pty slave %s: %m", pty_name);
+ }
+ }
+#endif /* TIOCGPTN */
+
+ if (sfd < 0) {
+ /* the old way - scan through the pty name space */
+ for (i = 0; i < 64; ++i) {
+ slprintf(pty_name, sizeof(pty_name), "/dev/pty%c%x",
+ 'p' + i / 16, i % 16);
+ mfd = open(pty_name, O_RDWR, 0);
+ if (mfd >= 0) {
+ pty_name[5] = 't';
+ sfd = open(pty_name, O_RDWR | O_NOCTTY, 0);
+ if (sfd >= 0) {
+ fchown(sfd, uid, -1);
+ fchmod(sfd, S_IRUSR | S_IWUSR);
+ break;
+ }
+ close(mfd);
+ }
+ }
+ }
+
+ if (sfd < 0)
+ return 0;
+
+ strlcpy(slave_name, pty_name, 16);
+ *master_fdp = mfd;
+ *slave_fdp = sfd;
+ if (tcgetattr(sfd, &tios) == 0) {
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+ tios.c_cflag |= CS8 | CREAD | CLOCAL;
+ tios.c_iflag = IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0)
+ warn("couldn't set attributes on pty: %m");
+ } else
+ warn("couldn't get attributes on pty: %m");
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * open_loopback - open the device we use for getting packets
+ * in demand mode. Under Linux, we use a pty master/slave pair.
+ */
+int
+open_ppp_loopback(void)
+{
+ int flags;
+
+ looped = 1;
+ if (new_style_driver) {
+ /* allocate ourselves a ppp unit */
+ if (make_ppp_unit() < 0)
+ die(1);
+ set_flags(ppp_dev_fd, SC_LOOP_TRAFFIC);
+ set_kdebugflag(kdebugflag);
+ ppp_fd = -1;
+ return ppp_dev_fd;
+ }
+
+ if (!get_pty(&master_fd, &slave_fd, loop_name, 0))
+ fatal("No free pty for loopback");
+ SYSDEBUG(("using %s for loopback", loop_name));
+
+ set_ppp_fd(slave_fd);
+
+ flags = fcntl(master_fd, F_GETFL);
+ if (flags == -1 ||
+ fcntl(master_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set master loopback to nonblock: %m(%d)", errno);
+
+ flags = fcntl(ppp_fd, F_GETFL);
+ if (flags == -1 ||
+ fcntl(ppp_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set slave loopback to nonblock: %m(%d)", errno);
+
+ if (ioctl(ppp_fd, TIOCSETD, &ppp_disc) < 0)
+ fatal("ioctl(TIOCSETD): %m(%d)", errno);
+/*
+ * Find out which interface we were given.
+ */
+ if (ioctl(ppp_fd, PPPIOCGUNIT, &ifunit) < 0)
+ fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+/*
+ * Enable debug in the driver if requested.
+ */
+ set_kdebugflag (kdebugflag);
+
+ return master_fd;
+}
+
+/********************************************************************
+ *
+ * restore_loop - reattach the ppp unit to the loopback.
+ *
+ * The kernel ppp driver automatically reattaches the ppp unit to
+ * the loopback if the serial port is set to a line discipline other
+ * than ppp, or if it detects a modem hangup. The former will happen
+ * in disestablish_ppp if the latter hasn't already happened, so we
+ * shouldn't need to do anything.
+ *
+ * Just to be sure, set the real serial port to the normal discipline.
+ */
+
+static void
+restore_loop(void)
+{
+ looped = 1;
+ if (new_style_driver) {
+ set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_LOOP_TRAFFIC);
+ return;
+ }
+ if (ppp_fd != slave_fd) {
+ (void) ioctl(ppp_fd, TIOCSETD, &tty_disc);
+ set_ppp_fd(slave_fd);
+ }
+}
+
+/********************************************************************
+ *
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+
+int
+sifnpmode(u, proto, mode)
+ int u;
+ int proto;
+ enum NPmode mode;
+{
+ struct npioctl npi;
+
+ npi.protocol = proto;
+ npi.mode = mode;
+ if (ioctl(ppp_dev_fd, PPPIOCSNPMODE, (caddr_t) &npi) < 0) {
+ if (! ok_error (errno))
+ error("ioctl(PPPIOCSNPMODE, %d, %d): %m (%d)",
+ proto, mode, errno);
+ return 0;
+ }
+ return 1;
+}
+
+
+/********************************************************************
+ *
+ * sipxfaddr - Config the interface IPX networknumber
+ */
+
+int sipxfaddr (int unit, unsigned long int network, unsigned char * node )
+{
+ int result = 1;
+
+#ifdef IPX_CHANGE
+ int skfd;
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+
+ skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+ if (skfd < 0) {
+ if (! ok_error (errno))
+ dbglog("socket(AF_IPX): %m (%d)", errno);
+ result = 0;
+ }
+ else {
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ memcpy (sipx->sipx_node, node, IPX_NODE_LEN);
+ sipx->sipx_family = AF_IPX;
+ sipx->sipx_port = 0;
+ sipx->sipx_network = htonl (network);
+ sipx->sipx_type = IPX_FRAME_ETHERII;
+ sipx->sipx_action = IPX_CRTITF;
+/*
+ * Set the IPX device
+ */
+ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ result = 0;
+ if (errno != EEXIST) {
+ if (! ok_error (errno))
+ dbglog("ioctl(SIOCSIFADDR, CRTITF): %m (%d)", errno);
+ }
+ else {
+ warn("ioctl(SIOCSIFADDR, CRTITF): Address already exists");
+ }
+ }
+ close (skfd);
+ }
+#endif
+ return result;
+}
+
+/********************************************************************
+ *
+ * cipxfaddr - Clear the information for the IPX network. The IPX routes
+ * are removed and the device is no longer able to pass IPX
+ * frames.
+ */
+
+int cipxfaddr (int unit)
+{
+ int result = 1;
+
+#ifdef IPX_CHANGE
+ int skfd;
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+
+ skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+ if (skfd < 0) {
+ if (! ok_error (errno))
+ dbglog("socket(AF_IPX): %m (%d)", errno);
+ result = 0;
+ }
+ else {
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ sipx->sipx_type = IPX_FRAME_ETHERII;
+ sipx->sipx_action = IPX_DLTITF;
+ sipx->sipx_family = AF_IPX;
+/*
+ * Set the IPX device
+ */
+ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+ if (! ok_error (errno))
+ info("ioctl(SIOCSIFADDR, IPX_DLTITF): %m (%d)", errno);
+ result = 0;
+ }
+ close (skfd);
+ }
+#endif
+ return result;
+}
+
+/*
+ * Use the hostname as part of the random number seed.
+ */
+int
+get_host_seed()
+{
+ int h;
+ char *p = hostname;
+
+ h = 407;
+ for (p = hostname; *p != 0; ++p)
+ h = h * 37 + *p;
+ return h;
+}
+
+/********************************************************************
+ *
+ * sys_check_options - check the options that the user specified
+ */
+
+int
+sys_check_options(void)
+{
+#ifdef IPX_CHANGE
+/*
+ * Disable the IPX protocol if the support is not present in the kernel.
+ */
+ char *path;
+
+ if (ipxcp_protent.enabled_flag) {
+ struct stat stat_buf;
+ if ((path = path_to_procfs("/net/ipx_interface")) == 0
+ || lstat(path, &stat_buf) < 0) {
+ error("IPX support is not present in the kernel\n");
+ ipxcp_protent.enabled_flag = 0;
+ }
+ }
+#endif
+ if (demand && driver_is_old) {
+ option_error("demand dialling is not supported by kernel driver "
+ "version %d.%d.%d", driver_version, driver_modification,
+ driver_patch);
+ return 0;
+ }
+ if (multilink && !new_style_driver) {
+ warn("Warning: multilink is not supported by the kernel driver");
+ multilink = 0;
+ }
+ return 1;
+}
diff --git a/mdk-stage1/ppp/pppd/sys-solaris.c b/mdk-stage1/ppp/pppd/sys-solaris.c
new file mode 100644
index 000000000..da5f9c45a
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/sys-solaris.c
@@ -0,0 +1,2737 @@
+/*
+ * System-dependent procedures for pppd under Solaris 2.
+ *
+ * Parts re-written by Adi Masputra <adi.masputra@sun.com>, based on
+ * the original sys-svr4.c
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#define RCSID "$Id$"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#ifndef CRTSCTS
+#include <sys/termiox.h>
+#endif
+#include <signal.h>
+#include <utmpx.h>
+#include <sys/types.h>
+#include <sys/ioccom.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysmacros.h>
+#include <sys/systeminfo.h>
+#include <sys/dlpi.h>
+#include <sys/stat.h>
+#include <sys/mkdev.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/route.h>
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include <netinet/in.h>
+#ifdef SOL2
+#include <sys/tihdr.h>
+#include <sys/tiuser.h>
+#include <inet/common.h>
+#include <inet/mib2.h>
+#include <sys/ethernet.h>
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "ccp.h"
+
+#if !defined(PPP_DRV_NAME)
+#define PPP_DRV_NAME "ppp"
+#endif /* !defined(PPP_DRV_NAME) */
+
+#if !defined(PPP_DEV_NAME)
+#define PPP_DEV_NAME "/dev/" PPP_DRV_NAME
+#endif /* !defined(PPP_DEV_NAME) */
+
+#if !defined(AHDLC_MOD_NAME)
+#define AHDLC_MOD_NAME "ppp_ahdl"
+#endif /* !defined(AHDLC_MOD_NAME) */
+
+#if !defined(COMP_MOD_NAME)
+#define COMP_MOD_NAME "ppp_comp"
+#endif /* !defined(COMP_MOD_NAME) */
+
+#if !defined(IP_DEV_NAME)
+#define IP_DEV_NAME "/dev/ip"
+#endif /* !defined(IP_DEV_NAME) */
+
+#if !defined(IP_MOD_NAME)
+#define IP_MOD_NAME "ip"
+#endif /* !defined(IP_MOD_NAME) */
+
+#if !defined(UDP_DEV_NAME) && defined(SOL2)
+#define UDP_DEV_NAME "/dev/udp"
+#endif /* !defined(UDP_DEV_NAME) && defined(SOL2) */
+
+#if !defined(UDP6_DEV_NAME) && defined(SOL2)
+#define UDP6_DEV_NAME "/dev/udp6"
+#endif /* !defined(UDP6_DEV_NAME) && defined(SOL2) */
+
+static const char rcsid[] = RCSID;
+
+#if defined(SOL2)
+/*
+ * "/dev/udp" is used as a multiplexor to PLINK the interface stream
+ * under. It is used in place of "/dev/ip" since STREAMS will not let
+ * a driver be PLINK'ed under itself, and "/dev/ip" is typically the
+ * driver at the bottom of the tunneling interfaces stream.
+ */
+static char *mux_dev_name = UDP_DEV_NAME;
+#else
+static char *mux_dev_name = IP_DEV_NAME;
+#endif
+static int pppfd;
+static int fdmuxid = -1;
+static int ipfd;
+static int ipmuxid = -1;
+
+#if defined(INET6) && defined(SOL2)
+static int ip6fd; /* IP file descriptor */
+static int ip6muxid = -1; /* Multiplexer file descriptor */
+static int if6_is_up = 0; /* IPv6 interface has been marked up */
+
+#define _IN6_LLX_FROM_EUI64(l, s, eui64, as) do { \
+ s->sin6_addr.s6_addr32[0] = htonl(as); \
+ eui64_copy(eui64, s->sin6_addr.s6_addr32[2]); \
+ s->sin6_family = AF_INET6; \
+ l.lifr_addr.ss_family = AF_INET6; \
+ l.lifr_addrlen = 10; \
+ l.lifr_addr = laddr; \
+ } while (0)
+
+#define IN6_LLADDR_FROM_EUI64(l, s, eui64) \
+ _IN6_LLX_FROM_EUI64(l, s, eui64, 0xfe800000)
+
+#define IN6_LLTOKEN_FROM_EUI64(l, s, eui64) \
+ _IN6_LLX_FROM_EUI64(l, s, eui64, 0)
+
+#endif /* defined(INET6) && defined(SOL2) */
+
+#if defined(INET6) && defined(SOL2)
+static char first_ether_name[LIFNAMSIZ]; /* Solaris 8 and above */
+#else
+static char first_ether_name[IFNAMSIZ]; /* Before Solaris 8 */
+#define MAXIFS 256 /* Max # of interfaces */
+#endif /* defined(INET6) && defined(SOL2) */
+
+static int restore_term;
+static struct termios inittermios;
+#ifndef CRTSCTS
+static struct termiox inittermiox;
+static int termiox_ok;
+#endif
+static struct winsize wsinfo; /* Initial window size info */
+static pid_t tty_sid; /* original session ID for terminal */
+
+extern u_char inpacket_buf[]; /* borrowed from main.c */
+
+#define MAX_POLLFDS 32
+static struct pollfd pollfds[MAX_POLLFDS];
+static int n_pollfds;
+
+static int link_mtu, link_mru;
+
+#define NMODULES 32
+static int tty_nmodules;
+static char tty_modules[NMODULES][FMNAMESZ+1];
+static int tty_npushed;
+
+static int if_is_up; /* Interface has been marked up */
+static u_int32_t remote_addr; /* IP address of peer */
+static u_int32_t default_route_gateway; /* Gateway for default route added */
+static u_int32_t proxy_arp_addr; /* Addr for proxy arp entry added */
+
+/* Prototypes for procedures local to this file. */
+static int translate_speed __P((int));
+static int baud_rate_of __P((int));
+static int get_ether_addr __P((u_int32_t, struct sockaddr *));
+static int get_hw_addr __P((char *, u_int32_t, struct sockaddr *));
+static int get_hw_addr_dlpi __P((char *, struct sockaddr *));
+static int dlpi_attach __P((int, int));
+static int dlpi_info_req __P((int));
+static int dlpi_get_reply __P((int, union DL_primitives *, int, int));
+static int strioctl __P((int, int, void *, int, int));
+
+#ifdef SOL2
+/*
+ * sifppa - Sets interface ppa
+ *
+ * without setting the ppa, ip module will return EINVAL upon setting the
+ * interface UP (SIOCSxIFFLAGS). This is because ip module in 2.8 expects
+ * two DLPI_INFO_REQ to be sent down to the driver (below ip) before
+ * IFF_UP can be set. Plumbing the device causes one DLPI_INFO_REQ to
+ * be sent down, and the second DLPI_INFO_REQ is sent upon receiving
+ * IF_UNITSEL (old) or SIOCSLIFNAME (new) ioctls. Such setting of the ppa
+ * is required because the ppp DLPI provider advertises itself as
+ * a DLPI style 2 type, which requires a point of attachment to be
+ * specified. The only way the user can specify a point of attachment
+ * is via SIOCSLIFNAME or IF_UNITSEL.
+ *
+ * Such changes in the behavior of ip module was made to meet new or
+ * evolving standards requirements.
+ *
+ */
+static int
+sifppa(fd, ppa)
+ int fd;
+ int ppa;
+{
+ return (int)ioctl(fd, IF_UNITSEL, (char *)&ppa);
+}
+#endif /* SOL2 */
+
+#if defined(SOL2) && defined(INET6)
+/*
+ * get_first_ethernet - returns the first Ethernet interface name found in
+ * the system, or NULL if none is found
+ *
+ * NOTE: This is the lifreq version (Solaris 8 and above)
+ */
+char *
+get_first_ethernet()
+{
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq *plifreq;
+ struct lifreq lifr;
+ int fd, num_ifs, i, found;
+ uint_t fl, req_size;
+ char *req;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ return 0;
+ }
+
+ /*
+ * Find out how many interfaces are running
+ */
+ lifn.lifn_family = AF_UNSPEC;
+ lifn.lifn_flags = LIFC_NOXMIT;
+ if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0) {
+ close(fd);
+ error("could not determine number of interfaces: %m");
+ return 0;
+ }
+
+ num_ifs = lifn.lifn_count;
+ req_size = num_ifs * sizeof(struct lifreq);
+ req = malloc(req_size);
+ if (req == NULL) {
+ close(fd);
+ error("out of memory");
+ return 0;
+ }
+
+ /*
+ * Get interface configuration info for all interfaces
+ */
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_flags = LIFC_NOXMIT;
+ lifc.lifc_len = req_size;
+ lifc.lifc_buf = req;
+ if (ioctl(fd, SIOCGLIFCONF, &lifc) < 0) {
+ close(fd);
+ free(req);
+ error("SIOCGLIFCONF: %m");
+ return 0;
+ }
+
+ /*
+ * And traverse each interface to look specifically for the first
+ * occurence of an Ethernet interface which has been marked up
+ */
+ plifreq = lifc.lifc_req;
+ found = 0;
+ for (i = lifc.lifc_len / sizeof(struct lifreq); i > 0; i--, plifreq++) {
+
+ if (strchr(plifreq->lifr_name, ':') != NULL)
+ continue;
+
+ memset(&lifr, 0, sizeof(lifr));
+ strncpy(lifr.lifr_name, plifreq->lifr_name, sizeof(lifr.lifr_name));
+ if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+ close(fd);
+ free(req);
+ error("SIOCGLIFFLAGS: %m");
+ return 0;
+ }
+ fl = lifr.lifr_flags;
+
+ if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+ != (IFF_UP | IFF_BROADCAST))
+ continue;
+
+ found = 1;
+ break;
+ }
+ free(req);
+ close(fd);
+
+ if (found) {
+ strncpy(first_ether_name, lifr.lifr_name, sizeof(first_ether_name));
+ return (char *)first_ether_name;
+ } else
+ return NULL;
+}
+#else
+/*
+ * get_first_ethernet - returns the first Ethernet interface name found in
+ * the system, or NULL if none is found
+ *
+ * NOTE: This is the ifreq version (before Solaris 8).
+ */
+char *
+get_first_ethernet()
+{
+ struct ifconf ifc;
+ struct ifreq *pifreq;
+ struct ifreq ifr;
+ int fd, num_ifs, i, found;
+ uint_t fl, req_size;
+ char *req;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ return 0;
+ }
+
+ /*
+ * Find out how many interfaces are running
+ */
+ if (ioctl(fd, SIOCGIFNUM, (char *)&num_ifs) < 0) {
+ num_ifs = MAXIFS;
+ }
+
+ req_size = num_ifs * sizeof(struct ifreq);
+ req = malloc(req_size);
+ if (req == NULL) {
+ close(fd);
+ error("out of memory");
+ return 0;
+ }
+
+ /*
+ * Get interface configuration info for all interfaces
+ */
+ ifc.ifc_len = req_size;
+ ifc.ifc_buf = req;
+ if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+ close(fd);
+ free(req);
+ error("SIOCGIFCONF: %m");
+ return 0;
+ }
+
+ /*
+ * And traverse each interface to look specifically for the first
+ * occurence of an Ethernet interface which has been marked up
+ */
+ pifreq = ifc.ifc_req;
+ found = 0;
+ for (i = ifc.ifc_len / sizeof(struct ifreq); i > 0; i--, pifreq++) {
+
+ if (strchr(pifreq->ifr_name, ':') != NULL)
+ continue;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, pifreq->ifr_name, sizeof(ifr.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ close(fd);
+ free(req);
+ error("SIOCGIFFLAGS: %m");
+ return 0;
+ }
+ fl = ifr.ifr_flags;
+
+ if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+ != (IFF_UP | IFF_BROADCAST))
+ continue;
+
+ found = 1;
+ break;
+ }
+ free(req);
+ close(fd);
+
+ if (found) {
+ strncpy(first_ether_name, ifr.ifr_name, sizeof(first_ether_name));
+ return (char *)first_ether_name;
+ } else
+ return NULL;
+}
+#endif /* defined(SOL2) && defined(INET6) */
+
+#if defined(SOL2)
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *if_name)
+{
+ struct sockaddr s_eth_addr;
+ struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data;
+
+ if (if_name == NULL)
+ return -1;
+
+ /*
+ * Send DL_INFO_REQ to the driver to solicit its MAC address
+ */
+ if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) {
+ error("could not obtain hardware address for %s", if_name);
+ return -1;
+ }
+
+ memcpy(addr, eth_addr->ether_addr_octet, 6);
+ return 1;
+}
+#endif /* SOL2 */
+
+#if defined(SOL2) && defined(INET6)
+/*
+ * slifname - Sets interface ppa and flags
+ *
+ * in addition to the comments stated in sifppa(), IFF_IPV6 bit must
+ * be set in order to declare this as an IPv6 interface
+ */
+static int
+slifname(fd, ppa)
+ int fd;
+ int ppa;
+{
+ struct lifreq lifr;
+ int ret;
+
+ memset(&lifr, 0, sizeof(lifr));
+ ret = ioctl(fd, SIOCGLIFFLAGS, &lifr);
+ if (ret < 0)
+ goto slifname_done;
+
+ lifr.lifr_flags |= IFF_IPV6;
+ lifr.lifr_flags &= ~(IFF_BROADCAST | IFF_IPV4);
+ lifr.lifr_ppa = ppa;
+ strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+
+ ret = ioctl(fd, SIOCSLIFNAME, &lifr);
+
+slifname_done:
+ return ret;
+
+
+}
+
+
+/*
+ * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI
+ *
+ * walks the list of valid ethernet interfaces, and convert the first
+ * found 48-bit MAC address into EUI 64. caller also assumes that
+ * the system has a properly configured Ethernet interface for this
+ * function to return non-zero.
+ */
+int
+ether_to_eui64(eui64_t *p_eui64)
+{
+ struct sockaddr s_eth_addr;
+ struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data;
+ char *if_name;
+
+ if ((if_name = get_first_ethernet()) == NULL) {
+ error("no persistent id can be found");
+ return 0;
+ }
+
+ /*
+ * Send DL_INFO_REQ to the driver to solicit its MAC address
+ */
+ if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) {
+ error("could not obtain hardware address for %s", if_name);
+ return 0;
+ }
+
+ /*
+ * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
+ */
+ p_eui64->e8[0] = (eth_addr->ether_addr_octet[0] & 0xFF) | 0x02;
+ p_eui64->e8[1] = (eth_addr->ether_addr_octet[1] & 0xFF);
+ p_eui64->e8[2] = (eth_addr->ether_addr_octet[2] & 0xFF);
+ p_eui64->e8[3] = 0xFF;
+ p_eui64->e8[4] = 0xFE;
+ p_eui64->e8[5] = (eth_addr->ether_addr_octet[3] & 0xFF);
+ p_eui64->e8[6] = (eth_addr->ether_addr_octet[4] & 0xFF);
+ p_eui64->e8[7] = (eth_addr->ether_addr_octet[5] & 0xFF);
+
+ return 1;
+}
+#endif /* defined(SOL2) && defined(INET6) */
+
+/*
+ * sys_init - System-dependent initialization.
+ */
+void
+sys_init()
+{
+ int ifd, x;
+ struct ifreq ifr;
+#if defined(INET6) && defined(SOL2)
+ int i6fd;
+ struct lifreq lifr;
+#endif /* defined(INET6) && defined(SOL2) */
+#if !defined(SOL2)
+ struct {
+ union DL_primitives prim;
+ char space[64];
+ } reply;
+#endif /* !defined(SOL2) */
+
+ ipfd = open(mux_dev_name, O_RDWR, 0);
+ if (ipfd < 0)
+ fatal("Couldn't open IP device: %m");
+
+#if defined(INET6) && defined(SOL2)
+ ip6fd = open(UDP6_DEV_NAME, O_RDWR, 0);
+ if (ip6fd < 0)
+ fatal("Couldn't open IP device (2): %m");
+#endif /* defined(INET6) && defined(SOL2) */
+
+ if (default_device && !notty)
+ tty_sid = getsid((pid_t)0);
+
+ pppfd = open(PPP_DEV_NAME, O_RDWR | O_NONBLOCK, 0);
+ if (pppfd < 0)
+ fatal("Can't open %s: %m", PPP_DEV_NAME);
+ if (kdebugflag & 1) {
+ x = PPPDBG_LOG + PPPDBG_DRIVER;
+ strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0);
+ }
+
+ /* Assign a new PPA and get its unit number. */
+ if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0)
+ fatal("Can't create new PPP interface: %m");
+
+#if defined(SOL2)
+ /*
+ * Since sys_init() is called prior to ifname being set in main(),
+ * we need to get the ifname now, otherwise slifname(), and others,
+ * will fail, or maybe, I should move them to a later point ?
+ * <adi.masputra@sun.com>
+ */
+ sprintf(ifname, PPP_DRV_NAME "%d", ifunit);
+#endif /* defined(SOL2) */
+ /*
+ * Open the ppp device again and link it under the ip multiplexor.
+ * IP will assign a unit number which hopefully is the same as ifunit.
+ * I don't know any way to be certain they will be the same. :-(
+ */
+ ifd = open(PPP_DEV_NAME, O_RDWR, 0);
+ if (ifd < 0)
+ fatal("Can't open %s (2): %m", PPP_DEV_NAME);
+ if (kdebugflag & 1) {
+ x = PPPDBG_LOG + PPPDBG_DRIVER;
+ strioctl(ifd, PPPIO_DEBUG, &x, sizeof(int), 0);
+ }
+
+#if defined(INET6) && defined(SOL2)
+ i6fd = open(PPP_DEV_NAME, O_RDWR, 0);
+ if (i6fd < 0) {
+ close(ifd);
+ fatal("Can't open %s (3): %m", PPP_DEV_NAME);
+ }
+ if (kdebugflag & 1) {
+ x = PPPDBG_LOG + PPPDBG_DRIVER;
+ strioctl(i6fd, PPPIO_DEBUG, &x, sizeof(int), 0);
+ }
+#endif /* defined(INET6) && defined(SOL2) */
+
+#if defined(SOL2)
+ if (ioctl(ifd, I_PUSH, IP_MOD_NAME) < 0) {
+ close(ifd);
+#if defined(INET6)
+ close(i6fd);
+#endif /* defined(INET6) */
+ fatal("Can't push IP module: %m");
+ }
+
+ /*
+ * Assign ppa according to the unit number returned by ppp device
+ * after plumbing is completed above.
+ */
+ if (sifppa(ifd, ifunit) < 0) {
+ close (ifd);
+#if defined(INET6)
+ close(i6fd);
+#endif /* defined(INET6) */
+ fatal("Can't set ppa for unit %d: %m", ifunit);
+ }
+
+#if defined(INET6)
+ /*
+ * An IPv6 interface is created anyway, even when the user does not
+ * explicitly enable it. Note that the interface will be marked
+ * IPv6 during slifname().
+ */
+ if (ioctl(i6fd, I_PUSH, IP_MOD_NAME) < 0) {
+ close(ifd);
+ close(i6fd);
+ fatal("Can't push IP module (2): %m");
+ }
+
+ /*
+ * Assign ppa according to the unit number returned by ppp device
+ * after plumbing is completed above. In addition, mark the interface
+ * as an IPv6 interface.
+ */
+ if (slifname(i6fd, ifunit) < 0) {
+ close(ifd);
+ close(i6fd);
+ fatal("Can't set ifname for unit %d: %m", ifunit);
+ }
+#endif /* defined(INET6) */
+
+ ipmuxid = ioctl(ipfd, I_PLINK, ifd);
+ close(ifd);
+ if (ipmuxid < 0) {
+#if defined(INET6)
+ close(i6fd);
+#endif /* defined(INET6) */
+ fatal("Can't I_PLINK PPP device to IP: %m");
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ sprintf(ifr.ifr_name, "%s", ifname);
+ ifr.ifr_ip_muxid = ipmuxid;
+
+ /*
+ * In Sol 8 and later, STREAMS dynamic module plumbing feature exists.
+ * This is so that an arbitrary module can be inserted, or deleted,
+ * between ip module and the device driver without tearing down the
+ * existing stream. Such feature requires the mux ids, which is set
+ * by SIOCSIFMUXID (or SIOCLSIFMUXID).
+ */
+ if (ioctl(ipfd, SIOCSIFMUXID, &ifr) < 0) {
+ ioctl(ipfd, I_PUNLINK, ipmuxid);
+#if defined(INET6)
+ close(i6fd);
+#endif /* defined(INET6) */
+ fatal("SIOCSIFMUXID: %m");
+ }
+
+#else /* else if !defined(SOL2) */
+
+ if (dlpi_attach(ifd, ifunit) < 0 ||
+ dlpi_get_reply(ifd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0) {
+ close(ifd);
+ fatal("Can't attach to ppp%d: %m", ifunit);
+ }
+
+ ipmuxid = ioctl(ipfd, I_LINK, ifd);
+ close(ifd);
+ if (ipmuxid < 0)
+ fatal("Can't link PPP device to IP: %m");
+#endif /* defined(SOL2) */
+
+#if defined(INET6) && defined(SOL2)
+ ip6muxid = ioctl(ip6fd, I_PLINK, i6fd);
+ close(i6fd);
+ if (ip6muxid < 0) {
+ ioctl(ipfd, I_PUNLINK, ipmuxid);
+ fatal("Can't I_PLINK PPP device to IP (2): %m");
+ }
+
+ memset(&lifr, 0, sizeof(lifr));
+ sprintf(lifr.lifr_name, "%s", ifname);
+ lifr.lifr_ip_muxid = ip6muxid;
+
+ /*
+ * Let IP know of the mux id [see comment for SIOCSIFMUXID above]
+ */
+ if (ioctl(ip6fd, SIOCSLIFMUXID, &lifr) < 0) {
+ ioctl(ipfd, I_PUNLINK, ipmuxid);
+ ioctl(ip6fd, I_PUNLINK, ip6muxid);
+ fatal("Can't link PPP device to IP (2): %m");
+ }
+#endif /* defined(INET6) && defined(SOL2) */
+
+#if !defined(SOL2)
+ /* Set the interface name for the link. */
+ slprintf(ifr.ifr_name, sizeof(ifr.ifr_name), PPP_DRV_NAME "%d", ifunit);
+ ifr.ifr_metric = ipmuxid;
+ if (strioctl(ipfd, SIOCSIFNAME, (char *)&ifr, sizeof ifr, 0) < 0)
+ fatal("Can't set interface name %s: %m", ifr.ifr_name);
+#endif /* !defined(SOL2) */
+
+ n_pollfds = 0;
+}
+
+/*
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This should call die() because it's called from die().
+ */
+void
+sys_cleanup()
+{
+#if defined(SOL2)
+ struct ifreq ifr;
+#if defined(INET6)
+ struct lifreq lifr;
+#endif /* defined(INET6) */
+#endif /* defined(SOL2) */
+
+#if defined(SOL2) && defined(INET6)
+ if (if6_is_up)
+ sif6down(0);
+#endif /* defined(SOL2) && defined(INET6) */
+ if (if_is_up)
+ sifdown(0);
+ if (default_route_gateway)
+ cifdefaultroute(0, default_route_gateway, default_route_gateway);
+ if (proxy_arp_addr)
+ cifproxyarp(0, proxy_arp_addr);
+#if defined(SOL2)
+ /*
+ * Make sure we ask ip what the muxid, because 'ifconfig modlist' will
+ * unlink and re-link the modules, causing the muxid to change.
+ */
+ memset(&ifr, 0, sizeof(ifr));
+ sprintf(ifr.ifr_name, "%s", ifname);
+ if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+ error("SIOCGIFFLAGS: %m");
+ return;
+ }
+
+ if (ioctl(ipfd, SIOCGIFMUXID, &ifr) < 0) {
+ error("SIOCGIFMUXID: %m");
+ return;
+ }
+
+ ipmuxid = ifr.ifr_ip_muxid;
+
+ if (ioctl(ipfd, I_PUNLINK, ipmuxid) < 0) {
+ error("Can't I_PUNLINK PPP from IP: %m");
+ return;
+ }
+#if defined(INET6)
+ /*
+ * Make sure we ask ip what the muxid, because 'ifconfig modlist' will
+ * unlink and re-link the modules, causing the muxid to change.
+ */
+ memset(&lifr, 0, sizeof(lifr));
+ sprintf(lifr.lifr_name, "%s", ifname);
+ if (ioctl(ip6fd, SIOCGLIFFLAGS, &lifr) < 0) {
+ error("SIOCGLIFFLAGS: %m");
+ return;
+ }
+
+ if (ioctl(ip6fd, SIOCGLIFMUXID, &lifr) < 0) {
+ error("SIOCGLIFMUXID: %m");
+ return;
+ }
+
+ ip6muxid = lifr.lifr_ip_muxid;
+
+ if (ioctl(ip6fd, I_PUNLINK, ip6muxid) < 0) {
+ error("Can't I_PUNLINK PPP from IP (2): %m");
+ }
+#endif /* defined(INET6) */
+#endif /* defined(SOL2) */
+}
+
+/*
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close()
+{
+ close(ipfd);
+#if defined(INET6) && defined(SOL2)
+ close(ip6fd);
+#endif /* defined(INET6) && defined(SOL2) */
+ if (pppfd >= 0)
+ close(pppfd);
+}
+
+/*
+ * sys_check_options - check the options that the user specified
+ */
+int
+sys_check_options()
+{
+ return 1;
+}
+
+#if 0
+/*
+ * daemon - Detach us from controlling terminal session.
+ */
+int
+daemon(nochdir, noclose)
+ int nochdir, noclose;
+{
+ int pid;
+
+ if ((pid = fork()) < 0)
+ return -1;
+ if (pid != 0)
+ exit(0); /* parent dies */
+ setsid();
+ if (!nochdir)
+ chdir("/");
+ if (!noclose) {
+ fclose(stdin); /* don't need stdin, stdout, stderr */
+ fclose(stdout);
+ fclose(stderr);
+ }
+ return 0;
+}
+#endif
+
+/*
+ * ppp_available - check whether the system has any ppp interfaces
+ */
+int
+ppp_available()
+{
+ struct stat buf;
+
+ return stat(PPP_DEV_NAME, &buf) >= 0;
+}
+
+/*
+ * any_compressions - see if compression is enabled or not
+ *
+ * In the STREAMS implementation of kernel-portion pppd,
+ * the comp STREAMS module performs the ACFC, PFC, as well
+ * CCP and VJ compressions. However, if the user has explicitly
+ * declare to not enable them from the command line, there is
+ * no point of having the comp module be pushed on the stream.
+ */
+static int
+any_compressions()
+{
+ if ((!lcp_wantoptions[0].neg_accompression) &&
+ (!lcp_wantoptions[0].neg_pcompression) &&
+ (!ccp_protent.enabled_flag) &&
+ (!ipcp_wantoptions[0].neg_vj)) {
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * tty_establish_ppp - Turn the serial port into a ppp interface.
+ */
+int
+tty_establish_ppp(fd)
+ int fd;
+{
+ int i;
+
+ /* Pop any existing modules off the tty stream. */
+ for (i = 0;; ++i)
+ if (ioctl(fd, I_LOOK, tty_modules[i]) < 0
+ || strcmp(tty_modules[i], "ptem") == 0
+ || ioctl(fd, I_POP, 0) < 0)
+ break;
+ tty_nmodules = i;
+
+ /* Push the async hdlc module and the compressor module. */
+ tty_npushed = 0;
+
+ if(!sync_serial) {
+ if (ioctl(fd, I_PUSH, AHDLC_MOD_NAME) < 0) {
+ error("Couldn't push PPP Async HDLC module: %m");
+ return -1;
+ }
+ ++tty_npushed;
+ }
+ if (kdebugflag & 4) {
+ i = PPPDBG_LOG + PPPDBG_AHDLC;
+ strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0);
+ }
+ /*
+ * There's no need to push comp module if we don't intend
+ * to compress anything
+ */
+ if (any_compressions()) {
+ if (ioctl(fd, I_PUSH, COMP_MOD_NAME) < 0)
+ error("Couldn't push PPP compression module: %m");
+ else
+ ++tty_npushed;
+ }
+
+ if (kdebugflag & 2) {
+ i = PPPDBG_LOG;
+ if (any_compressions())
+ i += PPPDBG_COMP;
+ strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0);
+ }
+
+ /* Link the serial port under the PPP multiplexor. */
+ if ((fdmuxid = ioctl(pppfd, I_LINK, fd)) < 0) {
+ error("Can't link tty to PPP mux: %m");
+ return -1;
+ }
+
+ return pppfd;
+}
+
+/*
+ * tty_disestablish_ppp - Restore the serial port to normal operation.
+ * It attempts to reconstruct the stream with the previously popped
+ * modules. This shouldn't call die() because it's called from die().
+ */
+void
+tty_disestablish_ppp(fd)
+ int fd;
+{
+ int i;
+
+ if (fdmuxid >= 0) {
+ if (ioctl(pppfd, I_UNLINK, fdmuxid) < 0) {
+ if (!hungup)
+ error("Can't unlink tty from PPP mux: %m");
+ }
+ fdmuxid = -1;
+
+ if (!hungup) {
+ while (tty_npushed > 0 && ioctl(fd, I_POP, 0) >= 0)
+ --tty_npushed;
+ for (i = tty_nmodules - 1; i >= 0; --i)
+ if (ioctl(fd, I_PUSH, tty_modules[i]) < 0)
+ error("Couldn't restore tty module %s: %m",
+ tty_modules[i]);
+ }
+ if (hungup && default_device && tty_sid > 0) {
+ /*
+ * If we have received a hangup, we need to send a SIGHUP
+ * to the terminal's controlling process. The reason is
+ * that the original stream head for the terminal hasn't
+ * seen the M_HANGUP message (it went up through the ppp
+ * driver to the stream head for our fd to /dev/ppp).
+ */
+ kill(tty_sid, SIGHUP);
+ }
+ }
+}
+
+/*
+ * Check whether the link seems not to be 8-bit clean.
+ */
+void
+clean_check()
+{
+ int x;
+ char *s;
+
+ if (strioctl(pppfd, PPPIO_GCLEAN, &x, 0, sizeof(x)) < 0)
+ return;
+ s = NULL;
+ switch (~x) {
+ case RCV_B7_0:
+ s = "bit 7 set to 1";
+ break;
+ case RCV_B7_1:
+ s = "bit 7 set to 0";
+ break;
+ case RCV_EVNP:
+ s = "odd parity";
+ break;
+ case RCV_ODDP:
+ s = "even parity";
+ break;
+ }
+ if (s != NULL) {
+ warn("Serial link is not 8-bit clean:");
+ warn("All received characters had %s", s);
+ }
+}
+
+/*
+ * List of valid speeds.
+ */
+struct speed {
+ int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+ { 50, B50 },
+#endif
+#ifdef B75
+ { 75, B75 },
+#endif
+#ifdef B110
+ { 110, B110 },
+#endif
+#ifdef B134
+ { 134, B134 },
+#endif
+#ifdef B150
+ { 150, B150 },
+#endif
+#ifdef B200
+ { 200, B200 },
+#endif
+#ifdef B300
+ { 300, B300 },
+#endif
+#ifdef B600
+ { 600, B600 },
+#endif
+#ifdef B1200
+ { 1200, B1200 },
+#endif
+#ifdef B1800
+ { 1800, B1800 },
+#endif
+#ifdef B2000
+ { 2000, B2000 },
+#endif
+#ifdef B2400
+ { 2400, B2400 },
+#endif
+#ifdef B3600
+ { 3600, B3600 },
+#endif
+#ifdef B4800
+ { 4800, B4800 },
+#endif
+#ifdef B7200
+ { 7200, B7200 },
+#endif
+#ifdef B9600
+ { 9600, B9600 },
+#endif
+#ifdef B19200
+ { 19200, B19200 },
+#endif
+#ifdef B38400
+ { 38400, B38400 },
+#endif
+#ifdef EXTA
+ { 19200, EXTA },
+#endif
+#ifdef EXTB
+ { 38400, EXTB },
+#endif
+#ifdef B57600
+ { 57600, B57600 },
+#endif
+#ifdef B76800
+ { 76800, B76800 },
+#endif
+#ifdef B115200
+ { 115200, B115200 },
+#endif
+#ifdef B153600
+ { 153600, B153600 },
+#endif
+#ifdef B230400
+ { 230400, B230400 },
+#endif
+#ifdef B307200
+ { 307200, B307200 },
+#endif
+#ifdef B460800
+ { 460800, B460800 },
+#endif
+ { 0, 0 }
+};
+
+/*
+ * Translate from bits/second to a speed_t.
+ */
+static int
+translate_speed(bps)
+ int bps;
+{
+ struct speed *speedp;
+
+ if (bps == 0)
+ return 0;
+ for (speedp = speeds; speedp->speed_int; speedp++)
+ if (bps == speedp->speed_int)
+ return speedp->speed_val;
+ warn("speed %d not supported", bps);
+ return 0;
+}
+
+/*
+ * Translate from a speed_t to bits/second.
+ */
+static int
+baud_rate_of(speed)
+ int speed;
+{
+ struct speed *speedp;
+
+ if (speed == 0)
+ return 0;
+ for (speedp = speeds; speedp->speed_int; speedp++)
+ if (speed == speedp->speed_val)
+ return speedp->speed_int;
+ return 0;
+}
+
+/*
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc. If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+void
+set_up_tty(fd, local)
+ int fd, local;
+{
+ int speed;
+ struct termios tios;
+#if !defined (CRTSCTS)
+ struct termiox tiox;
+#endif
+
+ if (!sync_serial && tcgetattr(fd, &tios) < 0)
+ fatal("tcgetattr: %m");
+
+#ifndef CRTSCTS
+ termiox_ok = 1;
+ if (!sync_serial && ioctl (fd, TCGETX, &tiox) < 0) {
+ termiox_ok = 0;
+ if (errno != ENOTTY)
+ error("TCGETX: %m");
+ }
+#endif
+
+ if (!restore_term) {
+ inittermios = tios;
+#ifndef CRTSCTS
+ inittermiox = tiox;
+#endif
+ if (!sync_serial)
+ ioctl(fd, TIOCGWINSZ, &wsinfo);
+ }
+
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+#ifdef CRTSCTS
+ if (crtscts > 0)
+ tios.c_cflag |= CRTSCTS;
+ else if (crtscts < 0)
+ tios.c_cflag &= ~CRTSCTS;
+#else
+ if (crtscts != 0 && !termiox_ok) {
+ error("Can't set RTS/CTS flow control");
+ } else if (crtscts > 0) {
+ tiox.x_hflag |= RTSXOFF|CTSXON;
+ } else if (crtscts < 0) {
+ tiox.x_hflag &= ~(RTSXOFF|CTSXON);
+ }
+#endif
+
+ tios.c_cflag |= CS8 | CREAD | HUPCL;
+ if (local || !modem)
+ tios.c_cflag |= CLOCAL;
+ tios.c_iflag = IGNBRK | IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+
+ if (crtscts == -2) {
+ tios.c_iflag |= IXON | IXOFF;
+ tios.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */
+ tios.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */
+ }
+
+ speed = translate_speed(inspeed);
+ if (speed) {
+ cfsetospeed(&tios, speed);
+ cfsetispeed(&tios, speed);
+ } else {
+ speed = cfgetospeed(&tios);
+ /*
+ * We can't proceed if the serial port speed is 0,
+ * since that implies that the serial port is disabled.
+ */
+ if ((speed == B0) && !sync_serial)
+ fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
+ }
+
+ if (!sync_serial && tcsetattr(fd, TCSAFLUSH, &tios) < 0)
+ fatal("tcsetattr: %m");
+
+#ifndef CRTSCTS
+ if (!sync_serial && termiox_ok && ioctl (fd, TCSETXF, &tiox) < 0){
+ error("TCSETXF: %m");
+ }
+#endif
+
+ baud_rate = inspeed = baud_rate_of(speed);
+ if (!sync_serial)
+ restore_term = 1;
+}
+
+/*
+ * restore_tty - restore the terminal to the saved settings.
+ */
+void
+restore_tty(fd)
+ int fd;
+{
+ if (restore_term) {
+ if (!default_device) {
+ /*
+ * Turn off echoing, because otherwise we can get into
+ * a loop with the tty and the modem echoing to each other.
+ * We presume we are the sole user of this tty device, so
+ * when we close it, it will revert to its defaults anyway.
+ */
+ inittermios.c_lflag &= ~(ECHO | ECHONL);
+ }
+ if (!sync_serial && tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
+ if (!hungup && errno != ENXIO)
+ warn("tcsetattr: %m");
+#ifndef CRTSCTS
+ if (!sync_serial && ioctl (fd, TCSETXF, &inittermiox) < 0){
+ if (!hungup && errno != ENXIO)
+ error("TCSETXF: %m");
+ }
+#endif
+ if (!sync_serial)
+ ioctl(fd, TIOCSWINSZ, &wsinfo);
+ restore_term = 0;
+ }
+}
+
+/*
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+void
+setdtr(fd, on)
+int fd, on;
+{
+ int modembits = TIOCM_DTR;
+
+ ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
+}
+
+/*
+ * open_loopback - open the device we use for getting packets
+ * in demand mode. Under Solaris 2, we use our existing fd
+ * to the ppp driver.
+ */
+int
+open_ppp_loopback()
+{
+ return pppfd;
+}
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ struct strbuf data;
+ int retries;
+ struct pollfd pfd;
+
+ if (debug)
+ dbglog("sent %P", p, len);
+
+ data.len = len;
+ data.buf = (caddr_t) p;
+ retries = 4;
+ while (putmsg(pppfd, NULL, &data, 0) < 0) {
+ if (--retries < 0 || (errno != EWOULDBLOCK && errno != EAGAIN)) {
+ if (errno != ENXIO)
+ error("Couldn't send packet: %m");
+ break;
+ }
+ pfd.fd = pppfd;
+ pfd.events = POLLOUT;
+ poll(&pfd, 1, 250); /* wait for up to 0.25 seconds */
+ }
+}
+
+
+/*
+ * wait_input - wait until there is data available,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_input(timo)
+ struct timeval *timo;
+{
+ int t;
+
+ t = timo == NULL? -1: timo->tv_sec * 1000 + timo->tv_usec / 1000;
+ if (poll(pollfds, n_pollfds, t) < 0 && errno != EINTR)
+ fatal("poll: %m");
+}
+
+/*
+ * add_fd - add an fd to the set that wait_input waits for.
+ */
+void add_fd(fd)
+ int fd;
+{
+ int n;
+
+ for (n = 0; n < n_pollfds; ++n)
+ if (pollfds[n].fd == fd)
+ return;
+ if (n_pollfds < MAX_POLLFDS) {
+ pollfds[n_pollfds].fd = fd;
+ pollfds[n_pollfds].events = POLLIN | POLLPRI | POLLHUP;
+ ++n_pollfds;
+ } else
+ error("Too many inputs!");
+}
+
+/*
+ * remove_fd - remove an fd from the set that wait_input waits for.
+ */
+void remove_fd(fd)
+ int fd;
+{
+ int n;
+
+ for (n = 0; n < n_pollfds; ++n) {
+ if (pollfds[n].fd == fd) {
+ while (++n < n_pollfds)
+ pollfds[n-1] = pollfds[n];
+ --n_pollfds;
+ break;
+ }
+ }
+}
+
+#if 0
+/*
+ * wait_loop_output - wait until there is data available on the
+ * loopback, for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_loop_output(timo)
+ struct timeval *timo;
+{
+ wait_input(timo);
+}
+
+/*
+ * wait_time - wait for a given length of time or until a
+ * signal is received.
+ */
+void
+wait_time(timo)
+ struct timeval *timo;
+{
+ int n;
+
+ n = select(0, NULL, NULL, NULL, timo);
+ if (n < 0 && errno != EINTR)
+ fatal("select: %m");
+}
+#endif
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+ u_char *buf;
+{
+ struct strbuf ctrl, data;
+ int flags, len;
+ unsigned char ctrlbuf[sizeof(union DL_primitives) + 64];
+
+ for (;;) {
+ data.maxlen = PPP_MRU + PPP_HDRLEN;
+ data.buf = (caddr_t) buf;
+ ctrl.maxlen = sizeof(ctrlbuf);
+ ctrl.buf = (caddr_t) ctrlbuf;
+ flags = 0;
+ len = getmsg(pppfd, &ctrl, &data, &flags);
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return -1;
+ fatal("Error reading packet: %m");
+ }
+
+ if (ctrl.len <= 0)
+ return data.len;
+
+ /*
+ * Got a M_PROTO or M_PCPROTO message. Interpret it
+ * as a DLPI primitive??
+ */
+ if (debug)
+ dbglog("got dlpi prim 0x%x, len=%d",
+ ((union DL_primitives *)ctrlbuf)->dl_primitive, ctrl.len);
+
+ }
+}
+
+/*
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output()
+{
+ int len;
+ int rv = 0;
+
+ while ((len = read_packet(inpacket_buf)) > 0) {
+ if (loop_frame(inpacket_buf, len))
+ rv = 1;
+ }
+ return rv;
+}
+
+/*
+ * netif_set_mtu - set the MTU on the PPP network interface.
+ */
+void
+netif_set_mtu(unit, mtu)
+ int unit, mtu;
+{
+ struct ifreq ifr;
+#if defined(INET6) && defined(SOL2)
+ struct lifreq lifr;
+ int fd;
+#endif /* defined(INET6) && defined(SOL2) */
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_metric = link_mtu;
+ if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) {
+ error("Couldn't set IP MTU (%s): %m", ifr.ifr_name);
+ }
+
+#if defined(INET6) && defined(SOL2)
+ fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd < 0)
+ error("Couldn't open IPv6 socket: %m");
+
+ memset(&lifr, 0, sizeof(lifr));
+ strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+ lifr.lifr_mtu = link_mtu;
+ if (ioctl(fd, SIOCSLIFMTU, &lifr) < 0) {
+ close(fd);
+ error("Couldn't set IPv6 MTU (%s): %m", ifr.ifr_name);
+ }
+ close(fd);
+#endif /* defined(INET6) && defined(SOL2) */
+}
+
+/*
+ * tty_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+tty_send_config(mtu, asyncmap, pcomp, accomp)
+ int mtu;
+ u_int32_t asyncmap;
+ int pcomp, accomp;
+{
+ int cf[2];
+
+ link_mtu = mtu;
+ if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) {
+ if (hungup && errno == ENXIO)
+ return;
+ error("Couldn't set MTU: %m");
+ }
+ if (fdmuxid >= 0) {
+ if (!sync_serial) {
+ if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+ error("Couldn't set transmit ACCM: %m");
+ }
+ }
+ cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
+ cf[1] = COMP_PROT | COMP_AC;
+ if (any_compressions() &&
+ strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ error("Couldn't set prot/AC compression: %m");
+ }
+ }
+}
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+tty_set_xaccm(accm)
+ ext_accm accm;
+{
+ if (sync_serial)
+ return;
+
+ if (fdmuxid >= 0
+ && strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) {
+ if (!hungup || errno != ENXIO)
+ warn("Couldn't set extended ACCM: %m");
+ }
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+tty_recv_config(mru, asyncmap, pcomp, accomp)
+ int mru;
+ u_int32_t asyncmap;
+ int pcomp, accomp;
+{
+ int cf[2];
+
+ link_mru = mru;
+ if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) {
+ if (hungup && errno == ENXIO)
+ return;
+ error("Couldn't set MRU: %m");
+ }
+ if (fdmuxid >= 0) {
+ if (!sync_serial) {
+ if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+ error("Couldn't set receive ACCM: %m");
+ }
+ }
+ cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0);
+ cf[1] = DECOMP_PROT | DECOMP_AC;
+ if (any_compressions() &&
+ strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ error("Couldn't set prot/AC decompression: %m");
+ }
+ }
+}
+
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+ int unit, opt_len, for_transmit;
+ u_char *opt_ptr;
+{
+ if (strioctl(pppfd, (for_transmit? PPPIO_XCOMP: PPPIO_RCOMP),
+ opt_ptr, opt_len, 0) >= 0)
+ return 1;
+ return (errno == ENOSR)? 0: -1;
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(unit, isopen, isup)
+ int unit, isopen, isup;
+{
+ int cf[2];
+
+ cf[0] = (isopen? CCP_ISOPEN: 0) + (isup? CCP_ISUP: 0);
+ cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR;
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ if (!hungup || errno != ENXIO)
+ error("Couldn't set kernel CCP state: %m");
+ }
+}
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+ int u;
+ struct ppp_idle *ip;
+{
+ return strioctl(pppfd, PPPIO_GIDLE, ip, 0, sizeof(struct ppp_idle)) >= 0;
+}
+
+/*
+ * get_ppp_stats - return statistics for the link.
+ */
+int
+get_ppp_stats(u, stats)
+ int u;
+ struct pppd_stats *stats;
+{
+ struct ppp_stats s;
+
+ if (!sync_serial &&
+ strioctl(pppfd, PPPIO_GETSTAT, &s, 0, sizeof(s)) < 0) {
+ error("Couldn't get link statistics: %m");
+ return 0;
+ }
+ stats->bytes_in = s.p.ppp_ibytes;
+ stats->bytes_out = s.p.ppp_obytes;
+ return 1;
+}
+
+#if 0
+/*
+ * set_filters - transfer the pass and active filters to the kernel.
+ */
+int
+set_filters(pass, active)
+ struct bpf_program *pass, *active;
+{
+ int ret = 1;
+
+ if (pass->bf_len > 0) {
+ if (strioctl(pppfd, PPPIO_PASSFILT, pass,
+ sizeof(struct bpf_program), 0) < 0) {
+ error("Couldn't set pass-filter in kernel: %m");
+ ret = 0;
+ }
+ }
+ if (active->bf_len > 0) {
+ if (strioctl(pppfd, PPPIO_ACTIVEFILT, active,
+ sizeof(struct bpf_program), 0) < 0) {
+ error("Couldn't set active-filter in kernel: %m");
+ ret = 0;
+ }
+ }
+ return ret;
+}
+#endif
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise. This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(unit)
+ int unit;
+{
+ int cf[2];
+
+ cf[0] = cf[1] = 0;
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ if (errno != ENXIO && errno != EINVAL)
+ error("Couldn't get compression flags: %m");
+ return 0;
+ }
+ return cf[0] & CCP_FATALERROR;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, xcidcomp, xmaxcid)
+ int u, vjcomp, xcidcomp, xmaxcid;
+{
+ int cf[2];
+ char maxcid[2];
+
+ if (vjcomp) {
+ maxcid[0] = xcidcomp;
+ maxcid[1] = 15; /* XXX should be rmaxcid */
+ if (strioctl(pppfd, PPPIO_VJINIT, maxcid, sizeof(maxcid), 0) < 0) {
+ error("Couldn't initialize VJ compression: %m");
+ }
+ }
+
+ cf[0] = (vjcomp? COMP_VJC + DECOMP_VJC: 0) /* XXX this is wrong */
+ + (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0);
+ cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID;
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ if (vjcomp)
+ error("Couldn't enable VJ compression: %m");
+ }
+
+ return 1;
+}
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(u)
+ int u;
+{
+ struct ifreq ifr;
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+ error("Couldn't mark interface up (get): %m");
+ return 0;
+ }
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+ error("Couldn't mark interface up (set): %m");
+ return 0;
+ }
+ if_is_up = 1;
+ return 1;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(u)
+ int u;
+{
+ struct ifreq ifr;
+
+ if (ipmuxid < 0)
+ return 1;
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+ error("Couldn't mark interface down (get): %m");
+ return 0;
+ }
+ ifr.ifr_flags &= ~IFF_UP;
+ if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+ error("Couldn't mark interface down (set): %m");
+ return 0;
+ }
+ if_is_up = 0;
+ return 1;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(u, proto, mode)
+ int u;
+ int proto;
+ enum NPmode mode;
+{
+ int npi[2];
+
+ npi[0] = proto;
+ npi[1] = (int) mode;
+ if (strioctl(pppfd, PPPIO_NPMODE, &npi, 2 * sizeof(int), 0) < 0) {
+ error("ioctl(set NP %d mode to %d): %m", proto, mode);
+ return 0;
+ }
+ return 1;
+}
+
+#if defined(SOL2) && defined(INET6)
+/*
+ * sif6up - Config the IPv6 interface up and enable IPv6 packets to pass.
+ */
+int
+sif6up(u)
+ int u;
+{
+ struct lifreq lifr;
+ int fd;
+
+ fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ return 0;
+ }
+
+ memset(&lifr, 0, sizeof(lifr));
+ strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+ if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+ close(fd);
+ return 0;
+ }
+
+ lifr.lifr_flags |= IFF_UP;
+ strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+ if (ioctl(fd, SIOCSLIFFLAGS, &lifr) < 0) {
+ close(fd);
+ return 0;
+ }
+
+ if6_is_up = 1;
+ close(fd);
+ return 1;
+}
+
+/*
+ * sifdown - Config the IPv6 interface down and disable IPv6.
+ */
+int
+sif6down(u)
+ int u;
+{
+ struct lifreq lifr;
+ int fd;
+
+ fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return 0;
+
+ memset(&lifr, 0, sizeof(lifr));
+ strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+ if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+ close(fd);
+ return 0;
+ }
+
+ lifr.lifr_flags &= ~IFF_UP;
+ strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+ if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+ close(fd);
+ return 0;
+ }
+
+ if6_is_up = 0;
+ close(fd);
+ return 1;
+}
+
+/*
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int
+sif6addr(u, o, h)
+ int u;
+ eui64_t o, h;
+{
+ struct lifreq lifr;
+ struct sockaddr_storage laddr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&laddr;
+ int fd;
+
+ fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return 0;
+
+ memset(&lifr, 0, sizeof(lifr));
+ strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+
+ /*
+ * Do this because /dev/ppp responds to DL_PHYS_ADDR_REQ with
+ * zero values, hence the interface token came to be zero too,
+ * and without this, in.ndpd will complain
+ */
+ IN6_LLTOKEN_FROM_EUI64(lifr, sin6, o);
+ if (ioctl(fd, SIOCSLIFTOKEN, &lifr) < 0) {
+ close(fd);
+ return 0;
+ }
+
+ /*
+ * Set the interface address and destination address
+ */
+ IN6_LLADDR_FROM_EUI64(lifr, sin6, o);
+ if (ioctl(fd, SIOCSLIFADDR, &lifr) < 0) {
+ close(fd);
+ return 0;
+ }
+
+ memset(&lifr, 0, sizeof(lifr));
+ strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+ IN6_LLADDR_FROM_EUI64(lifr, sin6, h);
+ if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) < 0) {
+ close(fd);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * cif6addr - Remove the IPv6 address from interface
+ */
+int
+cif6addr(u, o, h)
+ int u;
+ eui64_t o, h;
+{
+ return 1;
+}
+
+#endif /* defined(SOL2) && defined(INET6) */
+
+
+#define INET_ADDR(x) (((struct sockaddr_in *) &(x))->sin_addr.s_addr)
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+ int u;
+ u_int32_t o, h, m;
+{
+ struct ifreq ifr;
+ int ret = 1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_addr.sa_family = AF_INET;
+ INET_ADDR(ifr.ifr_addr) = m;
+ if (ioctl(ipfd, SIOCSIFNETMASK, &ifr) < 0) {
+ error("Couldn't set IP netmask: %m");
+ ret = 0;
+ }
+ ifr.ifr_addr.sa_family = AF_INET;
+ INET_ADDR(ifr.ifr_addr) = o;
+ if (ioctl(ipfd, SIOCSIFADDR, &ifr) < 0) {
+ error("Couldn't set local IP address: %m");
+ ret = 0;
+ }
+
+ /*
+ * On some systems, we have to explicitly set the point-to-point
+ * flag bit before we can set a destination address.
+ */
+ if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) >= 0
+ && (ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
+ ifr.ifr_flags |= IFF_POINTOPOINT;
+ if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+ error("Couldn't mark interface pt-to-pt: %m");
+ ret = 0;
+ }
+ }
+ ifr.ifr_dstaddr.sa_family = AF_INET;
+ INET_ADDR(ifr.ifr_dstaddr) = h;
+ if (ioctl(ipfd, SIOCSIFDSTADDR, &ifr) < 0) {
+ error("Couldn't set remote IP address: %m");
+ ret = 0;
+ }
+#if 0 /* now done in ppp_send_config */
+ ifr.ifr_metric = link_mtu;
+ if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) {
+ error("Couldn't set IP MTU: %m");
+ }
+#endif
+
+ remote_addr = h;
+ return ret;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+ int u;
+ u_int32_t o, h;
+{
+#if defined(__USLC__) /* was: #if 0 */
+ cifroute(unit, ouraddr, hisaddr);
+ if (ipmuxid >= 0) {
+ notice("Removing ppp interface unit");
+ if (ioctl(ipfd, I_UNLINK, ipmuxid) < 0) {
+ error("Can't remove ppp interface unit: %m");
+ return 0;
+ }
+ ipmuxid = -1;
+ }
+#endif
+ remote_addr = 0;
+ return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l, g;
+{
+ struct rtentry rt;
+
+#if defined(__USLC__)
+ g = l; /* use the local address as gateway */
+#endif
+ memset(&rt, 0, sizeof(rt));
+ rt.rt_dst.sa_family = AF_INET;
+ INET_ADDR(rt.rt_dst) = 0;
+ rt.rt_gateway.sa_family = AF_INET;
+ INET_ADDR(rt.rt_gateway) = g;
+ rt.rt_flags = RTF_GATEWAY;
+
+ if (ioctl(ipfd, SIOCADDRT, &rt) < 0) {
+ error("Can't add default route: %m");
+ return 0;
+ }
+
+ default_route_gateway = g;
+ return 1;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l, g;
+{
+ struct rtentry rt;
+
+#if defined(__USLC__)
+ g = l; /* use the local address as gateway */
+#endif
+ memset(&rt, 0, sizeof(rt));
+ rt.rt_dst.sa_family = AF_INET;
+ INET_ADDR(rt.rt_dst) = 0;
+ rt.rt_gateway.sa_family = AF_INET;
+ INET_ADDR(rt.rt_gateway) = g;
+ rt.rt_flags = RTF_GATEWAY;
+
+ if (ioctl(ipfd, SIOCDELRT, &rt) < 0) {
+ error("Can't delete default route: %m");
+ return 0;
+ }
+
+ default_route_gateway = 0;
+ return 1;
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ struct arpreq arpreq;
+
+ memset(&arpreq, 0, sizeof(arpreq));
+ if (!get_ether_addr(hisaddr, &arpreq.arp_ha))
+ return 0;
+
+ arpreq.arp_pa.sa_family = AF_INET;
+ INET_ADDR(arpreq.arp_pa) = hisaddr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ if (ioctl(ipfd, SIOCSARP, (caddr_t) &arpreq) < 0) {
+ error("Couldn't set proxy ARP entry: %m");
+ return 0;
+ }
+
+ proxy_arp_addr = hisaddr;
+ return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ struct arpreq arpreq;
+
+ memset(&arpreq, 0, sizeof(arpreq));
+ arpreq.arp_pa.sa_family = AF_INET;
+ INET_ADDR(arpreq.arp_pa) = hisaddr;
+ if (ioctl(ipfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+ error("Couldn't delete proxy ARP entry: %m");
+ return 0;
+ }
+
+ proxy_arp_addr = 0;
+ return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS 32
+
+static int
+get_ether_addr(ipaddr, hwaddr)
+ u_int32_t ipaddr;
+ struct sockaddr *hwaddr;
+{
+ struct ifreq *ifr, *ifend, ifreq;
+ int nif;
+ struct ifconf ifc;
+ u_int32_t ina, mask;
+
+ /*
+ * Scan through the system's network interfaces.
+ */
+#ifdef SIOCGIFNUM
+ if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0)
+#endif
+ nif = MAX_IFS;
+ ifc.ifc_len = nif * sizeof(struct ifreq);
+ ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len);
+ if (ifc.ifc_buf == 0)
+ return 0;
+ if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) {
+ warn("Couldn't get system interface list: %m");
+ free(ifc.ifc_buf);
+ return 0;
+ }
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ /*
+ * Check that the interface is up, and not point-to-point or loopback.
+ */
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ if (ioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags &
+ (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+ != (IFF_UP|IFF_BROADCAST))
+ continue;
+ /*
+ * Get its netmask and check that it's on the right subnet.
+ */
+ if (ioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ ina = INET_ADDR(ifr->ifr_addr);
+ mask = INET_ADDR(ifreq.ifr_addr);
+ if ((ipaddr & mask) == (ina & mask))
+ break;
+ }
+
+ if (ifr >= ifend) {
+ warn("No suitable interface found for proxy ARP");
+ free(ifc.ifc_buf);
+ return 0;
+ }
+
+ info("found interface %s for proxy ARP", ifr->ifr_name);
+ if (!get_hw_addr(ifr->ifr_name, ina, hwaddr)) {
+ error("Couldn't get hardware address for %s", ifr->ifr_name);
+ free(ifc.ifc_buf);
+ return 0;
+ }
+
+ free(ifc.ifc_buf);
+ return 1;
+}
+
+/*
+ * get_hw_addr_dlpi - obtain the hardware address using DLPI
+ */
+static int
+get_hw_addr_dlpi(name, hwaddr)
+ char *name;
+ struct sockaddr *hwaddr;
+{
+ char *p, *q;
+ int unit, iffd, adrlen;
+ unsigned char *adrp;
+ char ifdev[24];
+ struct {
+ union DL_primitives prim;
+ char space[64];
+ } reply;
+
+ /*
+ * We have to open the device and ask it for its hardware address.
+ * First split apart the device name and unit.
+ */
+ slprintf(ifdev, sizeof(ifdev), "/dev/%s", name);
+ for (q = ifdev + strlen(ifdev); --q >= ifdev; )
+ if (!isdigit(*q))
+ break;
+ unit = atoi(q+1);
+ q[1] = 0;
+
+ /*
+ * Open the device and do a DLPI attach and phys_addr_req.
+ */
+ iffd = open(ifdev, O_RDWR);
+ if (iffd < 0) {
+ error("Can't open %s: %m", ifdev);
+ return 0;
+ }
+ if (dlpi_attach(iffd, unit) < 0
+ || dlpi_get_reply(iffd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0
+ || dlpi_info_req(iffd) < 0
+ || dlpi_get_reply(iffd, &reply.prim, DL_INFO_ACK, sizeof(reply)) < 0) {
+ close(iffd);
+ return 0;
+ }
+
+ adrlen = reply.prim.info_ack.dl_addr_length;
+ adrp = (unsigned char *)&reply + reply.prim.info_ack.dl_addr_offset;
+
+#if DL_CURRENT_VERSION >= 2
+ if (reply.prim.info_ack.dl_sap_length < 0)
+ adrlen += reply.prim.info_ack.dl_sap_length;
+ else
+ adrp += reply.prim.info_ack.dl_sap_length;
+#endif
+
+ hwaddr->sa_family = AF_UNSPEC;
+ memcpy(hwaddr->sa_data, adrp, adrlen);
+
+ return 1;
+}
+/*
+ * get_hw_addr - obtain the hardware address for a named interface.
+ */
+static int
+get_hw_addr(name, ina, hwaddr)
+ char *name;
+ u_int32_t ina;
+ struct sockaddr *hwaddr;
+{
+ /* New way - get the address by doing an arp request. */
+ int s;
+ struct arpreq req;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ return 0;
+ memset(&req, 0, sizeof(req));
+ req.arp_pa.sa_family = AF_INET;
+ INET_ADDR(req.arp_pa) = ina;
+ if (ioctl(s, SIOCGARP, &req) < 0) {
+ error("Couldn't get ARP entry for %s: %m", ip_ntoa(ina));
+ return 0;
+ }
+ *hwaddr = req.arp_ha;
+ hwaddr->sa_family = AF_UNSPEC;
+
+ return 1;
+}
+
+static int
+dlpi_attach(fd, ppa)
+ int fd, ppa;
+{
+ dl_attach_req_t req;
+ struct strbuf buf;
+
+ req.dl_primitive = DL_ATTACH_REQ;
+ req.dl_ppa = ppa;
+ buf.len = sizeof(req);
+ buf.buf = (void *) &req;
+ return putmsg(fd, &buf, NULL, RS_HIPRI);
+}
+
+static int
+dlpi_info_req(fd)
+ int fd;
+{
+ dl_info_req_t req;
+ struct strbuf buf;
+
+ req.dl_primitive = DL_INFO_REQ;
+ buf.len = sizeof(req);
+ buf.buf = (void *) &req;
+ return putmsg(fd, &buf, NULL, RS_HIPRI);
+}
+
+static int
+dlpi_get_reply(fd, reply, expected_prim, maxlen)
+ union DL_primitives *reply;
+ int fd, expected_prim, maxlen;
+{
+ struct strbuf buf;
+ int flags, n;
+ struct pollfd pfd;
+
+ /*
+ * Use poll to wait for a message with a timeout.
+ */
+ pfd.fd = fd;
+ pfd.events = POLLIN | POLLPRI;
+ do {
+ n = poll(&pfd, 1, 1000);
+ } while (n == -1 && errno == EINTR);
+ if (n <= 0)
+ return -1;
+
+ /*
+ * Get the reply.
+ */
+ buf.maxlen = maxlen;
+ buf.buf = (void *) reply;
+ flags = 0;
+ if (getmsg(fd, &buf, NULL, &flags) < 0)
+ return -1;
+
+ if (buf.len < sizeof(ulong)) {
+ if (debug)
+ dbglog("dlpi response short (len=%d)\n", buf.len);
+ return -1;
+ }
+
+ if (reply->dl_primitive == expected_prim)
+ return 0;
+
+ if (debug) {
+ if (reply->dl_primitive == DL_ERROR_ACK) {
+ dbglog("dlpi error %d (unix errno %d) for prim %x\n",
+ reply->error_ack.dl_errno, reply->error_ack.dl_unix_errno,
+ reply->error_ack.dl_error_primitive);
+ } else {
+ dbglog("dlpi unexpected response prim %x\n",
+ reply->dl_primitive);
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'. If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+ u_int32_t addr;
+{
+ u_int32_t mask, nmask, ina;
+ struct ifreq *ifr, *ifend, ifreq;
+ int nif;
+ struct ifconf ifc;
+
+ addr = ntohl(addr);
+ if (IN_CLASSA(addr)) /* determine network mask for address class */
+ nmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(addr))
+ nmask = IN_CLASSB_NET;
+ else
+ nmask = IN_CLASSC_NET;
+ /* class D nets are disallowed by bad_ip_adrs */
+ mask = netmask | htonl(nmask);
+
+ /*
+ * Scan through the system's network interfaces.
+ */
+#ifdef SIOCGIFNUM
+ if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0)
+#endif
+ nif = MAX_IFS;
+ ifc.ifc_len = nif * sizeof(struct ifreq);
+ ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len);
+ if (ifc.ifc_buf == 0)
+ return mask;
+ if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) {
+ warn("Couldn't get system interface list: %m");
+ free(ifc.ifc_buf);
+ return mask;
+ }
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+ /*
+ * Check the interface's internet address.
+ */
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ ina = INET_ADDR(ifr->ifr_addr);
+ if ((ntohl(ina) & nmask) != (addr & nmask))
+ continue;
+ /*
+ * Check that the interface is up, and not point-to-point or loopback.
+ */
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ if (ioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+ != IFF_UP)
+ continue;
+ /*
+ * Get its netmask and OR it into our mask.
+ */
+ if (ioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask |= INET_ADDR(ifreq.ifr_addr);
+ }
+
+ free(ifc.ifc_buf);
+ return mask;
+}
+
+/*
+ * logwtmp - write an accounting record to the /var/adm/wtmp file.
+ */
+void
+logwtmp(line, name, host)
+ const char *line, *name, *host;
+{
+ static struct utmpx utmpx;
+
+ if (name[0] != 0) {
+ /* logging in */
+ strncpy(utmpx.ut_user, name, sizeof(utmpx.ut_user));
+ strncpy(utmpx.ut_id, ifname, sizeof(utmpx.ut_id));
+ strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
+ utmpx.ut_pid = getpid();
+ utmpx.ut_type = USER_PROCESS;
+ } else {
+ utmpx.ut_type = DEAD_PROCESS;
+ }
+ gettimeofday(&utmpx.ut_tv, NULL);
+ updwtmpx("/var/adm/wtmpx", &utmpx);
+}
+
+/*
+ * get_host_seed - return the serial number of this machine.
+ */
+int
+get_host_seed()
+{
+ char buf[32];
+
+ if (sysinfo(SI_HW_SERIAL, buf, sizeof(buf)) < 0) {
+ error("sysinfo: %m");
+ return 0;
+ }
+ return (int) strtoul(buf, NULL, 16);
+}
+
+static int
+strioctl(fd, cmd, ptr, ilen, olen)
+ int fd, cmd, ilen, olen;
+ void *ptr;
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+ if (ioctl(fd, I_STR, &str) == -1)
+ return -1;
+ if (str.ic_len != olen)
+ dbglog("strioctl: expected %d bytes, got %d for cmd %x\n",
+ olen, str.ic_len, cmd);
+ return 0;
+}
+
+#if 0
+/*
+ * lock - create a lock file for the named lock device
+ */
+
+#define LOCK_PREFIX "/var/spool/locks/LK."
+static char lock_file[40]; /* name of lock file created */
+
+int
+lock(dev)
+ char *dev;
+{
+ int n, fd, pid;
+ struct stat sbuf;
+ char ascii_pid[12];
+
+ if (stat(dev, &sbuf) < 0) {
+ error("Can't get device number for %s: %m", dev);
+ return -1;
+ }
+ if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
+ error("Can't lock %s: not a character device", dev);
+ return -1;
+ }
+ slprintf(lock_file, sizeof(lock_file), "%s%03d.%03d.%03d",
+ LOCK_PREFIX, major(sbuf.st_dev),
+ major(sbuf.st_rdev), minor(sbuf.st_rdev));
+
+ while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+ if (errno == EEXIST
+ && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+ /* Read the lock file to find out who has the device locked */
+ n = read(fd, ascii_pid, 11);
+ if (n <= 0) {
+ error("Can't read pid from lock file %s", lock_file);
+ close(fd);
+ } else {
+ ascii_pid[n] = 0;
+ pid = atoi(ascii_pid);
+ if (pid > 0 && kill(pid, 0) == -1 && errno == ESRCH) {
+ /* pid no longer exists - remove the lock file */
+ if (unlink(lock_file) == 0) {
+ close(fd);
+ notice("Removed stale lock on %s (pid %d)",
+ dev, pid);
+ continue;
+ } else
+ warn("Couldn't remove stale lock on %s",
+ dev);
+ } else
+ notice("Device %s is locked by pid %d",
+ dev, pid);
+ }
+ close(fd);
+ } else
+ error("Can't create lock file %s: %m", lock_file);
+ lock_file[0] = 0;
+ return -1;
+ }
+
+ slprintf(ascii_pid, sizeof(ascii_pid), "%10d\n", getpid());
+ write(fd, ascii_pid, 11);
+
+ close(fd);
+ return 1;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+ if (lock_file[0]) {
+ unlink(lock_file);
+ lock_file[0] = 0;
+ }
+}
+#endif
+
+/*
+ * cifroute - delete a route through the addresses given.
+ */
+int
+cifroute(u, our, his)
+ int u;
+ u_int32_t our, his;
+{
+ struct rtentry rt;
+
+ memset(&rt, 0, sizeof(rt));
+ rt.rt_dst.sa_family = AF_INET;
+ INET_ADDR(rt.rt_dst) = his;
+ rt.rt_gateway.sa_family = AF_INET;
+ INET_ADDR(rt.rt_gateway) = our;
+ rt.rt_flags = RTF_HOST;
+
+ if (ioctl(ipfd, SIOCDELRT, &rt) < 0) {
+ error("Can't delete route: %m");
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * have_route_to - determine if the system has a route to the specified
+ * IP address. Returns 0 if not, 1 if so, -1 if we can't tell.
+ * `addr' is in network byte order.
+ * For demand mode to work properly, we have to ignore routes
+ * through our own interface.
+ */
+#ifndef T_CURRENT /* needed for Solaris 2.5 */
+#define T_CURRENT MI_T_CURRENT
+#endif
+
+int
+have_route_to(addr)
+ u_int32_t addr;
+{
+#ifdef SOL2
+ int fd, r, flags, i;
+ struct {
+ struct T_optmgmt_req req;
+ struct opthdr hdr;
+ } req;
+ union {
+ struct T_optmgmt_ack ack;
+ unsigned char space[64];
+ } ack;
+ struct opthdr *rh;
+ struct strbuf cbuf, dbuf;
+ int nroutes;
+ mib2_ipRouteEntry_t routes[8];
+ mib2_ipRouteEntry_t *rp;
+
+ fd = open(mux_dev_name, O_RDWR);
+ if (fd < 0) {
+ warn("have_route_to: couldn't open %s: %m", mux_dev_name);
+ return -1;
+ }
+
+ req.req.PRIM_type = T_OPTMGMT_REQ;
+ req.req.OPT_offset = (char *) &req.hdr - (char *) &req;
+ req.req.OPT_length = sizeof(req.hdr);
+ req.req.MGMT_flags = T_CURRENT;
+
+ req.hdr.level = MIB2_IP;
+ req.hdr.name = 0;
+ req.hdr.len = 0;
+
+ cbuf.buf = (char *) &req;
+ cbuf.len = sizeof(req);
+
+ if (putmsg(fd, &cbuf, NULL, 0) == -1) {
+ warn("have_route_to: putmsg: %m");
+ close(fd);
+ return -1;
+ }
+
+ for (;;) {
+ cbuf.buf = (char *) &ack;
+ cbuf.maxlen = sizeof(ack);
+ dbuf.buf = (char *) routes;
+ dbuf.maxlen = sizeof(routes);
+ flags = 0;
+ r = getmsg(fd, &cbuf, &dbuf, &flags);
+ if (r == -1) {
+ warn("have_route_to: getmsg: %m");
+ close(fd);
+ return -1;
+ }
+
+ if (cbuf.len < sizeof(struct T_optmgmt_ack)
+ || ack.ack.PRIM_type != T_OPTMGMT_ACK
+ || ack.ack.MGMT_flags != T_SUCCESS
+ || ack.ack.OPT_length < sizeof(struct opthdr)) {
+ dbglog("have_route_to: bad message len=%d prim=%d",
+ cbuf.len, ack.ack.PRIM_type);
+ close(fd);
+ return -1;
+ }
+
+ rh = (struct opthdr *) ((char *)&ack + ack.ack.OPT_offset);
+ if (rh->level == 0 && rh->name == 0)
+ break;
+ if (rh->level != MIB2_IP || rh->name != MIB2_IP_21) {
+ while (r == MOREDATA)
+ r = getmsg(fd, NULL, &dbuf, &flags);
+ continue;
+ }
+
+ for (;;) {
+ nroutes = dbuf.len / sizeof(mib2_ipRouteEntry_t);
+ for (rp = routes, i = 0; i < nroutes; ++i, ++rp) {
+ if (rp->ipRouteMask != ~0) {
+ dbglog("have_route_to: dest=%x gw=%x mask=%x\n",
+ rp->ipRouteDest, rp->ipRouteNextHop,
+ rp->ipRouteMask);
+ if (((addr ^ rp->ipRouteDest) & rp->ipRouteMask) == 0
+ && rp->ipRouteNextHop != remote_addr)
+ return 1;
+ }
+ }
+ if (r == 0)
+ break;
+ r = getmsg(fd, NULL, &dbuf, &flags);
+ }
+ }
+ close(fd);
+ return 0;
+#else
+ return -1;
+#endif /* SOL2 */
+}
+
+/*
+ * get_pty - get a pty master/slave pair and chown the slave side to
+ * the uid given. Assumes slave_name points to MAXPATHLEN bytes of space.
+ */
+int
+get_pty(master_fdp, slave_fdp, slave_name, uid)
+ int *master_fdp;
+ int *slave_fdp;
+ char *slave_name;
+ int uid;
+{
+ int mfd, sfd;
+ char *pty_name;
+ struct termios tios;
+
+ mfd = open("/dev/ptmx", O_RDWR);
+ if (mfd < 0) {
+ error("Couldn't open pty master: %m");
+ return 0;
+ }
+
+ pty_name = ptsname(mfd);
+ if (pty_name == NULL) {
+ error("Couldn't get name of pty slave");
+ close(mfd);
+ return 0;
+ }
+ if (chown(pty_name, uid, -1) < 0)
+ warn("Couldn't change owner of pty slave: %m");
+ if (chmod(pty_name, S_IRUSR | S_IWUSR) < 0)
+ warn("Couldn't change permissions on pty slave: %m");
+ if (unlockpt(mfd) < 0)
+ warn("Couldn't unlock pty slave: %m");
+
+ sfd = open(pty_name, O_RDWR);
+ if (sfd < 0) {
+ error("Couldn't open pty slave %s: %m", pty_name);
+ close(mfd);
+ return 0;
+ }
+ if (ioctl(sfd, I_PUSH, "ptem") < 0)
+ warn("Couldn't push ptem module on pty slave: %m");
+
+ dbglog("Using %s", pty_name);
+ strlcpy(slave_name, pty_name, MAXPATHLEN);
+ *master_fdp = mfd;
+ *slave_fdp = sfd;
+
+ return 1;
+}
diff --git a/mdk-stage1/ppp/pppd/sys-sunos4.c b/mdk-stage1/ppp/pppd/sys-sunos4.c
new file mode 100644
index 000000000..3344948e9
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/sys-sunos4.c
@@ -0,0 +1,1559 @@
+/*
+ * System-dependent procedures for pppd under SunOS 4.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#define RCSID "$Id$"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <signal.h>
+#include <malloc.h>
+#include <utmp.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/nit_if.h>
+#include <net/route.h>
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+
+#if defined(sun) && defined(sparc)
+#include <alloca.h>
+#ifndef __GNUC__
+extern void *alloca();
+#endif
+#endif /*sparc*/
+
+static const char rcsid[] = RCSID;
+
+static int pppfd;
+static int fdmuxid = -1;
+static int iffd;
+static int sockfd;
+
+static int restore_term;
+static struct termios inittermios;
+static struct winsize wsinfo; /* Initial window size info */
+static pid_t parent_pid; /* PID of our parent */
+
+extern u_char inpacket_buf[]; /* borrowed from main.c */
+
+#define MAX_POLLFDS 32
+static struct pollfd pollfds[MAX_POLLFDS];
+static int n_pollfds;
+
+static int link_mtu, link_mru;
+
+#define NMODULES 32
+static int tty_nmodules;
+static char tty_modules[NMODULES][FMNAMESZ+1];
+
+static int if_is_up; /* Interface has been marked up */
+static u_int32_t ifaddrs[2]; /* local and remote addresses */
+static u_int32_t default_route_gateway; /* Gateway for default route added */
+static u_int32_t proxy_arp_addr; /* Addr for proxy arp entry added */
+
+/* Prototypes for procedures local to this file. */
+static int translate_speed __P((int));
+static int baud_rate_of __P((int));
+static int get_ether_addr __P((u_int32_t, struct sockaddr *));
+static int strioctl __P((int, int, void *, int, int));
+
+
+/*
+ * sys_init - System-dependent initialization.
+ */
+void
+sys_init()
+{
+ int x;
+
+ /* Get an internet socket for doing socket ioctl's on. */
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ fatal("Couldn't create IP socket: %m");
+
+ /*
+ * We may want to send a SIGHUP to the session leader associated
+ * with our controlling terminal later. Because SunOS doesn't
+ * have getsid(), we make do with sending the signal to our
+ * parent process.
+ */
+ parent_pid = getppid();
+
+ /*
+ * Open the ppp device.
+ */
+ pppfd = open("/dev/ppp", O_RDWR | O_NONBLOCK, 0);
+ if (pppfd < 0)
+ fatal("Can't open /dev/ppp: %m");
+ if (kdebugflag) {
+ x = PPPDBG_LOG + PPPDBG_DRIVER;
+ strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0);
+ }
+
+ /* Assign a new PPA and get its unit number. */
+ if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0)
+ fatal("Can't create new PPP interface: %m");
+
+ /*
+ * Open the ppp device again and push the if_ppp module on it.
+ */
+ iffd = open("/dev/ppp", O_RDWR, 0);
+ if (iffd < 0)
+ fatal("Can't open /dev/ppp (2): %m");
+ if (kdebugflag) {
+ x = PPPDBG_LOG + PPPDBG_DRIVER;
+ strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0);
+ }
+ if (strioctl(iffd, PPPIO_ATTACH, &ifunit, sizeof(int), 0) < 0)
+ fatal("Couldn't attach ppp interface to device: %m");
+ if (ioctl(iffd, I_PUSH, "if_ppp") < 0)
+ fatal("Can't push ppp interface module: %m");
+ if (kdebugflag) {
+ x = PPPDBG_LOG + PPPDBG_IF;
+ strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0);
+ }
+ if (strioctl(iffd, PPPIO_NEWPPA, &ifunit, sizeof(int), 0) < 0)
+ fatal("Couldn't create ppp interface unit: %m");
+ x = PPP_IP;
+ if (strioctl(iffd, PPPIO_BIND, &x, sizeof(int), 0) < 0)
+ fatal("Couldn't bind ppp interface to IP SAP: %m");
+
+ n_pollfds = 0;
+}
+
+/*
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This shouldn't call die() because it's called from die().
+ */
+void
+sys_cleanup()
+{
+ if (if_is_up)
+ sifdown(0);
+ if (ifaddrs[0])
+ cifaddr(0, ifaddrs[0], ifaddrs[1]);
+ if (default_route_gateway)
+ cifdefaultroute(0, 0, default_route_gateway);
+ if (proxy_arp_addr)
+ cifproxyarp(0, proxy_arp_addr);
+}
+
+/*
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close()
+{
+ close(iffd);
+ close(pppfd);
+ close(sockfd);
+}
+
+/*
+ * sys_check_options - check the options that the user specified
+ */
+int
+sys_check_options()
+{
+ return 1;
+}
+
+#if 0
+/*
+ * daemon - Detach us from controlling terminal session.
+ */
+int
+daemon(nochdir, noclose)
+ int nochdir, noclose;
+{
+ int pid;
+
+ if ((pid = fork()) < 0)
+ return -1;
+ if (pid != 0)
+ exit(0); /* parent dies */
+ setsid();
+ if (!nochdir)
+ chdir("/");
+ if (!noclose) {
+ fclose(stdin); /* don't need stdin, stdout, stderr */
+ fclose(stdout);
+ fclose(stderr);
+ }
+ return 0;
+}
+#endif
+
+/*
+ * ppp_available - check whether the system has any ppp interfaces
+ */
+int
+ppp_available()
+{
+ struct stat buf;
+
+ return stat("/dev/ppp", &buf) >= 0;
+}
+
+/*
+ * tty_establish_ppp - Turn the serial port into a ppp interface.
+ */
+int
+tty_establish_ppp(fd)
+ int fd;
+{
+ int i;
+
+ /* Pop any existing modules off the tty stream. */
+ for (i = 0;; ++i)
+ if (ioctl(fd, I_LOOK, tty_modules[i]) < 0
+ || ioctl(fd, I_POP, 0) < 0)
+ break;
+ tty_nmodules = i;
+
+ /* Push the async hdlc module and the compressor module. */
+ if (ioctl(fd, I_PUSH, "ppp_ahdl") < 0)
+ fatal("Couldn't push PPP Async HDLC module: %m");
+ if (ioctl(fd, I_PUSH, "ppp_comp") < 0)
+ error("Couldn't push PPP compression module: %m");
+
+ /* Link the serial port under the PPP multiplexor. */
+ if ((fdmuxid = ioctl(pppfd, I_LINK, fd)) < 0)
+ fatal("Can't link tty to PPP mux: %m");
+
+ return pppfd;
+}
+
+/*
+ * disestablish_ppp - Restore the serial port to normal operation.
+ * It attempts to reconstruct the stream with the previously popped
+ * modules. This shouldn't call die() because it's called from die().
+ */
+void
+tty_disestablish_ppp(fd)
+ int fd;
+{
+ int i;
+
+ if (fdmuxid >= 0) {
+ if (ioctl(pppfd, I_UNLINK, fdmuxid) < 0) {
+ if (!hungup)
+ error("Can't unlink tty from PPP mux: %m");
+ }
+ fdmuxid = -1;
+
+ if (!hungup) {
+ while (ioctl(fd, I_POP, 0) >= 0)
+ ;
+ for (i = tty_nmodules - 1; i >= 0; --i)
+ if (ioctl(fd, I_PUSH, tty_modules[i]) < 0)
+ error("Couldn't restore tty module %s: %m",
+ tty_modules[i]);
+ }
+ if (hungup && default_device && parent_pid > 0) {
+ /*
+ * If we have received a hangup, we need to send a SIGHUP
+ * to the terminal's controlling process. The reason is
+ * that the original stream head for the terminal hasn't
+ * seen the M_HANGUP message (it went up through the ppp
+ * driver to the stream head for our fd to /dev/ppp).
+ * Actually we send the signal to the process that invoked
+ * pppd, since SunOS doesn't have getsid().
+ */
+ kill(parent_pid, SIGHUP);
+ }
+ }
+}
+
+/*
+ * Check whether the link seems not to be 8-bit clean.
+ */
+void
+clean_check()
+{
+ int x;
+ char *s;
+
+ if (strioctl(pppfd, PPPIO_GCLEAN, &x, 0, sizeof(x)) < 0)
+ return;
+ s = NULL;
+ switch (~x) {
+ case RCV_B7_0:
+ s = "bit 7 set to 1";
+ break;
+ case RCV_B7_1:
+ s = "bit 7 set to 0";
+ break;
+ case RCV_EVNP:
+ s = "odd parity";
+ break;
+ case RCV_ODDP:
+ s = "even parity";
+ break;
+ }
+ if (s != NULL) {
+ warn("Serial link is not 8-bit clean:");
+ warn("All received characters had %s", s);
+ }
+}
+
+/*
+ * List of valid speeds.
+ */
+struct speed {
+ int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+ { 50, B50 },
+#endif
+#ifdef B75
+ { 75, B75 },
+#endif
+#ifdef B110
+ { 110, B110 },
+#endif
+#ifdef B134
+ { 134, B134 },
+#endif
+#ifdef B150
+ { 150, B150 },
+#endif
+#ifdef B200
+ { 200, B200 },
+#endif
+#ifdef B300
+ { 300, B300 },
+#endif
+#ifdef B600
+ { 600, B600 },
+#endif
+#ifdef B1200
+ { 1200, B1200 },
+#endif
+#ifdef B1800
+ { 1800, B1800 },
+#endif
+#ifdef B2000
+ { 2000, B2000 },
+#endif
+#ifdef B2400
+ { 2400, B2400 },
+#endif
+#ifdef B3600
+ { 3600, B3600 },
+#endif
+#ifdef B4800
+ { 4800, B4800 },
+#endif
+#ifdef B7200
+ { 7200, B7200 },
+#endif
+#ifdef B9600
+ { 9600, B9600 },
+#endif
+#ifdef B19200
+ { 19200, B19200 },
+#endif
+#ifdef B38400
+ { 38400, B38400 },
+#endif
+#ifdef EXTA
+ { 19200, EXTA },
+#endif
+#ifdef EXTB
+ { 38400, EXTB },
+#endif
+#ifdef B57600
+ { 57600, B57600 },
+#endif
+#ifdef B115200
+ { 115200, B115200 },
+#endif
+ { 0, 0 }
+};
+
+/*
+ * Translate from bits/second to a speed_t.
+ */
+static int
+translate_speed(bps)
+ int bps;
+{
+ struct speed *speedp;
+
+ if (bps == 0)
+ return 0;
+ for (speedp = speeds; speedp->speed_int; speedp++)
+ if (bps == speedp->speed_int)
+ return speedp->speed_val;
+ warn("speed %d not supported", bps);
+ return 0;
+}
+
+/*
+ * Translate from a speed_t to bits/second.
+ */
+static int
+baud_rate_of(speed)
+ int speed;
+{
+ struct speed *speedp;
+
+ if (speed == 0)
+ return 0;
+ for (speedp = speeds; speedp->speed_int; speedp++)
+ if (speed == speedp->speed_val)
+ return speedp->speed_int;
+ return 0;
+}
+
+/*
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc. If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+void
+set_up_tty(fd, local)
+ int fd, local;
+{
+ int speed;
+ struct termios tios;
+
+ if (tcgetattr(fd, &tios) < 0)
+ fatal("tcgetattr: %m");
+
+ if (!restore_term) {
+ inittermios = tios;
+ ioctl(fd, TIOCGWINSZ, &wsinfo);
+ }
+
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+ if (crtscts > 0)
+ tios.c_cflag |= CRTSCTS;
+ else if (crtscts < 0)
+ tios.c_cflag &= ~CRTSCTS;
+
+ tios.c_cflag |= CS8 | CREAD | HUPCL;
+ if (local || !modem)
+ tios.c_cflag |= CLOCAL;
+ tios.c_iflag = IGNBRK | IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+
+ if (crtscts == -2) {
+ tios.c_iflag |= IXON | IXOFF;
+ tios.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */
+ tios.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */
+ }
+
+ speed = translate_speed(inspeed);
+ if (speed) {
+ cfsetospeed(&tios, speed);
+ cfsetispeed(&tios, speed);
+ } else {
+ speed = cfgetospeed(&tios);
+ /*
+ * We can't proceed if the serial port speed is 0,
+ * since that implies that the serial port is disabled.
+ */
+ if (speed == B0)
+ fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
+ }
+
+ if (tcsetattr(fd, TCSAFLUSH, &tios) < 0)
+ fatal("tcsetattr: %m");
+
+ baud_rate = inspeed = baud_rate_of(speed);
+ restore_term = 1;
+}
+
+/*
+ * restore_tty - restore the terminal to the saved settings.
+ */
+void
+restore_tty(fd)
+ int fd;
+{
+ if (restore_term) {
+ if (!default_device) {
+ /*
+ * Turn off echoing, because otherwise we can get into
+ * a loop with the tty and the modem echoing to each other.
+ * We presume we are the sole user of this tty device, so
+ * when we close it, it will revert to its defaults anyway.
+ */
+ inittermios.c_lflag &= ~(ECHO | ECHONL);
+ }
+ if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
+ if (!hungup && errno != ENXIO)
+ warn("tcsetattr: %m");
+ ioctl(fd, TIOCSWINSZ, &wsinfo);
+ restore_term = 0;
+ }
+}
+
+/*
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+void
+setdtr(fd, on)
+int fd, on;
+{
+ int modembits = TIOCM_DTR;
+
+ ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
+}
+
+/*
+ * open_loopback - open the device we use for getting packets
+ * in demand mode. Under SunOS, we use our existing fd
+ * to the ppp driver.
+ */
+int
+open_ppp_loopback()
+{
+ return pppfd;
+}
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ struct strbuf data;
+ int retries;
+ struct pollfd pfd;
+
+ if (debug)
+ dbglog("sent %P", p, len);
+
+ data.len = len;
+ data.buf = (caddr_t) p;
+ retries = 4;
+ while (putmsg(pppfd, NULL, &data, 0) < 0) {
+ if (--retries < 0 || (errno != EWOULDBLOCK && errno != EAGAIN)) {
+ if (errno != ENXIO)
+ error("Couldn't send packet: %m");
+ break;
+ }
+ pfd.fd = pppfd;
+ pfd.events = POLLOUT;
+ poll(&pfd, 1, 250); /* wait for up to 0.25 seconds */
+ }
+}
+
+
+/*
+ * wait_input - wait until there is data available,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_input(timo)
+ struct timeval *timo;
+{
+ int t;
+
+ t = timo == NULL? -1: timo->tv_sec * 1000 + timo->tv_usec / 1000;
+ if (poll(pollfds, n_pollfds, t) < 0 && errno != EINTR) {
+ if (errno != EAGAIN)
+ fatal("poll: %m");
+ /* we can get EAGAIN on a heavily loaded system,
+ * just wait a short time and try again. */
+ usleep(50000);
+ }
+}
+
+/*
+ * add_fd - add an fd to the set that wait_input waits for.
+ */
+void add_fd(fd)
+ int fd;
+{
+ int n;
+
+ for (n = 0; n < n_pollfds; ++n)
+ if (pollfds[n].fd == fd)
+ return;
+ if (n_pollfds < MAX_POLLFDS) {
+ pollfds[n_pollfds].fd = fd;
+ pollfds[n_pollfds].events = POLLIN | POLLPRI | POLLHUP;
+ ++n_pollfds;
+ } else
+ error("Too many inputs!");
+}
+
+/*
+ * remove_fd - remove an fd from the set that wait_input waits for.
+ */
+void remove_fd(fd)
+ int fd;
+{
+ int n;
+
+ for (n = 0; n < n_pollfds; ++n) {
+ if (pollfds[n].fd == fd) {
+ while (++n < n_pollfds)
+ pollfds[n-1] = pollfds[n];
+ --n_pollfds;
+ break;
+ }
+ }
+}
+
+#if 0
+/*
+ * wait_loop_output - wait until there is data available on the
+ * loopback, for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_loop_output(timo)
+ struct timeval *timo;
+{
+ wait_input(timo);
+}
+
+/*
+ * wait_time - wait for a given length of time or until a
+ * signal is received.
+ */
+void
+wait_time(timo)
+ struct timeval *timo;
+{
+ int n;
+
+ n = select(0, NULL, NULL, NULL, timo);
+ if (n < 0 && errno != EINTR)
+ fatal("select: %m");
+}
+#endif
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+ u_char *buf;
+{
+ struct strbuf ctrl, data;
+ int flags, len;
+ unsigned char ctrlbuf[64];
+
+ for (;;) {
+ data.maxlen = PPP_MRU + PPP_HDRLEN;
+ data.buf = (caddr_t) buf;
+ ctrl.maxlen = sizeof(ctrlbuf);
+ ctrl.buf = (caddr_t) ctrlbuf;
+ flags = 0;
+ len = getmsg(pppfd, &ctrl, &data, &flags);
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return -1;
+ fatal("Error reading packet: %m");
+ }
+
+ if (ctrl.len <= 0)
+ return data.len;
+
+ /*
+ * Got a M_PROTO or M_PCPROTO message. Huh?
+ */
+ if (debug)
+ dbglog("got ctrl msg len=%d", ctrl.len);
+
+ }
+}
+
+/*
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output()
+{
+ int len;
+ int rv = 0;
+
+ while ((len = read_packet(inpacket_buf)) > 0) {
+ if (loop_frame(inpacket_buf, len))
+ rv = 1;
+ }
+ return rv;
+}
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+ int unit, mtu;
+ u_int32_t asyncmap;
+ int pcomp, accomp;
+{
+ int cf[2];
+ struct ifreq ifr;
+
+ link_mtu = mtu;
+ if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) {
+ if (hungup && errno == ENXIO)
+ return;
+ error("Couldn't set MTU: %m");
+ }
+ if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+ error("Couldn't set transmit ACCM: %m");
+ }
+ cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
+ cf[1] = COMP_PROT | COMP_AC;
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ error("Couldn't set prot/AC compression: %m");
+ }
+
+ /* set mtu for ip as well */
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_metric = link_mtu;
+ if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) {
+ error("Couldn't set IP MTU: %m");
+ }
+}
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+ppp_set_xaccm(unit, accm)
+ int unit;
+ ext_accm accm;
+{
+ if (strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) {
+ if (!hungup || errno != ENXIO)
+ warn("Couldn't set extended ACCM: %m");
+ }
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+ int unit, mru;
+ u_int32_t asyncmap;
+ int pcomp, accomp;
+{
+ int cf[2];
+
+ link_mru = mru;
+ if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) {
+ if (hungup && errno == ENXIO)
+ return;
+ error("Couldn't set MRU: %m");
+ }
+ if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+ error("Couldn't set receive ACCM: %m");
+ }
+ cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0);
+ cf[1] = DECOMP_PROT | DECOMP_AC;
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ error("Couldn't set prot/AC decompression: %m");
+ }
+}
+
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+ int unit, opt_len, for_transmit;
+ u_char *opt_ptr;
+{
+ if (strioctl(pppfd, (for_transmit? PPPIO_XCOMP: PPPIO_RCOMP),
+ opt_ptr, opt_len, 0) >= 0)
+ return 1;
+ return (errno == ENOSR)? 0: -1;
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(unit, isopen, isup)
+ int unit, isopen, isup;
+{
+ int cf[2];
+
+ cf[0] = (isopen? CCP_ISOPEN: 0) + (isup? CCP_ISUP: 0);
+ cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR;
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ if (!hungup || errno != ENXIO)
+ error("Couldn't set kernel CCP state: %m");
+ }
+}
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+ int u;
+ struct ppp_idle *ip;
+{
+ return strioctl(pppfd, PPPIO_GIDLE, ip, 0, sizeof(struct ppp_idle)) >= 0;
+}
+
+/*
+ * get_ppp_stats - return statistics for the link.
+ */
+int
+get_ppp_stats(u, stats)
+ int u;
+ struct pppd_stats *stats;
+{
+ struct ppp_stats s;
+
+ if (strioctl(pppfd, PPPIO_GETSTAT, &s, 0, sizeof(s)) < 0) {
+ error("Couldn't get link statistics: %m");
+ return 0;
+ }
+ stats->bytes_in = s.p.ppp_ibytes;
+ stats->bytes_out = s.p.ppp_obytes;
+ return 1;
+}
+
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise. This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(unit)
+ int unit;
+{
+ int cf[2];
+
+ cf[0] = cf[1] = 0;
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ if (errno != ENXIO && errno != EINVAL)
+ error("Couldn't get compression flags: %m");
+ return 0;
+ }
+ return cf[0] & CCP_FATALERROR;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, xcidcomp, xmaxcid)
+ int u, vjcomp, xcidcomp, xmaxcid;
+{
+ int cf[2];
+ char maxcid[2];
+
+ if (vjcomp) {
+ maxcid[0] = xcidcomp;
+ maxcid[1] = 15; /* XXX should be rmaxcid */
+ if (strioctl(pppfd, PPPIO_VJINIT, maxcid, sizeof(maxcid), 0) < 0) {
+ error("Couldn't initialize VJ compression: %m");
+ }
+ }
+
+ cf[0] = (vjcomp? COMP_VJC + DECOMP_VJC: 0) /* XXX this is wrong */
+ + (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0);
+ cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID;
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+ if (vjcomp)
+ error("Couldn't enable VJ compression: %m");
+ }
+
+ return 1;
+}
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(u)
+ int u;
+{
+ struct ifreq ifr;
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+ error("Couldn't mark interface up (get): %m");
+ return 0;
+ }
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
+ error("Couldn't mark interface up (set): %m");
+ return 0;
+ }
+ if_is_up = 1;
+ return 1;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(u)
+ int u;
+{
+ struct ifreq ifr;
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+ error("Couldn't mark interface down (get): %m");
+ return 0;
+ }
+ if ((ifr.ifr_flags & IFF_UP) != 0) {
+ ifr.ifr_flags &= ~IFF_UP;
+ if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
+ error("Couldn't mark interface down (set): %m");
+ return 0;
+ }
+ }
+ if_is_up = 0;
+ return 1;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(u, proto, mode)
+ int u;
+ int proto;
+ enum NPmode mode;
+{
+ int npi[2];
+
+ npi[0] = proto;
+ npi[1] = (int) mode;
+ if (strioctl(pppfd, PPPIO_NPMODE, npi, 2 * sizeof(int), 0) < 0) {
+ error("ioctl(set NP %d mode to %d): %m", proto, mode);
+ return 0;
+ }
+ return 1;
+}
+
+#define INET_ADDR(x) (((struct sockaddr_in *) &(x))->sin_addr.s_addr)
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+ int u;
+ u_int32_t o, h, m;
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_addr.sa_family = AF_INET;
+ INET_ADDR(ifr.ifr_addr) = m;
+ if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
+ error("Couldn't set IP netmask: %m");
+ }
+ ifr.ifr_addr.sa_family = AF_INET;
+ INET_ADDR(ifr.ifr_addr) = o;
+ if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
+ error("Couldn't set local IP address: %m");
+ }
+ ifr.ifr_dstaddr.sa_family = AF_INET;
+ INET_ADDR(ifr.ifr_dstaddr) = h;
+ if (ioctl(sockfd, SIOCSIFDSTADDR, &ifr) < 0) {
+ error("Couldn't set remote IP address: %m");
+ }
+#if 0 /* now done in ppp_send_config */
+ ifr.ifr_metric = link_mtu;
+ if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) {
+ error("Couldn't set IP MTU: %m");
+ }
+#endif
+ ifaddrs[0] = o;
+ ifaddrs[1] = h;
+
+ return 1;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+ int u;
+ u_int32_t o, h;
+{
+ struct rtentry rt;
+
+ bzero(&rt, sizeof(rt));
+ rt.rt_dst.sa_family = AF_INET;
+ INET_ADDR(rt.rt_dst) = h;
+ rt.rt_gateway.sa_family = AF_INET;
+ INET_ADDR(rt.rt_gateway) = o;
+ rt.rt_flags = RTF_HOST;
+ if (ioctl(sockfd, SIOCDELRT, &rt) < 0)
+ error("Couldn't delete route through interface: %m");
+ ifaddrs[0] = 0;
+ return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l, g;
+{
+ struct rtentry rt;
+
+ bzero(&rt, sizeof(rt));
+ rt.rt_dst.sa_family = AF_INET;
+ INET_ADDR(rt.rt_dst) = 0;
+ rt.rt_gateway.sa_family = AF_INET;
+ INET_ADDR(rt.rt_gateway) = g;
+ rt.rt_flags = RTF_GATEWAY;
+
+ if (ioctl(sockfd, SIOCADDRT, &rt) < 0) {
+ error("Can't add default route: %m");
+ return 0;
+ }
+
+ default_route_gateway = g;
+ return 1;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l, g;
+{
+ struct rtentry rt;
+
+ bzero(&rt, sizeof(rt));
+ rt.rt_dst.sa_family = AF_INET;
+ INET_ADDR(rt.rt_dst) = 0;
+ rt.rt_gateway.sa_family = AF_INET;
+ INET_ADDR(rt.rt_gateway) = g;
+ rt.rt_flags = RTF_GATEWAY;
+
+ if (ioctl(sockfd, SIOCDELRT, &rt) < 0) {
+ error("Can't delete default route: %m");
+ return 0;
+ }
+
+ default_route_gateway = 0;
+ return 1;
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ struct arpreq arpreq;
+
+ bzero(&arpreq, sizeof(arpreq));
+ if (!get_ether_addr(hisaddr, &arpreq.arp_ha))
+ return 0;
+
+ arpreq.arp_pa.sa_family = AF_INET;
+ INET_ADDR(arpreq.arp_pa) = hisaddr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ if (ioctl(sockfd, SIOCSARP, (caddr_t) &arpreq) < 0) {
+ error("Couldn't set proxy ARP entry: %m");
+ return 0;
+ }
+
+ proxy_arp_addr = hisaddr;
+ return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ struct arpreq arpreq;
+
+ bzero(&arpreq, sizeof(arpreq));
+ arpreq.arp_pa.sa_family = AF_INET;
+ INET_ADDR(arpreq.arp_pa) = hisaddr;
+ if (ioctl(sockfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+ error("Couldn't delete proxy ARP entry: %m");
+ return 0;
+ }
+
+ proxy_arp_addr = 0;
+ return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS 32
+
+static int
+get_ether_addr(ipaddr, hwaddr)
+ u_int32_t ipaddr;
+ struct sockaddr *hwaddr;
+{
+ struct ifreq *ifr, *ifend;
+ u_int32_t ina, mask;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+ int nit_fd;
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+ error("ioctl(SIOCGIFCONF): %m");
+ return 0;
+ }
+
+ /*
+ * Scan through looking for an interface with an Internet
+ * address on the same subnet as `ipaddr'.
+ */
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *)
+ ((char *)&ifr->ifr_addr + sizeof(struct sockaddr))) {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+
+ /*
+ * Check that the interface is up, and not point-to-point
+ * or loopback.
+ */
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags &
+ (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+ != (IFF_UP|IFF_BROADCAST))
+ continue;
+
+ /*
+ * Get its netmask and check that it's on the right subnet.
+ */
+ if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+ mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr;
+ if ((ipaddr & mask) != (ina & mask))
+ continue;
+
+ break;
+ }
+ }
+
+ if (ifr >= ifend)
+ return 0;
+ info("found interface %s for proxy arp", ifr->ifr_name);
+
+ /*
+ * Grab the physical address for this interface.
+ */
+ if ((nit_fd = open("/dev/nit", O_RDONLY)) < 0) {
+ error("Couldn't open /dev/nit: %m");
+ return 0;
+ }
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ if (ioctl(nit_fd, NIOCBIND, &ifreq) < 0
+ || ioctl(nit_fd, SIOCGIFADDR, &ifreq) < 0) {
+ error("Couldn't get hardware address for %s: %m",
+ ifreq.ifr_name);
+ close(nit_fd);
+ return 0;
+ }
+
+ hwaddr->sa_family = AF_UNSPEC;
+ memcpy(hwaddr->sa_data, ifreq.ifr_addr.sa_data, 6);
+ close(nit_fd);
+ return 1;
+}
+
+/*
+ * have_route_to - determine if the system has any route to
+ * a given IP address.
+ * For demand mode to work properly, we have to ignore routes
+ * through our own interface.
+ */
+int have_route_to(addr)
+ u_int32_t addr;
+{
+ return -1;
+}
+
+#define WTMPFILE "/usr/adm/wtmp"
+
+void
+logwtmp(line, name, host)
+ const char *line, *name, *host;
+{
+ int fd;
+ struct stat buf;
+ struct utmp ut;
+
+ if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0)
+ return;
+ if (!fstat(fd, &buf)) {
+ strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+ strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+ (void)time(&ut.ut_time);
+ if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp))
+ (void)ftruncate(fd, buf.st_size);
+ }
+ close(fd);
+}
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'. If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+ u_int32_t addr;
+{
+ u_int32_t mask, nmask, ina;
+ struct ifreq *ifr, *ifend, ifreq;
+ struct ifconf ifc;
+
+ addr = ntohl(addr);
+ if (IN_CLASSA(addr)) /* determine network mask for address class */
+ nmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(addr))
+ nmask = IN_CLASSB_NET;
+ else
+ nmask = IN_CLASSC_NET;
+ /* class D nets are disallowed by bad_ip_adrs */
+ mask = netmask | htonl(nmask);
+
+ /*
+ * Scan through the system's network interfaces.
+ */
+ ifc.ifc_len = MAX_IFS * sizeof(struct ifreq);
+ ifc.ifc_req = alloca(ifc.ifc_len);
+ if (ifc.ifc_req == 0)
+ return mask;
+ if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+ warn("Couldn't get system interface list: %m");
+ return mask;
+ }
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+ /*
+ * Check the interface's internet address.
+ */
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ ina = INET_ADDR(ifr->ifr_addr);
+ if ((ntohl(ina) & nmask) != (addr & nmask))
+ continue;
+ /*
+ * Check that the interface is up, and not point-to-point or loopback.
+ */
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+ != IFF_UP)
+ continue;
+ /*
+ * Get its netmask and OR it into our mask.
+ */
+ if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask |= INET_ADDR(ifreq.ifr_addr);
+ }
+
+ return mask;
+}
+
+static int
+strioctl(fd, cmd, ptr, ilen, olen)
+ int fd, cmd, ilen, olen;
+ void *ptr;
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+ if (ioctl(fd, I_STR, &str) == -1)
+ return -1;
+ if (str.ic_len != olen)
+ dbglog("strioctl: expected %d bytes, got %d for cmd %x\n",
+ olen, str.ic_len, cmd);
+ return 0;
+}
+
+/*
+ * Use the hostid as part of the random number seed.
+ */
+int
+get_host_seed()
+{
+ return gethostid();
+}
+
+#if 0
+/*
+ * Code for locking/unlocking the serial device.
+ * This code is derived from chat.c.
+ */
+
+#if !defined(HDB) && !defined(SUNOS3)
+#define HDB 1 /* ascii lock files are the default */
+#endif
+
+#ifndef LOCK_DIR
+# if HDB
+# define PIDSTRING
+# define LOCK_PREFIX "/usr/spool/locks/LCK.."
+# else /* HDB */
+# define LOCK_PREFIX "/usr/spool/uucp/LCK.."
+# endif /* HDB */
+#endif /* LOCK_DIR */
+
+static char *lock_file; /* name of lock file created */
+
+/*
+ * lock - create a lock file for the named device.
+ */
+int
+lock(dev)
+ char *dev;
+{
+ char hdb_lock_buffer[12];
+ int fd, pid, n;
+ char *p;
+ size_t l;
+
+ if ((p = strrchr(dev, '/')) != NULL)
+ dev = p + 1;
+ l = strlen(LOCK_PREFIX) + strlen(dev) + 1;
+ lock_file = malloc(l);
+ if (lock_file == NULL)
+ novm("lock file name");
+ slprintf(lock_file, l, "%s%s", LOCK_PREFIX, dev);
+
+ while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+ if (errno == EEXIST
+ && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+ /* Read the lock file to find out who has the device locked */
+#ifdef PIDSTRING
+ n = read(fd, hdb_lock_buffer, 11);
+ if (n > 0) {
+ hdb_lock_buffer[n] = 0;
+ pid = atoi(hdb_lock_buffer);
+ }
+#else
+ n = read(fd, &pid, sizeof(pid));
+#endif
+ if (n <= 0) {
+ error("Can't read pid from lock file %s", lock_file);
+ close(fd);
+ } else {
+ if (kill(pid, 0) == -1 && errno == ESRCH) {
+ /* pid no longer exists - remove the lock file */
+ if (unlink(lock_file) == 0) {
+ close(fd);
+ notice("Removed stale lock on %s (pid %d)",
+ dev, pid);
+ continue;
+ } else
+ warn("Couldn't remove stale lock on %s",
+ dev);
+ } else
+ notice("Device %s is locked by pid %d",
+ dev, pid);
+ }
+ close(fd);
+ } else
+ error("Can't create lock file %s: %m", lock_file);
+ free(lock_file);
+ lock_file = NULL;
+ return -1;
+ }
+
+#ifdef PIDSTRING
+ slprintf(hdb_lock_buffer, sizeof(hdb_lock_buffer), "%10d\n", getpid());
+ write(fd, hdb_lock_buffer, 11);
+#else
+ pid = getpid();
+ write(fd, &pid, sizeof pid);
+#endif
+
+ close(fd);
+ return 0;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+ if (lock_file) {
+ unlink(lock_file);
+ free(lock_file);
+ lock_file = NULL;
+ }
+}
+#endif /* lock stuff removed */
+
+/*
+ * get_pty - get a pty master/slave pair and chown the slave side
+ * to the uid given. Assumes slave_name points to >= 12 bytes of space.
+ */
+int
+get_pty(master_fdp, slave_fdp, slave_name, uid)
+ int *master_fdp;
+ int *slave_fdp;
+ char *slave_name;
+ int uid;
+{
+ int i, mfd, sfd;
+ char pty_name[12];
+ struct termios tios;
+
+ sfd = -1;
+ for (i = 0; i < 64; ++i) {
+ slprintf(pty_name, sizeof(pty_name), "/dev/pty%c%x",
+ 'p' + i / 16, i % 16);
+ mfd = open(pty_name, O_RDWR, 0);
+ if (mfd >= 0) {
+ pty_name[5] = 't';
+ sfd = open(pty_name, O_RDWR | O_NOCTTY, 0);
+ if (sfd >= 0)
+ break;
+ close(mfd);
+ }
+ }
+ if (sfd < 0)
+ return 0;
+
+ strlcpy(slave_name, pty_name, 12);
+ *master_fdp = mfd;
+ *slave_fdp = sfd;
+ fchown(sfd, uid, -1);
+ fchmod(sfd, S_IRUSR | S_IWUSR);
+ if (tcgetattr(sfd, &tios) == 0) {
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+ tios.c_cflag |= CS8 | CREAD;
+ tios.c_iflag = IGNPAR | CLOCAL;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0)
+ warn("couldn't set attributes on pty: %m");
+ } else
+ warn("couldn't get attributes on pty: %m");
+
+ return 1;
+}
+
+/*
+ * SunOS doesn't have strtoul :-(
+ */
+unsigned long
+strtoul(str, ptr, base)
+ char *str, **ptr;
+ int base;
+{
+ return (unsigned long) strtol(str, ptr, base);
+}
+
+/*
+ * Or strerror :-(
+ */
+extern char *sys_errlist[];
+extern int sys_nerr;
+
+char *
+strerror(n)
+ int n;
+{
+ static char unknown[32];
+
+ if (n > 0 && n < sys_nerr)
+ return sys_errlist[n];
+ slprintf(unknown, sizeof(unknown), "Error %d", n);
+ return unknown;
+}
diff --git a/mdk-stage1/ppp/pppd/tdb.c b/mdk-stage1/ppp/pppd/tdb.c
new file mode 100644
index 000000000..7fd58291e
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/tdb.c
@@ -0,0 +1,1282 @@
+/*
+ * Database functions
+ * Copyright (C) Andrew Tridgell 1999
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms AND provided that this software or
+ * any derived work is only used as part of the PPP daemon (pppd)
+ * and related utilities.
+ * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Note: this software is also available under the Gnu Public License
+ * version 2 or later.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include "tdb.h"
+
+#define TDB_VERSION (0x26011967 + 1)
+#define TDB_MAGIC (0x26011999U)
+#define TDB_FREE_MAGIC (~TDB_MAGIC)
+#define TDB_ALIGN 4
+#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGN)
+#define DEFAULT_HASH_SIZE 128
+#define TDB_PAGE_SIZE 0x2000
+#define TDB_LEN_MULTIPLIER 10
+#define FREELIST_TOP (sizeof(struct tdb_header))
+
+#define LOCK_SET 1
+#define LOCK_CLEAR 0
+
+/* lock offsets */
+#define GLOBAL_LOCK 0
+#define ACTIVE_LOCK 4
+#define LIST_LOCK_BASE 1024
+
+#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+/* the body of the database is made of one list_struct for the free space
+ plus a separate data list for each hash value */
+struct list_struct {
+ tdb_len rec_len; /* total byte length of record */
+ tdb_off next; /* offset of the next record in the list */
+ tdb_len key_len; /* byte length of key */
+ tdb_len data_len; /* byte length of data */
+ unsigned full_hash; /* the full 32 bit hash of the key */
+ unsigned magic; /* try to catch errors */
+ /*
+ the following union is implied
+ union {
+ char record[rec_len];
+ struct {
+ char key[key_len];
+ char data[data_len];
+ }
+ }
+ */
+};
+
+/* a null data record - useful for error returns */
+static TDB_DATA null_data;
+
+/* a byte range locking function - return 0 on success
+ this functions locks/unlocks 1 byte at the specified offset */
+static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset,
+ int set, int rw_type, int lck_type)
+{
+#if NOLOCK
+ return 0;
+#else
+ struct flock fl;
+
+ if (tdb->fd == -1) return 0; /* for in memory tdb */
+
+ if (tdb->read_only) return -1;
+
+ fl.l_type = set==LOCK_SET?rw_type:F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = offset;
+ fl.l_len = 1;
+ fl.l_pid = 0;
+
+ if (fcntl(tdb->fd, lck_type, &fl) != 0) {
+#if TDB_DEBUG
+ if (lck_type == F_SETLKW) {
+ printf("lock %d failed at %d (%s)\n",
+ set, offset, strerror(errno));
+ }
+#endif
+ tdb->ecode = TDB_ERR_LOCK;
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+/* lock a list in the database. list -1 is the alloc list */
+static int tdb_lock(TDB_CONTEXT *tdb, int list)
+{
+ if (list < -1 || list >= (int)tdb->header.hash_size) {
+#if TDB_DEBUG
+ printf("bad list %d\n", list);
+#endif
+ return -1;
+ }
+ if (tdb->locked[list+1] == 0) {
+ if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_SET,
+ F_WRLCK, F_SETLKW) != 0) {
+ return -1;
+ }
+ }
+ tdb->locked[list+1]++;
+ return 0;
+}
+
+/* unlock the database. */
+static int tdb_unlock(TDB_CONTEXT *tdb, int list)
+{
+ if (list < -1 || list >= (int)tdb->header.hash_size) {
+#if TDB_DEBUG
+ printf("bad unlock list %d\n", list);
+#endif
+ return -1;
+ }
+
+ if (tdb->locked[list+1] == 0) {
+#if TDB_DEBUG
+ printf("not locked %d\n", list);
+#endif
+ tdb->ecode = TDB_ERR_LOCK;
+ return -1;
+ }
+ if (tdb->locked[list+1] == 1) {
+ if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_CLEAR,
+ F_WRLCK, F_SETLKW) != 0) {
+ return -1;
+ }
+ }
+ tdb->locked[list+1]--;
+ return 0;
+}
+
+/* the hash algorithm - turn a key into an integer
+ This is based on the hash agorithm from gdbm */
+static unsigned tdb_hash(TDB_DATA *key)
+{
+ unsigned value; /* Used to compute the hash value. */
+ unsigned i; /* Used to cycle through random values. */
+
+ /* Set the initial value from the key size. */
+ value = 0x238F13AF * key->dsize;
+ for (i=0; i < key->dsize; i++) {
+ value = (value + (key->dptr[i] << (i*5 % 24)));
+ }
+
+ value = (1103515243 * value + 12345);
+
+ return value;
+}
+
+/* find the top of the hash chain for an open database */
+static tdb_off tdb_hash_top(TDB_CONTEXT *tdb, unsigned hash)
+{
+ tdb_off ret;
+ hash = BUCKET(hash);
+ ret = FREELIST_TOP + (hash+1)*sizeof(tdb_off);
+ return ret;
+}
+
+
+/* check for an out of bounds access - if it is out of bounds then
+ see if the database has been expanded by someone else and expand
+ if necessary */
+static int tdb_oob(TDB_CONTEXT *tdb, tdb_off offset)
+{
+ struct stat st;
+ if ((offset <= tdb->map_size) || (tdb->fd == -1)) return 0;
+
+ fstat(tdb->fd, &st);
+ if (st.st_size <= (ssize_t)offset) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ }
+
+#if HAVE_MMAP
+ if (tdb->map_ptr) {
+ munmap(tdb->map_ptr, tdb->map_size);
+ tdb->map_ptr = NULL;
+ }
+#endif
+
+ tdb->map_size = st.st_size;
+#if HAVE_MMAP
+ tdb->map_ptr = (void *)mmap(NULL, tdb->map_size,
+ tdb->read_only?PROT_READ:PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_FILE, tdb->fd, 0);
+#endif
+ return 0;
+}
+
+
+/* write a lump of data at a specified offset */
+static int tdb_write(TDB_CONTEXT *tdb, tdb_off offset, const char *buf, tdb_len len)
+{
+ if (tdb_oob(tdb, offset + len) != 0) {
+ /* oops - trying to write beyond the end of the database! */
+ return -1;
+ }
+
+ if (tdb->map_ptr) {
+ memcpy(offset + (char *)tdb->map_ptr, buf, len);
+ } else {
+ if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
+ write(tdb->fd, buf, len) != (ssize_t)len) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* read a lump of data at a specified offset */
+static int tdb_read(TDB_CONTEXT *tdb, tdb_off offset, char *buf, tdb_len len)
+{
+ if (tdb_oob(tdb, offset + len) != 0) {
+ /* oops - trying to read beyond the end of the database! */
+ return -1;
+ }
+
+ if (tdb->map_ptr) {
+ memcpy(buf, offset + (char *)tdb->map_ptr, len);
+ } else {
+ if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
+ read(tdb->fd, buf, len) != (ssize_t)len) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/* read a lump of data, allocating the space for it */
+static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
+{
+ char *buf;
+
+ buf = (char *)malloc(len);
+
+ if (!buf) {
+ tdb->ecode = TDB_ERR_OOM;
+ return NULL;
+ }
+
+ if (tdb_read(tdb, offset, buf, len) == -1) {
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+/* convenience routine for writing a record */
+static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ return tdb_write(tdb, offset, (char *)rec, sizeof(*rec));
+}
+
+/* convenience routine for writing a tdb_off */
+static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+ return tdb_write(tdb, offset, (char *)d, sizeof(*d));
+}
+
+/* read a tdb_off from the store */
+static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+ return tdb_read(tdb, offset, (char *)d, sizeof(*d));
+}
+
+/* read a record and check for simple errors */
+static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ if (tdb_read(tdb, offset, (char *)rec, sizeof(*rec)) == -1) return -1;
+ if (rec->magic != TDB_MAGIC) {
+#if TDB_DEBUG
+ printf("bad magic 0x%08x at offset %d\n",
+ rec->magic, offset);
+#endif
+ tdb->ecode = TDB_ERR_CORRUPT;
+ return -1;
+ }
+ if (tdb_oob(tdb, rec->next) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+/* expand the database at least length bytes by expanding the
+ underlying file and doing the mmap again if necessary */
+static int tdb_expand(TDB_CONTEXT *tdb, tdb_off length)
+{
+ struct list_struct rec;
+ tdb_off offset, ptr;
+ char b = 0;
+
+ tdb_lock(tdb,-1);
+
+ /* make sure we know about any previous expansions by another
+ process */
+ tdb_oob(tdb,tdb->map_size + 1);
+
+ /* always make room for at least 10 more records */
+ length *= TDB_LEN_MULTIPLIER;
+
+ /* and round the database up to a multiple of TDB_PAGE_SIZE */
+ length = ((tdb->map_size + length + TDB_PAGE_SIZE) & ~(TDB_PAGE_SIZE - 1)) - tdb->map_size;
+
+ /* expand the file itself */
+ if (tdb->fd != -1) {
+ lseek(tdb->fd, tdb->map_size + length - 1, SEEK_SET);
+ if (write(tdb->fd, &b, 1) != 1) goto fail;
+ }
+
+ /* form a new freelist record */
+ offset = FREELIST_TOP;
+ rec.rec_len = length - sizeof(rec);
+ rec.magic = TDB_FREE_MAGIC;
+ if (ofs_read(tdb, offset, &rec.next) == -1) {
+ goto fail;
+ }
+
+#if HAVE_MMAP
+ if (tdb->fd != -1 && tdb->map_ptr) {
+ munmap(tdb->map_ptr, tdb->map_size);
+ tdb->map_ptr = NULL;
+ }
+#endif
+
+ tdb->map_size += length;
+
+ if (tdb->fd == -1) {
+ tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
+ }
+
+ /* write it out */
+ if (rec_write(tdb, tdb->map_size - length, &rec) == -1) {
+ goto fail;
+ }
+
+ /* link it into the free list */
+ ptr = tdb->map_size - length;
+ if (ofs_write(tdb, offset, &ptr) == -1) goto fail;
+
+#if HAVE_MMAP
+ if (tdb->fd != -1) {
+ tdb->map_ptr = (void *)mmap(NULL, tdb->map_size,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_FILE, tdb->fd, 0);
+ }
+#endif
+
+ tdb_unlock(tdb, -1);
+ return 0;
+
+ fail:
+ tdb_unlock(tdb,-1);
+ return -1;
+}
+
+/* allocate some space from the free list. The offset returned points
+ to a unconnected list_struct within the database with room for at
+ least length bytes of total data
+
+ 0 is returned if the space could not be allocated
+ */
+static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length)
+{
+ tdb_off offset, rec_ptr, last_ptr;
+ struct list_struct rec, lastrec, newrec;
+
+ tdb_lock(tdb, -1);
+
+ again:
+ last_ptr = 0;
+ offset = FREELIST_TOP;
+
+ /* read in the freelist top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ goto fail;
+ }
+
+ /* keep looking until we find a freelist record that is big
+ enough */
+ while (rec_ptr) {
+ if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
+ goto fail;
+ }
+
+ if (rec.magic != TDB_FREE_MAGIC) {
+#if TDB_DEBUG
+ printf("bad magic 0x%08x in free list\n", rec.magic);
+#endif
+ goto fail;
+ }
+
+ if (rec.rec_len >= length) {
+ /* found it - now possibly split it up */
+ if (rec.rec_len > length + MIN_REC_SIZE) {
+ length = (length + TDB_ALIGN) & ~(TDB_ALIGN-1);
+
+ newrec.rec_len = rec.rec_len - (sizeof(rec) + length);
+ newrec.next = rec.next;
+ newrec.magic = TDB_FREE_MAGIC;
+
+ rec.rec_len = length;
+ rec.next = rec_ptr + sizeof(rec) + rec.rec_len;
+
+ if (rec_write(tdb, rec.next, &newrec) == -1) {
+ goto fail;
+ }
+
+ if (rec_write(tdb, rec_ptr, &rec) == -1) {
+ goto fail;
+ }
+ }
+
+ /* remove it from the list */
+ if (last_ptr == 0) {
+ offset = FREELIST_TOP;
+
+ if (ofs_write(tdb, offset, &rec.next) == -1) {
+ goto fail;
+ }
+ } else {
+ lastrec.next = rec.next;
+ if (rec_write(tdb, last_ptr, &lastrec) == -1) {
+ goto fail;
+ }
+ }
+
+ /* all done - return the new record offset */
+ tdb_unlock(tdb, -1);
+ return rec_ptr;
+ }
+
+ /* move to the next record */
+ lastrec = rec;
+ last_ptr = rec_ptr;
+ rec_ptr = rec.next;
+ }
+
+ /* we didn't find enough space. See if we can expand the
+ database and if we can then try again */
+ if (tdb_expand(tdb, length + sizeof(rec)) == 0) goto again;
+
+ fail:
+#if TDB_DEBUG
+ printf("tdb_allocate failed for size %u\n", length);
+#endif
+ tdb_unlock(tdb, -1);
+ return 0;
+}
+
+/* initialise a new database with a specified hash size */
+static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+{
+ struct tdb_header header;
+ tdb_off offset;
+ int i, size = 0;
+ tdb_off buf[16];
+
+ /* create the header */
+ memset(&header, 0, sizeof(header));
+ memcpy(header.magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
+ header.version = TDB_VERSION;
+ header.hash_size = hash_size;
+ lseek(tdb->fd, 0, SEEK_SET);
+ ftruncate(tdb->fd, 0);
+
+ if (tdb->fd != -1 && write(tdb->fd, &header, sizeof(header)) !=
+ sizeof(header)) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ } else size += sizeof(header);
+
+ /* the freelist and hash pointers */
+ offset = 0;
+ memset(buf, 0, sizeof(buf));
+
+ for (i=0;(hash_size+1)-i >= 16; i += 16) {
+ if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(buf)) !=
+ sizeof(buf)) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ } else size += sizeof(buf);
+ }
+
+ for (;i<hash_size+1; i++) {
+ if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(tdb_off)) !=
+ sizeof(tdb_off)) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ } else size += sizeof(tdb_off);
+ }
+
+ if (tdb->fd == -1) {
+ tdb->map_ptr = calloc(size, 1);
+ tdb->map_size = size;
+ if (tdb->map_ptr == NULL) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ }
+ memcpy(&tdb->header, &header, sizeof(header));
+ }
+
+#if TDB_DEBUG
+ printf("initialised database of hash_size %u\n",
+ hash_size);
+#endif
+ return 0;
+}
+
+/* Returns 0 on fail. On success, return offset of record, and fills
+ in rec */
+static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, unsigned int hash,
+ struct list_struct *rec)
+{
+ tdb_off offset, rec_ptr;
+
+ /* find the top of the hash chain */
+ offset = tdb_hash_top(tdb, hash);
+
+ /* read in the hash top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1)
+ return 0;
+
+ /* keep looking until we find the right record */
+ while (rec_ptr) {
+ if (rec_read(tdb, rec_ptr, rec) == -1)
+ return 0;
+
+ if (hash == rec->full_hash && key.dsize == rec->key_len) {
+ char *k;
+ /* a very likely hit - read the key */
+ k = tdb_alloc_read(tdb, rec_ptr + sizeof(*rec),
+ rec->key_len);
+
+ if (!k)
+ return 0;
+
+ if (memcmp(key.dptr, k, key.dsize) == 0) {
+ free(k);
+ return rec_ptr;
+ }
+ free(k);
+ }
+
+ /* move to the next record */
+ rec_ptr = rec->next;
+ }
+ return 0;
+}
+
+/*
+ return an error string for the last tdb error
+*/
+char *tdb_error(TDB_CONTEXT *tdb)
+{
+ int i;
+ static struct {
+ enum TDB_ERROR ecode;
+ char *estring;
+ } emap[] = {
+ {TDB_SUCCESS, "Success"},
+ {TDB_ERR_CORRUPT, "Corrupt database"},
+ {TDB_ERR_IO, "IO Error"},
+ {TDB_ERR_LOCK, "Locking error"},
+ {TDB_ERR_OOM, "Out of memory"},
+ {TDB_ERR_EXISTS, "Record exists"},
+ {-1, NULL}};
+ if (tdb != NULL) {
+ for (i=0;emap[i].estring;i++) {
+ if (tdb->ecode == emap[i].ecode) return emap[i].estring;
+ }
+ } else {
+ return "Invalid tdb context";
+ }
+ return "Invalid error code";
+}
+
+
+/* update an entry in place - this only works if the new data size
+ is <= the old data size and the key exists.
+ on failure return -1
+*/
+int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
+{
+ unsigned hash;
+ struct list_struct rec;
+ tdb_off rec_ptr;
+ int ret = -1;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_update() called with null context\n");
+#endif
+ return -1;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ tdb_lock(tdb, BUCKET(hash));
+ rec_ptr = tdb_find(tdb, key, hash, &rec);
+
+ if (!rec_ptr)
+ goto out;
+
+ /* must be long enough */
+ if (rec.rec_len < key.dsize + dbuf.dsize)
+ goto out;
+
+ if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+ dbuf.dptr, dbuf.dsize) == -1)
+ goto out;
+
+ if (dbuf.dsize != rec.data_len) {
+ /* update size */
+ rec.data_len = dbuf.dsize;
+ ret = rec_write(tdb, rec_ptr, &rec);
+ } else
+ ret = 0;
+
+ out:
+ tdb_unlock(tdb, BUCKET(hash));
+ return ret;
+}
+
+/* find an entry in the database given a key */
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ unsigned hash;
+ tdb_off rec_ptr;
+ struct list_struct rec;
+ TDB_DATA ret = null_data;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_fetch() called with null context\n");
+#endif
+ return null_data;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ tdb_lock(tdb, BUCKET(hash));
+ rec_ptr = tdb_find(tdb, key, hash, &rec);
+
+ if (rec_ptr) {
+ ret.dptr = tdb_alloc_read(tdb,
+ rec_ptr + sizeof(rec) + rec.key_len,
+ rec.data_len);
+ ret.dsize = rec.data_len;
+ }
+
+ tdb_unlock(tdb, BUCKET(hash));
+ return ret;
+}
+
+/* check if an entry in the database exists
+
+ note that 1 is returned if the key is found and 0 is returned if not found
+ this doesn't match the conventions in the rest of this module, but is
+ compatible with gdbm
+*/
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ unsigned hash;
+ tdb_off rec_ptr;
+ struct list_struct rec;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_exists() called with null context\n");
+#endif
+ return 0;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ tdb_lock(tdb, BUCKET(hash));
+ rec_ptr = tdb_find(tdb, key, hash, &rec);
+ tdb_unlock(tdb, BUCKET(hash));
+
+ return rec_ptr != 0;
+}
+
+/* traverse the entire database - calling fn(tdb, key, data) on each element.
+ return -1 on error or the record count traversed
+ if fn is NULL then it is not called
+ a non-zero return value from fn() indicates that the traversal should stop
+ */
+int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void* state), void* state)
+{
+ int count = 0;
+ unsigned h;
+ tdb_off offset, rec_ptr;
+ struct list_struct rec;
+ char *data;
+ TDB_DATA key, dbuf;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_traverse() called with null context\n");
+#endif
+ return -1;
+ }
+
+ /* loop over all hash chains */
+ for (h = 0; h < tdb->header.hash_size; h++) {
+ tdb_lock(tdb, BUCKET(h));
+
+ /* read in the hash top */
+ offset = tdb_hash_top(tdb, h);
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ goto fail;
+ }
+
+ /* traverse all records for this hash */
+ while (rec_ptr) {
+ if (rec_read(tdb, rec_ptr, &rec) == -1) {
+ goto fail;
+ }
+
+ /* now read the full record */
+ data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
+ rec.key_len + rec.data_len);
+ if (!data) {
+ goto fail;
+ }
+
+ key.dptr = data;
+ key.dsize = rec.key_len;
+ dbuf.dptr = data + rec.key_len;
+ dbuf.dsize = rec.data_len;
+ count++;
+
+ if (fn && fn(tdb, key, dbuf, state) != 0) {
+ /* they want us to stop traversing */
+ free(data);
+ tdb_unlock(tdb, BUCKET(h));
+ return count;
+ }
+
+ /* a miss - drat */
+ free(data);
+
+ /* move to the next record */
+ rec_ptr = rec.next;
+ }
+ tdb_unlock(tdb, BUCKET(h));
+ }
+
+ /* return the number traversed */
+ return count;
+
+ fail:
+ tdb_unlock(tdb, BUCKET(h));
+ return -1;
+}
+
+
+/* find the first entry in the database and return its key */
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
+{
+ tdb_off offset, rec_ptr;
+ struct list_struct rec;
+ unsigned hash;
+ TDB_DATA ret;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_firstkey() called with null context\n");
+#endif
+ return null_data;
+ }
+
+ /* look for a non-empty hash chain */
+ for (hash = 0, rec_ptr = 0;
+ hash < tdb->header.hash_size;
+ hash++) {
+ /* find the top of the hash chain */
+ offset = tdb_hash_top(tdb, hash);
+
+ tdb_lock(tdb, BUCKET(hash));
+
+ /* read in the hash top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ goto fail;
+ }
+
+ if (rec_ptr) break;
+
+ tdb_unlock(tdb, BUCKET(hash));
+ }
+
+ if (rec_ptr == 0) return null_data;
+
+ /* we've found a non-empty chain, now read the record */
+ if (rec_read(tdb, rec_ptr, &rec) == -1) {
+ goto fail;
+ }
+
+ /* allocate and read the key space */
+ ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
+ ret.dsize = rec.key_len;
+ tdb_unlock(tdb, BUCKET(hash));
+ return ret;
+
+ fail:
+ tdb_unlock(tdb, BUCKET(hash));
+ return null_data;
+}
+
+/* find the next entry in the database, returning its key */
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ unsigned hash, hbucket;
+ tdb_off rec_ptr, offset;
+ struct list_struct rec;
+ TDB_DATA ret;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_nextkey() called with null context\n");
+#endif
+ return null_data;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+ hbucket = BUCKET(hash);
+
+ tdb_lock(tdb, hbucket);
+ rec_ptr = tdb_find(tdb, key, hash, &rec);
+ if (rec_ptr) {
+ /* we want the next record after this one */
+ rec_ptr = rec.next;
+ }
+
+ /* not found or last in hash: look for next non-empty hash chain */
+ while (rec_ptr == 0) {
+ tdb_unlock(tdb, hbucket);
+
+ if (++hbucket >= tdb->header.hash_size - 1)
+ return null_data;
+
+ offset = tdb_hash_top(tdb, hbucket);
+ tdb_lock(tdb, hbucket);
+ /* read in the hash top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ tdb_unlock(tdb, hbucket);
+ return null_data;
+ }
+ }
+
+ /* Read the record. */
+ if (rec_read(tdb, rec_ptr, &rec) == -1) {
+ tdb_unlock(tdb, hbucket);
+ return null_data;
+ }
+ /* allocate and read the key */
+ ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
+ ret.dsize = rec.key_len;
+ tdb_unlock(tdb, hbucket);
+
+ return ret;
+}
+
+/* delete an entry in the database given a key */
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ unsigned hash;
+ tdb_off offset, rec_ptr, last_ptr;
+ struct list_struct rec, lastrec;
+ char *data = NULL;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_delete() called with null context\n");
+#endif
+ return -1;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ tdb_lock(tdb, BUCKET(hash));
+
+ /* find the top of the hash chain */
+ offset = tdb_hash_top(tdb, hash);
+
+ /* read in the hash top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ goto fail;
+ }
+
+ last_ptr = 0;
+
+ /* keep looking until we find the right record */
+ while (rec_ptr) {
+ if (rec_read(tdb, rec_ptr, &rec) == -1) {
+ goto fail;
+ }
+
+ if (hash == rec.full_hash && key.dsize == rec.key_len) {
+ /* a very likely hit - read the record and full key */
+ data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
+ rec.key_len);
+ if (!data) {
+ goto fail;
+ }
+
+ if (memcmp(key.dptr, data, key.dsize) == 0) {
+ /* a definite match - delete it */
+ if (last_ptr == 0) {
+ offset = tdb_hash_top(tdb, hash);
+ if (ofs_write(tdb, offset, &rec.next) == -1) {
+ goto fail;
+ }
+ } else {
+ lastrec.next = rec.next;
+ if (rec_write(tdb, last_ptr, &lastrec) == -1) {
+ goto fail;
+ }
+ }
+ tdb_unlock(tdb, BUCKET(hash));
+ tdb_lock(tdb, -1);
+ /* and recover the space */
+ offset = FREELIST_TOP;
+ if (ofs_read(tdb, offset, &rec.next) == -1) {
+ goto fail2;
+ }
+ rec.magic = TDB_FREE_MAGIC;
+ if (rec_write(tdb, rec_ptr, &rec) == -1) {
+ goto fail2;
+ }
+ if (ofs_write(tdb, offset, &rec_ptr) == -1) {
+ goto fail2;
+ }
+
+ /* yipee - all done */
+ free(data);
+ tdb_unlock(tdb, -1);
+ return 0;
+ }
+
+ /* a miss - drat */
+ free(data);
+ data = NULL;
+ }
+
+ /* move to the next record */
+ last_ptr = rec_ptr;
+ lastrec = rec;
+ rec_ptr = rec.next;
+ }
+
+ fail:
+ if (data) free(data);
+ tdb_unlock(tdb, BUCKET(hash));
+ return -1;
+
+ fail2:
+ if (data) free(data);
+ tdb_unlock(tdb, -1);
+ return -1;
+}
+
+
+/* store an element in the database, replacing any existing element
+ with the same key
+
+ return 0 on success, -1 on failure
+*/
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+{
+ struct list_struct rec;
+ unsigned hash;
+ tdb_off rec_ptr, offset;
+ char *p = NULL;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_store() called with null context\n");
+#endif
+ return -1;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ /* check for it existing */
+ if (flag == TDB_INSERT && tdb_exists(tdb, key)) {
+ tdb->ecode = TDB_ERR_EXISTS;
+ return -1;
+ }
+
+ /* first try in-place update */
+ if (flag != TDB_INSERT && tdb_update(tdb, key, dbuf) == 0) {
+ return 0;
+ }
+
+ rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize);
+ if (rec_ptr == 0) {
+ return -1;
+ }
+
+ tdb_lock(tdb, BUCKET(hash));
+
+ /* delete any existing record - if it doesn't exist we don't care */
+ if (flag != TDB_INSERT) {
+ tdb_delete(tdb, key);
+ }
+
+ /* read the newly created record */
+ if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
+ goto fail;
+ }
+
+ if (rec.magic != TDB_FREE_MAGIC) goto fail;
+
+ /* find the top of the hash chain */
+ offset = tdb_hash_top(tdb, hash);
+
+ /* read in the hash top diretcly into our next pointer */
+ if (ofs_read(tdb, offset, &rec.next) == -1) {
+ goto fail;
+ }
+
+ rec.key_len = key.dsize;
+ rec.data_len = dbuf.dsize;
+ rec.full_hash = hash;
+ rec.magic = TDB_MAGIC;
+
+ p = (char *)malloc(sizeof(rec) + key.dsize + dbuf.dsize);
+ if (!p) {
+ tdb->ecode = TDB_ERR_OOM;
+ goto fail;
+ }
+
+ memcpy(p, &rec, sizeof(rec));
+ memcpy(p+sizeof(rec), key.dptr, key.dsize);
+ memcpy(p+sizeof(rec)+key.dsize, dbuf.dptr, dbuf.dsize);
+
+ if (tdb_write(tdb, rec_ptr, p, sizeof(rec)+key.dsize+dbuf.dsize) == -1)
+ goto fail;
+
+ free(p);
+ p = NULL;
+
+ /* and point the top of the hash chain at it */
+ if (ofs_write(tdb, offset, &rec_ptr) == -1) goto fail;
+
+ tdb_unlock(tdb, BUCKET(hash));
+ return 0;
+
+ fail:
+#if TDB_DEBUG
+ printf("store failed for hash 0x%08x in bucket %u\n", hash, BUCKET(hash));
+#endif
+ if (p) free(p);
+ tdb_unlock(tdb, BUCKET(hash));
+ return -1;
+}
+
+
+/* open the database, creating it if necessary
+
+ The open_flags and mode are passed straight to the open call on the database
+ file. A flags value of O_WRONLY is invalid
+
+ The hash size is advisory, use zero for a default value.
+
+ return is NULL on error
+*/
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
+{
+ TDB_CONTEXT tdb, *ret;
+ struct stat st;
+
+ memset(&tdb, 0, sizeof(tdb));
+
+ tdb.fd = -1;
+ tdb.name = NULL;
+ tdb.map_ptr = NULL;
+
+ if ((open_flags & O_ACCMODE) == O_WRONLY) {
+ goto fail;
+ }
+
+ if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE;
+
+ tdb.read_only = ((open_flags & O_ACCMODE) == O_RDONLY);
+
+ if (name != NULL) {
+ tdb.fd = open(name, open_flags, mode);
+ if (tdb.fd == -1) {
+ goto fail;
+ }
+ }
+
+ /* ensure there is only one process initialising at once */
+ tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_SET, F_WRLCK, F_SETLKW);
+
+ if (tdb_flags & TDB_CLEAR_IF_FIRST) {
+ /* we need to zero the database if we are the only
+ one with it open */
+ if (tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_WRLCK, F_SETLK) == 0) {
+ ftruncate(tdb.fd, 0);
+ tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLK);
+ }
+ }
+
+ /* leave this lock in place */
+ tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_RDLCK, F_SETLKW);
+
+ if (read(tdb.fd, &tdb.header, sizeof(tdb.header)) != sizeof(tdb.header) ||
+ strcmp(tdb.header.magic_food, TDB_MAGIC_FOOD) != 0 ||
+ tdb.header.version != TDB_VERSION) {
+ /* its not a valid database - possibly initialise it */
+ if (!(open_flags & O_CREAT)) {
+ goto fail;
+ }
+ if (tdb_new_database(&tdb, hash_size) == -1) goto fail;
+
+ lseek(tdb.fd, 0, SEEK_SET);
+ if (tdb.fd != -1 && read(tdb.fd, &tdb.header,
+ sizeof(tdb.header)) !=
+ sizeof(tdb.header))
+ goto fail;
+ }
+
+ if (tdb.fd != -1) {
+ fstat(tdb.fd, &st);
+
+ /* map the database and fill in the return structure */
+ tdb.name = (char *)strdup(name);
+ tdb.map_size = st.st_size;
+ }
+
+ tdb.locked = (int *)calloc(tdb.header.hash_size+1,
+ sizeof(tdb.locked[0]));
+ if (!tdb.locked) {
+ goto fail;
+ }
+
+#if HAVE_MMAP
+ if (tdb.fd != -1) {
+ tdb.map_ptr = (void *)mmap(NULL, st.st_size,
+ tdb.read_only? PROT_READ : PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_FILE, tdb.fd, 0);
+ }
+#endif
+
+ ret = (TDB_CONTEXT *)malloc(sizeof(tdb));
+ if (!ret) goto fail;
+
+ *ret = tdb;
+
+#if TDB_DEBUG
+ printf("mapped database of hash_size %u map_size=%u\n",
+ hash_size, tdb.map_size);
+#endif
+
+ tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLKW);
+ return ret;
+
+ fail:
+ if (tdb.name) free(tdb.name);
+ if (tdb.fd != -1) close(tdb.fd);
+ if (tdb.map_ptr) munmap(tdb.map_ptr, tdb.map_size);
+
+ return NULL;
+}
+
+/* close a database */
+int tdb_close(TDB_CONTEXT *tdb)
+{
+ if (!tdb) return -1;
+
+ if (tdb->name) free(tdb->name);
+ if (tdb->fd != -1) close(tdb->fd);
+ if (tdb->locked) free(tdb->locked);
+
+ if (tdb->map_ptr) {
+ if (tdb->fd != -1) {
+ munmap(tdb->map_ptr, tdb->map_size);
+ } else {
+ free(tdb->map_ptr);
+ }
+ }
+
+ memset(tdb, 0, sizeof(*tdb));
+ free(tdb);
+
+ return 0;
+}
+
+/* lock the database. If we already have it locked then don't do anything */
+int tdb_writelock(TDB_CONTEXT *tdb)
+{
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_writelock() called with null context\n");
+#endif
+ return -1;
+ }
+
+ return tdb_lock(tdb, -1);
+}
+
+/* unlock the database. */
+int tdb_writeunlock(TDB_CONTEXT *tdb)
+{
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_writeunlock() called with null context\n");
+#endif
+ return -1;
+ }
+
+ return tdb_unlock(tdb, -1);
+}
+
+/* lock one hash chain. This is meant to be used to reduce locking
+ contention - it cannot guarantee how many records will be locked */
+int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_lockchain() called with null context\n");
+#endif
+ return -1;
+ }
+
+ return tdb_lock(tdb, BUCKET(tdb_hash(&key)));
+}
+
+
+/* unlock one hash chain */
+int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_unlockchain() called with null context\n");
+#endif
+ return -1;
+ }
+
+ return tdb_unlock(tdb, BUCKET(tdb_hash(&key)));
+}
diff --git a/mdk-stage1/ppp/pppd/tdb.h b/mdk-stage1/ppp/pppd/tdb.h
new file mode 100644
index 000000000..56ae0ac2a
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/tdb.h
@@ -0,0 +1,77 @@
+#define STANDALONE 1
+/*
+ * Database functions
+ * Copyright (C) Andrew Tridgell 1999
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms AND provided that this software or
+ * any derived work is only used as part of the PPP daemon (pppd)
+ * and related utilities.
+ * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Note: this software is also available under the Gnu Public License
+ * version 2 or later.
+ */
+
+typedef unsigned tdb_len;
+typedef unsigned tdb_off;
+
+#define TDB_MAGIC_FOOD "TDB file\n"
+
+/* this is stored at the front of every database */
+struct tdb_header {
+ char magic_food[32]; /* for /etc/magic */
+ unsigned version; /* version of the code */
+ unsigned hash_size; /* number of hash entries */
+};
+
+typedef struct {
+ char *dptr;
+ size_t dsize;
+} TDB_DATA;
+
+/* this is the context structure that is returned from a db open */
+typedef struct {
+ char *name; /* the name of the database */
+ void *map_ptr; /* where it is currently mapped */
+ int fd; /* open file descriptor for the database */
+ tdb_len map_size; /* how much space has been mapped */
+ int read_only; /* opened read-only */
+ int *locked; /* set if we have a chain locked */
+ int ecode; /* error code for last tdb error */
+ struct tdb_header header; /* a cached copy of the header */
+} TDB_CONTEXT;
+
+/* flags to tdb_store() */
+#define TDB_REPLACE 1
+#define TDB_INSERT 2
+
+/* flags for tdb_open() */
+#define TDB_CLEAR_IF_FIRST 1
+
+/* error codes */
+enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK,
+ TDB_ERR_OOM, TDB_ERR_EXISTS};
+
+#if STANDALONE
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode);
+char *tdb_error(TDB_CONTEXT *tdb);
+int tdb_writelock(TDB_CONTEXT *tdb);
+int tdb_writeunlock(TDB_CONTEXT *tdb);
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+int tdb_close(TDB_CONTEXT *tdb);
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_traverse(TDB_CONTEXT *tdb,
+ int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state),
+ void *state);
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+#endif
diff --git a/mdk-stage1/ppp/pppd/tty.c b/mdk-stage1/ppp/pppd/tty.c
new file mode 100644
index 000000000..4db707968
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/tty.c
@@ -0,0 +1,1164 @@
+/*
+ * tty.c - code for handling serial ports in pppd.
+ *
+ * Copyright (C) 2000 Paul Mackerras.
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+
+void tty_process_extra_options __P((void));
+void tty_check_options __P((void));
+int connect_tty __P((void));
+void disconnect_tty __P((void));
+void tty_close_fds __P((void));
+void cleanup_tty __P((void));
+void tty_do_send_config __P((int, u_int32_t, int, int));
+
+static int setdevname __P((char *, char **, int));
+static int setspeed __P((char *, char **, int));
+static int setxonxoff __P((char **));
+static int setescape __P((char **));
+static void printescape __P((option_t *, void (*)(void *, char *,...),void *));
+static void finish_tty __P((void));
+static int start_charshunt __P((int, int));
+static void stop_charshunt __P((void *, int));
+static void charshunt_done __P((void *));
+static void charshunt __P((int, int, char *));
+static int record_write __P((FILE *, int code, u_char *buf, int nb,
+ struct timeval *));
+static int open_socket __P((char *));
+static void maybe_relock __P((void *, int));
+
+static int pty_master; /* fd for master side of pty */
+static int pty_slave; /* fd for slave side of pty */
+static int real_ttyfd; /* fd for actual serial port (not pty) */
+static int ttyfd; /* Serial port file descriptor */
+static char speed_str[16]; /* Serial port speed as string */
+
+mode_t tty_mode = (mode_t)-1; /* Original access permissions to tty */
+int baud_rate; /* Actual bits/second for serial device */
+char *callback_script; /* script for doing callback */
+int charshunt_pid; /* Process ID for charshunt */
+int locked; /* lock() has succeeded */
+struct stat devstat; /* result of stat() on devnam */
+
+/* option variables */
+int crtscts = 0; /* Use hardware flow control */
+bool modem = 1; /* Use modem control lines */
+int inspeed = 0; /* Input/Output speed requested */
+bool lockflag = 0; /* Create lock file to lock the serial dev */
+char *initializer = NULL; /* Script to initialize physical link */
+char *connect_script = NULL; /* Script to establish physical link */
+char *disconnect_script = NULL; /* Script to disestablish physical link */
+char *welcomer = NULL; /* Script to run after phys link estab. */
+char *ptycommand = NULL; /* Command to run on other side of pty */
+bool notty = 0; /* Stdin/out is not a tty */
+char *record_file = NULL; /* File to record chars sent/received */
+int max_data_rate; /* max bytes/sec through charshunt */
+bool sync_serial = 0; /* Device is synchronous serial device */
+char *pty_socket = NULL; /* Socket to connect to pty */
+int using_pty = 0; /* we're allocating a pty as the device */
+
+extern uid_t uid;
+extern int kill_link;
+
+/* XXX */
+extern int privopen; /* don't lock, open device as root */
+
+u_int32_t xmit_accm[8]; /* extended transmit ACCM */
+
+/* option descriptors */
+option_t tty_options[] = {
+ /* device name must be first, or change connect_tty() below! */
+ { "device name", o_wild, (void *) &setdevname,
+ "Serial port device name",
+ OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC,
+ devnam},
+
+ { "tty speed", o_wild, (void *) &setspeed,
+ "Baud rate for serial port",
+ OPT_PRIO | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, speed_str },
+
+ { "lock", o_bool, &lockflag,
+ "Lock serial device with UUCP-style lock file", OPT_PRIO | 1 },
+ { "nolock", o_bool, &lockflag,
+ "Don't lock serial device", OPT_PRIOSUB | OPT_PRIV },
+
+ { "init", o_string, &initializer,
+ "A program to initialize the device", OPT_PRIO | OPT_PRIVFIX },
+
+ { "connect", o_string, &connect_script,
+ "A program to set up a connection", OPT_PRIO | OPT_PRIVFIX },
+
+ { "disconnect", o_string, &disconnect_script,
+ "Program to disconnect serial device", OPT_PRIO | OPT_PRIVFIX },
+
+ { "welcome", o_string, &welcomer,
+ "Script to welcome client", OPT_PRIO | OPT_PRIVFIX },
+
+ { "pty", o_string, &ptycommand,
+ "Script to run on pseudo-tty master side",
+ OPT_PRIO | OPT_PRIVFIX | OPT_DEVNAM },
+
+ { "notty", o_bool, &notty,
+ "Input/output is not a tty", OPT_DEVNAM | 1 },
+
+ { "socket", o_string, &pty_socket,
+ "Send and receive over socket, arg is host:port",
+ OPT_PRIO | OPT_DEVNAM },
+
+ { "record", o_string, &record_file,
+ "Record characters sent/received to file", OPT_PRIO },
+
+ { "crtscts", o_int, &crtscts,
+ "Set hardware (RTS/CTS) flow control",
+ OPT_PRIO | OPT_NOARG | OPT_VAL(1) },
+ { "cdtrcts", o_int, &crtscts,
+ "Set alternate hardware (DTR/CTS) flow control",
+ OPT_PRIOSUB | OPT_NOARG | OPT_VAL(2) },
+ { "nocrtscts", o_int, &crtscts,
+ "Disable hardware flow control",
+ OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) },
+ { "-crtscts", o_int, &crtscts,
+ "Disable hardware flow control",
+ OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
+ { "nocdtrcts", o_int, &crtscts,
+ "Disable hardware flow control",
+ OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
+ { "xonxoff", o_special_noarg, (void *)setxonxoff,
+ "Set software (XON/XOFF) flow control", OPT_PRIOSUB },
+
+ { "modem", o_bool, &modem,
+ "Use modem control lines", OPT_PRIO | 1 },
+ { "local", o_bool, &modem,
+ "Don't use modem control lines", OPT_PRIOSUB | 0 },
+
+ { "sync", o_bool, &sync_serial,
+ "Use synchronous HDLC serial encoding", 1 },
+
+ { "datarate", o_int, &max_data_rate,
+ "Maximum data rate in bytes/sec (with pty, notty or record option)",
+ OPT_PRIO },
+
+ { "escape", o_special, (void *)setescape,
+ "List of character codes to escape on transmission",
+ OPT_A2PRINTER, (void *)printescape },
+
+ { NULL }
+};
+
+
+struct channel tty_channel = {
+ tty_options,
+ &tty_process_extra_options,
+ &tty_check_options,
+ &connect_tty,
+ &disconnect_tty,
+ &tty_establish_ppp,
+ &tty_disestablish_ppp,
+ &tty_do_send_config,
+ &tty_recv_config,
+ &cleanup_tty,
+ &tty_close_fds
+};
+
+/*
+ * setspeed - Set the serial port baud rate.
+ * If doit is 0, the call is to check whether this option is
+ * potentially a speed value.
+ */
+static int
+setspeed(arg, argv, doit)
+ char *arg;
+ char **argv;
+ int doit;
+{
+ char *ptr;
+ int spd;
+
+ spd = strtol(arg, &ptr, 0);
+ if (ptr == arg || *ptr != 0 || spd == 0)
+ return 0;
+ if (doit) {
+ inspeed = spd;
+ slprintf(speed_str, sizeof(speed_str), "%d", spd);
+ }
+ return 1;
+}
+
+
+/*
+ * setdevname - Set the device name.
+ * If doit is 0, the call is to check whether this option is
+ * potentially a device name.
+ */
+static int
+setdevname(cp, argv, doit)
+ char *cp;
+ char **argv;
+ int doit;
+{
+ struct stat statbuf;
+ char dev[MAXPATHLEN];
+
+ if (*cp == 0)
+ return 0;
+
+ if (strncmp("/dev/", cp, 5) != 0) {
+ strlcpy(dev, "/dev/", sizeof(dev));
+ strlcat(dev, cp, sizeof(dev));
+ cp = dev;
+ }
+
+ /*
+ * Check if there is a character device by this name.
+ */
+ if (stat(cp, &statbuf) < 0) {
+ if (!doit)
+ return errno != ENOENT;
+ option_error("Couldn't stat %s: %m", cp);
+ return 0;
+ }
+ if (!S_ISCHR(statbuf.st_mode)) {
+ if (doit)
+ option_error("%s is not a character device", cp);
+ return 0;
+ }
+
+ if (doit) {
+ strlcpy(devnam, cp, sizeof(devnam));
+ devstat = statbuf;
+ default_device = 0;
+ }
+
+ return 1;
+}
+
+static int
+setxonxoff(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */
+ lcp_wantoptions[0].neg_asyncmap = 1;
+
+ crtscts = -2;
+ return 1;
+}
+
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+static int
+setescape(argv)
+ char **argv;
+{
+ int n, ret;
+ char *p, *endp;
+
+ p = *argv;
+ ret = 1;
+ while (*p) {
+ n = strtol(p, &endp, 16);
+ if (p == endp) {
+ option_error("escape parameter contains invalid hex number '%s'",
+ p);
+ return 0;
+ }
+ p = endp;
+ if (n < 0 || n == 0x5E || n > 0xFF) {
+ option_error("can't escape character 0x%x", n);
+ ret = 0;
+ } else
+ xmit_accm[n >> 5] |= 1 << (n & 0x1F);
+ while (*p == ',' || *p == ' ')
+ ++p;
+ }
+ lcp_allowoptions[0].asyncmap = xmit_accm[0];
+ return ret;
+}
+
+static void
+printescape(opt, printer, arg)
+ option_t *opt;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int n;
+ int first = 1;
+
+ for (n = 0; n < 256; ++n) {
+ if (n == 0x7d)
+ n += 2; /* skip 7d, 7e */
+ if (xmit_accm[n >> 5] & (1 << (n & 0x1f))) {
+ if (!first)
+ printer(arg, ",");
+ else
+ first = 0;
+ printer(arg, "%x", n);
+ }
+ }
+ if (first)
+ printer(arg, "oops # nothing escaped");
+}
+
+/*
+ * tty_init - do various tty-related initializations.
+ */
+void tty_init()
+{
+ add_notifier(&pidchange, maybe_relock, 0);
+ the_channel = &tty_channel;
+ xmit_accm[3] = 0x60000000;
+}
+
+/*
+ * tty_process_extra_options - work out which tty device we are using
+ * and read its options file.
+ */
+void tty_process_extra_options()
+{
+ using_pty = notty || ptycommand != NULL || pty_socket != NULL;
+ if (using_pty)
+ return;
+ if (default_device) {
+ char *p;
+ if (!isatty(0) || (p = ttyname(0)) == NULL) {
+ option_error("no device specified and stdin is not a tty");
+ exit(EXIT_OPTION_ERROR);
+ }
+ strlcpy(devnam, p, sizeof(devnam));
+ if (stat(devnam, &devstat) < 0)
+ fatal("Couldn't stat default device %s: %m", devnam);
+ }
+
+
+ /*
+ * Parse the tty options file.
+ * The per-tty options file should not change
+ * ptycommand, pty_socket, notty or devnam.
+ * options_for_tty doesn't override options set on the command line,
+ * except for some privileged options.
+ */
+ if (!options_for_tty())
+ exit(EXIT_OPTION_ERROR);
+}
+
+/*
+ * tty_check_options - do consistency checks on the options we were given.
+ */
+void
+tty_check_options()
+{
+ struct stat statbuf;
+ int fdflags;
+
+ if (demand && connect_script == 0) {
+ option_error("connect script is required for demand-dialling\n");
+ exit(EXIT_OPTION_ERROR);
+ }
+ /* default holdoff to 0 if no connect script has been given */
+ if (connect_script == 0 && !holdoff_specified)
+ holdoff = 0;
+
+ if (using_pty) {
+ if (!default_device) {
+ option_error("%s option precludes specifying device name",
+ notty? "notty": "pty");
+ exit(EXIT_OPTION_ERROR);
+ }
+ if (ptycommand != NULL && notty) {
+ option_error("pty option is incompatible with notty option");
+ exit(EXIT_OPTION_ERROR);
+ }
+ if (pty_socket != NULL && (ptycommand != NULL || notty)) {
+ option_error("socket option is incompatible with pty and notty");
+ exit(EXIT_OPTION_ERROR);
+ }
+ default_device = notty;
+ lockflag = 0;
+ modem = 0;
+ if (notty && log_to_fd <= 1)
+ log_to_fd = -1;
+ } else {
+ /*
+ * If the user has specified a device which is the same as
+ * the one on stdin, pretend they didn't specify any.
+ * If the device is already open read/write on stdin,
+ * we assume we don't need to lock it, and we can open it
+ * as root.
+ */
+ if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode)
+ && statbuf.st_rdev == devstat.st_rdev) {
+ default_device = 1;
+ fdflags = fcntl(0, F_GETFL);
+ if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR)
+ privopen = 1;
+ }
+ }
+ if (default_device)
+ nodetach = 1;
+
+ /*
+ * Don't send log messages to the serial port, it tends to
+ * confuse the peer. :-)
+ */
+ if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0
+ && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
+ log_to_fd = -1;
+}
+
+/*
+ * connect_tty - get the serial port ready to start doing PPP.
+ * That is, open the serial port, set its speed and mode, and run
+ * the connector and/or welcomer.
+ */
+int connect_tty()
+{
+ char *connector;
+ int fdflags;
+ struct stat statbuf;
+ char numbuf[16];
+
+ /*
+ * Get a pty master/slave pair if the pty, notty, socket,
+ * or record options were specified.
+ */
+ strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+ pty_master = -1;
+ pty_slave = -1;
+ real_ttyfd = -1;
+ if (using_pty || record_file != NULL) {
+ if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
+ error("Couldn't allocate pseudo-tty");
+ status = EXIT_FATAL_ERROR;
+ return -1;
+ }
+ set_up_tty(pty_slave, 1);
+ }
+
+ /*
+ * Lock the device if we've been asked to.
+ */
+ status = EXIT_LOCK_FAILED;
+ if (lockflag && !privopen) {
+ if (lock(devnam) < 0)
+ return -1;
+ 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.
+ */
+ 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_IWGRP | 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));
+ }
+
+ /*
+ * If the pty, socket, notty and/or record option was specified,
+ * start up the character shunt now.
+ */
+ status = EXIT_PTYCMD_FAILED;
+ if (ptycommand != NULL) {
+ if (record_file != NULL) {
+ int ipipe[2], opipe[2], ok;
+
+ if (pipe(ipipe) < 0 || pipe(opipe) < 0)
+ fatal("Couldn't create pipes for record option: %m");
+ ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0
+ && start_charshunt(ipipe[0], opipe[1]);
+ close(ipipe[0]);
+ close(ipipe[1]);
+ close(opipe[0]);
+ close(opipe[1]);
+ if (!ok)
+ return -1;
+ } else {
+ if (device_script(ptycommand, pty_master, pty_master, 1) < 0)
+ return -1;
+ ttyfd = pty_slave;
+ close(pty_master);
+ pty_master = -1;
+ }
+ } else if (pty_socket != NULL) {
+ int fd = open_socket(pty_socket);
+ if (fd < 0)
+ return -1;
+ if (!start_charshunt(fd, fd))
+ return -1;
+ } else if (notty) {
+ if (!start_charshunt(0, 1))
+ return -1;
+ } else if (record_file != NULL) {
+ if (!start_charshunt(ttyfd, ttyfd))
+ return -1;
+ }
+
+ /* run connection script */
+ if ((connector && connector[0]) || initializer) {
+ if (real_ttyfd != -1) {
+ /* XXX do this if doing_callback == CALLBACK_DIALIN? */
+ if (!default_device && modem) {
+ setdtr(real_ttyfd, 0); /* in case modem is off hook */
+ sleep(1);
+ setdtr(real_ttyfd, 1);
+ }
+ }
+
+ if (initializer && initializer[0]) {
+ if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
+ error("Initializer script failed");
+ status = EXIT_INIT_FAILED;
+ return -1;
+ }
+ if (kill_link) {
+ disconnect_tty();
+ return -1;
+ }
+ info("Serial port initialized.");
+ }
+
+ if (connector && connector[0]) {
+ if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
+ error("Connect script failed");
+ status = EXIT_CONNECT_FAILED;
+ return -1;
+ }
+ if (kill_link) {
+ disconnect_tty();
+ return -1;
+ }
+ info("Serial connection established.");
+ }
+
+ /* set line speed, flow control, etc.;
+ clear CLOCAL if modem option */
+ if (real_ttyfd != -1)
+ set_up_tty(real_ttyfd, 0);
+
+ if (doing_callback == CALLBACK_DIALIN)
+ connector = NULL;
+ }
+
+ /* reopen tty if necessary to wait for carrier */
+ if (connector == NULL && modem && devnam[0] != 0) {
+ int i;
+ for (;;) {
+ if ((i = open(devnam, O_RDWR)) >= 0)
+ break;
+ if (errno != EINTR) {
+ error("Failed to reopen %s: %m", devnam);
+ status = EXIT_OPEN_FAILED;
+ }
+ if (!persist || errno != EINTR || hungup || kill_link)
+ return -1;
+ }
+ close(i);
+ }
+
+ slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
+ script_setenv("SPEED", numbuf, 0);
+
+ /* run welcome script, if any */
+ if (welcomer && welcomer[0]) {
+ if (device_script(welcomer, ttyfd, ttyfd, 0) < 0)
+ warn("Welcome script failed");
+ }
+
+ /*
+ * 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.
+ */
+ if (connector != NULL || ptycommand != NULL)
+ listen_time = connect_delay;
+
+ return ttyfd;
+}
+
+
+void disconnect_tty()
+{
+ if (disconnect_script == NULL || hungup)
+ return;
+ if (real_ttyfd >= 0)
+ set_up_tty(real_ttyfd, 1);
+ if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) {
+ warn("disconnect script failed");
+ } else {
+ info("Serial link disconnected.");
+ }
+}
+
+void tty_close_fds()
+{
+ if (pty_master >= 0)
+ close(pty_master);
+ if (pty_slave >= 0)
+ close(pty_slave);
+ if (real_ttyfd >= 0) {
+ close(real_ttyfd);
+ real_ttyfd = -1;
+ }
+ /* N.B. ttyfd will == either pty_slave or real_ttyfd */
+}
+
+void cleanup_tty()
+{
+ if (real_ttyfd >= 0)
+ finish_tty();
+ tty_close_fds();
+ if (locked) {
+ unlock();
+ locked = 0;
+ }
+}
+
+/*
+ * tty_do_send_config - set transmit-side PPP configuration.
+ * We set the extended transmit ACCM here as well.
+ */
+void
+tty_do_send_config(mtu, accm, pcomp, accomp)
+ int mtu;
+ u_int32_t accm;
+ int pcomp, accomp;
+{
+ tty_set_xaccm(xmit_accm);
+ tty_send_config(mtu, accm, pcomp, accomp);
+}
+
+/*
+ * finish_tty - restore the terminal device to its original settings
+ */
+static void
+finish_tty()
+{
+ /* drop dtr to hang up */
+ if (!default_device && modem) {
+ setdtr(real_ttyfd, 0);
+ /*
+ * This sleep is in case the serial port has CLOCAL set by default,
+ * and consequently will reassert DTR when we close the device.
+ */
+ sleep(1);
+ }
+
+ restore_tty(real_ttyfd);
+
+ if (tty_mode != (mode_t) -1) {
+ if (fchmod(real_ttyfd, tty_mode) != 0) {
+ /* XXX if devnam is a symlink, this will change the link */
+ chmod(devnam, tty_mode);
+ }
+ }
+
+ close(real_ttyfd);
+ real_ttyfd = -1;
+}
+
+/*
+ * maybe_relock - our PID has changed, maybe update the lock file.
+ */
+static void
+maybe_relock(arg, pid)
+ void *arg;
+ int pid;
+{
+ if (locked)
+ relock(pid);
+}
+
+/*
+ * open_socket - establish a stream socket connection to the nominated
+ * host and port.
+ */
+static int
+open_socket(dest)
+ char *dest;
+{
+ char *sep, *endp = NULL;
+ int sock, port = -1;
+ u_int32_t host;
+ struct hostent *hent;
+ struct sockaddr_in sad;
+
+ /* parse host:port and resolve host to an IP address */
+ sep = strchr(dest, ':');
+ if (sep != NULL)
+ port = strtol(sep+1, &endp, 10);
+ if (port < 0 || endp == sep+1 || sep == dest) {
+ error("Can't parse host:port for socket destination");
+ return -1;
+ }
+ *sep = 0;
+ host = inet_addr(dest);
+ if (host == (u_int32_t) -1) {
+ hent = gethostbyname(dest);
+ if (hent == NULL) {
+ error("%s: unknown host in socket option", dest);
+ *sep = ':';
+ return -1;
+ }
+ host = *(u_int32_t *)(hent->h_addr_list[0]);
+ }
+ *sep = ':';
+
+ /* get a socket and connect it to the other end */
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ error("Can't create socket: %m");
+ return -1;
+ }
+ memset(&sad, 0, sizeof(sad));
+ sad.sin_family = AF_INET;
+ sad.sin_port = htons(port);
+ sad.sin_addr.s_addr = host;
+ if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
+ error("Can't connect to %s: %m", dest);
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+
+/*
+ * start_charshunt - create a child process to run the character shunt.
+ */
+static int
+start_charshunt(ifd, ofd)
+ int ifd, ofd;
+{
+ int cpid;
+
+ cpid = fork();
+ if (cpid == -1) {
+ error("Can't fork process for character shunt: %m");
+ return 0;
+ }
+ if (cpid == 0) {
+ /* child */
+ close(pty_slave);
+ setuid(uid);
+ if (getuid() != uid)
+ fatal("setuid failed");
+ setgid(getgid());
+ if (!nodetach)
+ log_to_fd = -1;
+ charshunt(ifd, ofd, record_file);
+ exit(0);
+ }
+ charshunt_pid = cpid;
+ add_notifier(&sigreceived, stop_charshunt, 0);
+ close(pty_master);
+ pty_master = -1;
+ ttyfd = pty_slave;
+ record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
+ return 1;
+}
+
+static void
+charshunt_done(arg)
+ void *arg;
+{
+ charshunt_pid = 0;
+}
+
+static void
+stop_charshunt(arg, sig)
+ void *arg;
+ int sig;
+{
+ if (charshunt_pid)
+ kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM));
+}
+
+/*
+ * charshunt - the character shunt, which passes characters between
+ * the pty master side and the serial port (or stdin/stdout).
+ * This runs as the user (not as root).
+ * (We assume ofd >= ifd which is true the way this gets called. :-).
+ */
+static void
+charshunt(ifd, ofd, record_file)
+ int ifd, ofd;
+ char *record_file;
+{
+ int n, nfds;
+ fd_set ready, writey;
+ u_char *ibufp, *obufp;
+ int nibuf, nobuf;
+ int flags;
+ int pty_readable, stdin_readable;
+ struct timeval lasttime;
+ FILE *recordf = NULL;
+ int ilevel, olevel, max_level;
+ struct timeval levelt, tout, *top;
+ extern u_char inpacket_buf[];
+
+ /*
+ * Reset signal handlers.
+ */
+ signal(SIGHUP, SIG_IGN); /* Hangup */
+ signal(SIGINT, SIG_DFL); /* Interrupt */
+ signal(SIGTERM, SIG_DFL); /* Terminate */
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGUSR1, SIG_DFL);
+ signal(SIGUSR2, SIG_DFL);
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGSEGV, SIG_DFL);
+#ifdef SIGBUS
+ signal(SIGBUS, SIG_DFL);
+#endif
+#ifdef SIGEMT
+ signal(SIGEMT, SIG_DFL);
+#endif
+#ifdef SIGPOLL
+ signal(SIGPOLL, SIG_DFL);
+#endif
+#ifdef SIGPROF
+ signal(SIGPROF, SIG_DFL);
+#endif
+#ifdef SIGSYS
+ signal(SIGSYS, SIG_DFL);
+#endif
+#ifdef SIGTRAP
+ signal(SIGTRAP, SIG_DFL);
+#endif
+#ifdef SIGVTALRM
+ signal(SIGVTALRM, SIG_DFL);
+#endif
+#ifdef SIGXCPU
+ signal(SIGXCPU, SIG_DFL);
+#endif
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, SIG_DFL);
+#endif
+
+ /*
+ * Open the record file if required.
+ */
+ if (record_file != NULL) {
+ recordf = fopen(record_file, "a");
+ if (recordf == NULL)
+ error("Couldn't create record file %s: %m", record_file);
+ }
+
+ /* set all the fds to non-blocking mode */
+ flags = fcntl(pty_master, F_GETFL);
+ if (flags == -1
+ || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set pty master to nonblock: %m");
+ flags = fcntl(ifd, F_GETFL);
+ if (flags == -1
+ || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
+ if (ofd != ifd) {
+ flags = fcntl(ofd, F_GETFL);
+ if (flags == -1
+ || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set stdout to nonblock: %m");
+ }
+
+ nibuf = nobuf = 0;
+ ibufp = obufp = NULL;
+ pty_readable = stdin_readable = 1;
+
+ ilevel = olevel = 0;
+ gettimeofday(&levelt, NULL);
+ if (max_data_rate) {
+ max_level = max_data_rate / 10;
+ if (max_level < 100)
+ max_level = 100;
+ } else
+ max_level = PPP_MRU + PPP_HDRLEN + 1;
+
+ nfds = (ofd > pty_master? ofd: pty_master) + 1;
+ if (recordf != NULL) {
+ gettimeofday(&lasttime, NULL);
+ putc(7, recordf); /* put start marker */
+ putc(lasttime.tv_sec >> 24, recordf);
+ putc(lasttime.tv_sec >> 16, recordf);
+ putc(lasttime.tv_sec >> 8, recordf);
+ putc(lasttime.tv_sec, recordf);
+ lasttime.tv_usec = 0;
+ }
+
+ while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
+ top = 0;
+ tout.tv_sec = 0;
+ tout.tv_usec = 10000;
+ FD_ZERO(&ready);
+ FD_ZERO(&writey);
+ if (nibuf != 0) {
+ if (ilevel >= max_level)
+ top = &tout;
+ else
+ FD_SET(pty_master, &writey);
+ } else if (stdin_readable)
+ FD_SET(ifd, &ready);
+ if (nobuf != 0) {
+ if (olevel >= max_level)
+ top = &tout;
+ else
+ FD_SET(ofd, &writey);
+ } else if (pty_readable)
+ FD_SET(pty_master, &ready);
+ if (select(nfds, &ready, &writey, NULL, top) < 0) {
+ if (errno != EINTR)
+ fatal("select");
+ continue;
+ }
+ if (max_data_rate) {
+ double dt;
+ int nbt;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+ dt = (now.tv_sec - levelt.tv_sec
+ + (now.tv_usec - levelt.tv_usec) / 1e6);
+ nbt = (int)(dt * max_data_rate);
+ ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
+ olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
+ levelt = now;
+ } else
+ ilevel = olevel = 0;
+ if (FD_ISSET(ifd, &ready)) {
+ ibufp = inpacket_buf;
+ nibuf = read(ifd, ibufp, PPP_MRU + PPP_HDRLEN);
+ if (nibuf < 0 && errno == EIO)
+ nibuf = 0;
+ if (nibuf < 0) {
+ if (!(errno == EINTR || errno == EAGAIN)) {
+ error("Error reading standard input: %m");
+ break;
+ }
+ nibuf = 0;
+ } else if (nibuf == 0) {
+ /* end of file from stdin */
+ stdin_readable = 0;
+ /* do a 0-length write, hopefully this will generate
+ an EOF (hangup) on the slave side. */
+ write(pty_master, inpacket_buf, 0);
+ if (recordf)
+ if (!record_write(recordf, 4, NULL, 0, &lasttime))
+ recordf = NULL;
+ } else {
+ FD_SET(pty_master, &writey);
+ if (recordf)
+ if (!record_write(recordf, 2, ibufp, nibuf, &lasttime))
+ recordf = NULL;
+ }
+ }
+ if (FD_ISSET(pty_master, &ready)) {
+ obufp = outpacket_buf;
+ nobuf = read(pty_master, obufp, PPP_MRU + PPP_HDRLEN);
+ if (nobuf < 0 && errno == EIO)
+ nobuf = 0;
+ if (nobuf < 0) {
+ if (!(errno == EINTR || errno == EAGAIN)) {
+ error("Error reading pseudo-tty master: %m");
+ break;
+ }
+ nobuf = 0;
+ } else if (nobuf == 0) {
+ /* end of file from the pty - slave side has closed */
+ pty_readable = 0;
+ stdin_readable = 0; /* pty is not writable now */
+ nibuf = 0;
+ close(ofd);
+ if (recordf)
+ if (!record_write(recordf, 3, NULL, 0, &lasttime))
+ recordf = NULL;
+ } else {
+ FD_SET(ofd, &writey);
+ if (recordf)
+ if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
+ recordf = NULL;
+ }
+ }
+ if (FD_ISSET(ofd, &writey)) {
+ n = nobuf;
+ if (olevel + n > max_level)
+ n = max_level - olevel;
+ n = write(ofd, obufp, n);
+ if (n < 0) {
+ if (errno == EIO) {
+ pty_readable = 0;
+ nobuf = 0;
+ } else if (errno != EAGAIN && errno != EINTR) {
+ error("Error writing standard output: %m");
+ break;
+ }
+ } else {
+ obufp += n;
+ nobuf -= n;
+ olevel += n;
+ }
+ }
+ if (FD_ISSET(pty_master, &writey)) {
+ n = nibuf;
+ if (ilevel + n > max_level)
+ n = max_level - ilevel;
+ n = write(pty_master, ibufp, n);
+ if (n < 0) {
+ if (errno == EIO) {
+ stdin_readable = 0;
+ nibuf = 0;
+ } else if (errno != EAGAIN && errno != EINTR) {
+ error("Error writing pseudo-tty master: %m");
+ break;
+ }
+ } else {
+ ibufp += n;
+ nibuf -= n;
+ ilevel += n;
+ }
+ }
+ }
+ exit(0);
+}
+
+static int
+record_write(f, code, buf, nb, tp)
+ FILE *f;
+ int code;
+ u_char *buf;
+ int nb;
+ struct timeval *tp;
+{
+ struct timeval now;
+ int diff;
+
+ gettimeofday(&now, NULL);
+ now.tv_usec /= 100000; /* actually 1/10 s, not usec now */
+ diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec);
+ if (diff > 0) {
+ if (diff > 255) {
+ putc(5, f);
+ putc(diff >> 24, f);
+ putc(diff >> 16, f);
+ putc(diff >> 8, f);
+ putc(diff, f);
+ } else {
+ putc(6, f);
+ putc(diff, f);
+ }
+ *tp = now;
+ }
+ putc(code, f);
+ if (buf != NULL) {
+ putc(nb >> 8, f);
+ putc(nb, f);
+ fwrite(buf, nb, 1, f);
+ }
+ fflush(f);
+ if (ferror(f)) {
+ error("Error writing record file: %m");
+ return 0;
+ }
+ return 1;
+}
diff --git a/mdk-stage1/ppp/pppd/upap.c b/mdk-stage1/ppp/pppd/upap.c
new file mode 100644
index 000000000..bd569fb74
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/upap.c
@@ -0,0 +1,640 @@
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pppd.h"
+#include "upap.h"
+
+static const char rcsid[] = RCSID;
+
+static bool hide_password = 1;
+
+/*
+ * Command-line options.
+ */
+static option_t pap_option_list[] = {
+ { "hide-password", o_bool, &hide_password,
+ "Don't output passwords to log", OPT_PRIO | 1 },
+ { "show-password", o_bool, &hide_password,
+ "Show password string in debug log messages", OPT_PRIOSUB | 0 },
+
+ { "pap-restart", o_int, &upap[0].us_timeouttime,
+ "Set retransmit timeout for PAP", OPT_PRIO },
+ { "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
+ "Set max number of transmissions for auth-reqs", OPT_PRIO },
+ { "pap-timeout", o_int, &upap[0].us_reqtimeout,
+ "Set time limit for peer PAP authentication", OPT_PRIO },
+
+ { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init __P((int));
+static void upap_lowerup __P((int));
+static void upap_lowerdown __P((int));
+static void upap_input __P((int, u_char *, int));
+static void upap_protrej __P((int));
+static int upap_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+
+struct protent pap_protent = {
+ PPP_PAP,
+ upap_init,
+ upap_input,
+ upap_protrej,
+ upap_lowerup,
+ upap_lowerdown,
+ NULL,
+ NULL,
+ upap_printpkt,
+ NULL,
+ 1,
+ "PAP",
+ NULL,
+ pap_option_list,
+ NULL,
+ NULL,
+ NULL
+};
+
+upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */
+
+static void upap_timeout __P((void *));
+static void upap_reqtimeout __P((void *));
+static void upap_rauthreq __P((upap_state *, u_char *, int, int));
+static void upap_rauthack __P((upap_state *, u_char *, int, int));
+static void upap_rauthnak __P((upap_state *, u_char *, int, int));
+static void upap_sauthreq __P((upap_state *));
+static void upap_sresp __P((upap_state *, int, int, char *, int));
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void
+upap_init(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ u->us_unit = unit;
+ u->us_user = NULL;
+ u->us_userlen = 0;
+ u->us_passwd = NULL;
+ u->us_passwdlen = 0;
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+ u->us_id = 0;
+ u->us_timeouttime = UPAP_DEFTIMEOUT;
+ u->us_maxtransmits = 10;
+ u->us_reqtimeout = UPAP_DEFREQTIME;
+}
+
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(unit, user, password)
+ int unit;
+ char *user, *password;
+{
+ upap_state *u = &upap[unit];
+
+ /* Save the username and password we're given */
+ u->us_user = user;
+ u->us_userlen = strlen(user);
+ u->us_passwd = password;
+ u->us_passwdlen = strlen(password);
+ u->us_transmits = 0;
+
+ /* Lower layer up yet? */
+ if (u->us_clientstate == UPAPCS_INITIAL ||
+ u->us_clientstate == UPAPCS_PENDING) {
+ u->us_clientstate = UPAPCS_PENDING;
+ return;
+ }
+
+ upap_sauthreq(u); /* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ /* Lower layer up yet? */
+ if (u->us_serverstate == UPAPSS_INITIAL ||
+ u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_PENDING;
+ return;
+ }
+
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+}
+
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void
+upap_timeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ)
+ return;
+
+ if (u->us_transmits >= u->us_maxtransmits) {
+ /* give up in disgust */
+ error("No response to PAP authenticate-requests");
+ u->us_clientstate = UPAPCS_BADAUTH;
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+ return;
+ }
+
+ upap_sauthreq(u); /* Send Authenticate-Request */
+}
+
+
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void
+upap_reqtimeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_serverstate != UPAPSS_LISTEN)
+ return; /* huh?? */
+
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ u->us_serverstate = UPAPSS_BADAUTH;
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void
+upap_lowerup(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_INITIAL)
+ u->us_clientstate = UPAPCS_CLOSED;
+ else if (u->us_clientstate == UPAPCS_PENDING) {
+ upap_sauthreq(u); /* send an auth-request */
+ }
+
+ if (u->us_serverstate == UPAPSS_INITIAL)
+ u->us_serverstate = UPAPSS_CLOSED;
+ else if (u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+upap_lowerdown(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. In any case, pretend lower layer went down.
+ */
+static void
+upap_protrej(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) {
+ error("PAP authentication failed due to protocol-reject");
+ auth_withpeer_fail(unit, PPP_PAP);
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN) {
+ error("PAP authentication of peer failed (protocol-reject)");
+ auth_peer_fail(unit, PPP_PAP);
+ }
+ upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void
+upap_input(unit, inpacket, l)
+ int unit;
+ u_char *inpacket;
+ int l;
+{
+ upap_state *u = &upap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < UPAP_HEADERLEN) {
+ UPAPDEBUG(("pap_input: rcvd short header."));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < UPAP_HEADERLEN) {
+ UPAPDEBUG(("pap_input: rcvd illegal length."));
+ return;
+ }
+ if (len > l) {
+ UPAPDEBUG(("pap_input: rcvd short packet."));
+ return;
+ }
+ len -= UPAP_HEADERLEN;
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case UPAP_AUTHREQ:
+ upap_rauthreq(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHACK:
+ upap_rauthack(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHNAK:
+ upap_rauthnak(u, inp, id, len);
+ break;
+
+ default: /* XXX Need code reject */
+ break;
+ }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char ruserlen, rpasswdlen;
+ char *ruser, *rpasswd;
+ int retcode;
+ char *msg;
+ int msglen;
+
+ if (u->us_serverstate < UPAPSS_LISTEN)
+ return;
+
+ /*
+ * If we receive a duplicate authenticate-request, we are
+ * supposed to return the same status as for the first request.
+ */
+ if (u->us_serverstate == UPAPSS_OPEN) {
+ upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
+ return;
+ }
+ if (u->us_serverstate == UPAPSS_BADAUTH) {
+ upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
+ return;
+ }
+
+ /*
+ * Parse user/passwd.
+ */
+ if (len < 1) {
+ UPAPDEBUG(("pap_rauth: rcvd short packet."));
+ return;
+ }
+ GETCHAR(ruserlen, inp);
+ len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+ if (len < 0) {
+ UPAPDEBUG(("pap_rauth: rcvd short packet."));
+ return;
+ }
+ ruser = (char *) inp;
+ INCPTR(ruserlen, inp);
+ GETCHAR(rpasswdlen, inp);
+ if (len < rpasswdlen) {
+ UPAPDEBUG(("pap_rauth: rcvd short packet."));
+ return;
+ }
+ rpasswd = (char *) inp;
+
+ /*
+ * Check the username and password given.
+ */
+ retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
+ rpasswdlen, &msg);
+ BZERO(rpasswd, rpasswdlen);
+ msglen = strlen(msg);
+ if (msglen > 255)
+ msglen = 255;
+
+ upap_sresp(u, retcode, id, msg, msglen);
+
+ if (retcode == UPAP_AUTHACK) {
+ u->us_serverstate = UPAPSS_OPEN;
+ auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+ } else {
+ u->us_serverstate = UPAPSS_BADAUTH;
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ }
+
+ if (u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void
+upap_rauthack(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < 1) {
+ UPAPDEBUG(("pap_rauthack: ignoring missing msg-length."));
+ } else {
+ GETCHAR(msglen, inp);
+ if (msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(("pap_rauthack: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+
+ u->us_clientstate = UPAPCS_OPEN;
+
+ auth_withpeer_success(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nakk.
+ */
+static void
+upap_rauthnak(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < 1) {
+ UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length."));
+ } else {
+ GETCHAR(msglen, inp);
+ if (msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(("pap_rauthnak: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+
+ u->us_clientstate = UPAPCS_BADAUTH;
+
+ error("PAP authentication failed");
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(u)
+ upap_state *u;
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
+ u->us_userlen + u->us_passwdlen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(UPAP_AUTHREQ, outp);
+ PUTCHAR(++u->us_id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(u->us_userlen, outp);
+ BCOPY(u->us_user, outp, u->us_userlen);
+ INCPTR(u->us_userlen, outp);
+ PUTCHAR(u->us_passwdlen, outp);
+ BCOPY(u->us_passwd, outp, u->us_passwdlen);
+
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ TIMEOUT(upap_timeout, u, u->us_timeouttime);
+ ++u->us_transmits;
+ u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(u, code, id, msg, msglen)
+ upap_state *u;
+ u_char code, id;
+ char *msg;
+ int msglen;
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+ outp = outpacket_buf;
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(msglen, outp);
+ BCOPY(msg, outp, msglen);
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static char *upap_codenames[] = {
+ "AuthReq", "AuthAck", "AuthNak"
+};
+
+static int
+upap_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len;
+ int mlen, ulen, wlen;
+ char *user, *pwd, *msg;
+ u_char *pstart;
+
+ if (plen < UPAP_HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < UPAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(upap_codenames) / sizeof(char *))
+ printer(arg, " %s", upap_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= UPAP_HEADERLEN;
+ switch (code) {
+ case UPAP_AUTHREQ:
+ if (len < 1)
+ break;
+ ulen = p[0];
+ if (len < ulen + 2)
+ break;
+ wlen = p[ulen + 1];
+ if (len < ulen + wlen + 2)
+ break;
+ user = (char *) (p + 1);
+ pwd = (char *) (p + ulen + 2);
+ p += ulen + wlen + 2;
+ len -= ulen + wlen + 2;
+ printer(arg, " user=");
+ print_string(user, ulen, printer, arg);
+ printer(arg, " password=");
+ if (!hide_password)
+ print_string(pwd, wlen, printer, arg);
+ else
+ printer(arg, "<hidden>");
+ break;
+ case UPAP_AUTHACK:
+ case UPAP_AUTHNAK:
+ if (len < 1)
+ break;
+ mlen = p[0];
+ if (len < mlen + 1)
+ break;
+ msg = (char *) (p + 1);
+ p += mlen + 1;
+ len -= mlen + 1;
+ printer(arg, " ");
+ print_string(msg, mlen, printer, arg);
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
diff --git a/mdk-stage1/ppp/pppd/upap.h b/mdk-stage1/ppp/pppd/upap.h
new file mode 100644
index 000000000..42d6f4f0f
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/upap.h
@@ -0,0 +1,87 @@
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN 4
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ 1 /* Authenticate-Request */
+#define UPAP_AUTHACK 2 /* Authenticate-Ack */
+#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
+
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+ int us_unit; /* Interface unit number */
+ char *us_user; /* User */
+ int us_userlen; /* User length */
+ char *us_passwd; /* Password */
+ int us_passwdlen; /* Password length */
+ int us_clientstate; /* Client state */
+ int us_serverstate; /* Server state */
+ u_char us_id; /* Current id */
+ int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */
+ int us_transmits; /* Number of auth-reqs sent */
+ int us_maxtransmits; /* Maximum number of auth-reqs to send */
+ int us_reqtimeout; /* Time to wait for auth-req from peer */
+} upap_state;
+
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL 0 /* Connection down */
+#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPCS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN 4 /* We've received an Ack */
+#define UPAPCS_BADAUTH 5 /* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL 0 /* Connection down */
+#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPSS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */
+#define UPAPSS_OPEN 4 /* We've sent an Ack */
+#define UPAPSS_BADAUTH 5 /* We've sent a Nak */
+
+
+/*
+ * Timeouts.
+ */
+#define UPAP_DEFTIMEOUT 3 /* Timeout (seconds) for retransmitting req */
+#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */
+
+extern upap_state upap[];
+
+void upap_authwithpeer __P((int, char *, char *));
+void upap_authpeer __P((int));
+
+extern struct protent pap_protent;
diff --git a/mdk-stage1/ppp/pppd/utils.c b/mdk-stage1/ppp/pppd/utils.c
new file mode 100644
index 000000000..6a5b88e8b
--- /dev/null
+++ b/mdk-stage1/ppp/pppd/utils.c
@@ -0,0 +1,948 @@
+/*
+ * utils.c - various utility functions used in pppd.
+ *
+ * Copyright (c) 1999 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define RCSID "$Id$"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifdef SVR4
+#include <sys/mkdev.h>
+#endif
+
+#include "pppd.h"
+
+static const char rcsid[] = RCSID;
+
+#if defined(SUNOS4)
+extern char *strerror();
+#endif
+
+static void logit __P((int, char *, va_list));
+static void log_write __P((int, char *));
+static void vslp_printer __P((void *, char *, ...));
+static void format_packet __P((u_char *, int, void (*) (void *, char *, ...),
+ void *));
+
+struct buffer_info {
+ char *ptr;
+ int len;
+};
+
+/*
+ * strlcpy - like strcpy/strncpy, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t
+strlcpy(dest, src, len)
+ char *dest;
+ const char *src;
+ size_t len;
+{
+ size_t ret = strlen(src);
+
+ if (len != 0) {
+ if (ret < len)
+ strcpy(dest, src);
+ else {
+ strncpy(dest, src, len - 1);
+ dest[len-1] = 0;
+ }
+ }
+ return ret;
+}
+
+/*
+ * strlcat - like strcat/strncat, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t
+strlcat(dest, src, len)
+ char *dest;
+ const char *src;
+ size_t len;
+{
+ size_t dlen = strlen(dest);
+
+ return dlen + strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0));
+}
+
+
+/*
+ * slprintf - format a message into a buffer. Like sprintf except we
+ * also specify the length of the output buffer, and we handle
+ * %r (recursive format), %m (error message), %v (visible string),
+ * %q (quoted string), %t (current time) and %I (IP address) formats.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+int
+slprintf __V((char *buf, int buflen, char *fmt, ...))
+{
+ va_list args;
+ int n;
+
+#if defined(__STDC__)
+ va_start(args, fmt);
+#else
+ char *buf;
+ int buflen;
+ char *fmt;
+ va_start(args);
+ buf = va_arg(args, char *);
+ buflen = va_arg(args, int);
+ fmt = va_arg(args, char *);
+#endif
+ n = vslprintf(buf, buflen, fmt, args);
+ va_end(args);
+ return n;
+}
+
+/*
+ * vslprintf - like slprintf, takes a va_list instead of a list of args.
+ */
+#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int
+vslprintf(buf, buflen, fmt, args)
+ char *buf;
+ int buflen;
+ char *fmt;
+ va_list args;
+{
+ int c, i, n;
+ int width, prec, fillch;
+ int base, len, neg, quoted;
+ unsigned long val = 0;
+ char *str, *f, *buf0;
+ unsigned char *p;
+ char num[32];
+ time_t t;
+ u_int32_t ip;
+ static char hexchars[] = "0123456789abcdef";
+ struct buffer_info bufinfo;
+
+ buf0 = buf;
+ --buflen;
+ while (buflen > 0) {
+ for (f = fmt; *f != '%' && *f != 0; ++f)
+ ;
+ if (f > fmt) {
+ len = f - fmt;
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, fmt, len);
+ buf += len;
+ buflen -= len;
+ fmt = f;
+ }
+ if (*fmt == 0)
+ break;
+ c = *++fmt;
+ width = 0;
+ prec = -1;
+ fillch = ' ';
+ if (c == '0') {
+ fillch = '0';
+ c = *++fmt;
+ }
+ if (c == '*') {
+ width = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ width = width * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if (c == '.') {
+ c = *++fmt;
+ if (c == '*') {
+ prec = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ prec = 0;
+ while (isdigit(c)) {
+ prec = prec * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ }
+ str = 0;
+ base = 0;
+ neg = 0;
+ ++fmt;
+ switch (c) {
+ case 'd':
+ i = va_arg(args, int);
+ if (i < 0) {
+ neg = 1;
+ val = -i;
+ } else
+ val = i;
+ base = 10;
+ break;
+ case 'u':
+ val = va_arg(args, unsigned int);
+ base = 10;
+ break;
+ case 'o':
+ val = va_arg(args, unsigned int);
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ val = va_arg(args, unsigned int);
+ base = 16;
+ break;
+ case 'p':
+ val = (unsigned long) va_arg(args, void *);
+ base = 16;
+ neg = 2;
+ break;
+ case 's':
+ str = va_arg(args, char *);
+ break;
+ case 'c':
+ num[0] = va_arg(args, int);
+ num[1] = 0;
+ str = num;
+ break;
+ case 'm':
+ str = strerror(errno);
+ break;
+ case 'I':
+ ip = va_arg(args, u_int32_t);
+ ip = ntohl(ip);
+ slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff,
+ (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
+ str = num;
+ break;
+ case 'r':
+ f = va_arg(args, char *);
+#ifndef __powerpc__
+ n = vslprintf(buf, buflen + 1, f, va_arg(args, va_list));
+#else
+ /* On the powerpc, a va_list is an array of 1 structure */
+ n = vslprintf(buf, buflen + 1, f, va_arg(args, void *));
+#endif
+ buf += n;
+ buflen -= n;
+ continue;
+ case 't':
+ time(&t);
+ str = ctime(&t);
+ str += 4; /* chop off the day name */
+ str[15] = 0; /* chop off year and newline */
+ break;
+ case 'v': /* "visible" string */
+ case 'q': /* quoted string */
+ quoted = c == 'q';
+ p = va_arg(args, unsigned char *);
+ if (fillch == '0' && prec >= 0) {
+ n = prec;
+ } else {
+ n = strlen((char *)p);
+ if (prec >= 0 && n > prec)
+ n = prec;
+ }
+ while (n > 0 && buflen > 0) {
+ c = *p++;
+ --n;
+ if (!quoted && c >= 0x80) {
+ OUTCHAR('M');
+ OUTCHAR('-');
+ c -= 0x80;
+ }
+ if (quoted && (c == '"' || c == '\\'))
+ OUTCHAR('\\');
+ if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+ if (quoted) {
+ OUTCHAR('\\');
+ switch (c) {
+ case '\t': OUTCHAR('t'); break;
+ case '\n': OUTCHAR('n'); break;
+ case '\b': OUTCHAR('b'); break;
+ case '\f': OUTCHAR('f'); break;
+ default:
+ OUTCHAR('x');
+ OUTCHAR(hexchars[c >> 4]);
+ OUTCHAR(hexchars[c & 0xf]);
+ }
+ } else {
+ if (c == '\t')
+ OUTCHAR(c);
+ else {
+ OUTCHAR('^');
+ OUTCHAR(c ^ 0x40);
+ }
+ }
+ } else
+ OUTCHAR(c);
+ }
+ continue;
+ case 'P': /* print PPP packet */
+ bufinfo.ptr = buf;
+ bufinfo.len = buflen + 1;
+ p = va_arg(args, unsigned char *);
+ n = va_arg(args, int);
+ format_packet(p, n, vslp_printer, &bufinfo);
+ buf = bufinfo.ptr;
+ buflen = bufinfo.len - 1;
+ continue;
+ case 'B':
+ p = va_arg(args, unsigned char *);
+ for (n = prec; n > 0; --n) {
+ c = *p++;
+ if (fillch == ' ')
+ OUTCHAR(' ');
+ OUTCHAR(hexchars[(c >> 4) & 0xf]);
+ OUTCHAR(hexchars[c & 0xf]);
+ }
+ continue;
+ default:
+ *buf++ = '%';
+ if (c != '%')
+ --fmt; /* so %z outputs %z etc. */
+ --buflen;
+ continue;
+ }
+ if (base != 0) {
+ str = num + sizeof(num);
+ *--str = 0;
+ while (str > num + neg) {
+ *--str = hexchars[val % base];
+ val = val / base;
+ if (--prec <= 0 && val == 0)
+ break;
+ }
+ switch (neg) {
+ case 1:
+ *--str = '-';
+ break;
+ case 2:
+ *--str = 'x';
+ *--str = '0';
+ break;
+ }
+ len = num + sizeof(num) - 1 - str;
+ } else {
+ len = strlen(str);
+ if (prec >= 0 && len > prec)
+ len = prec;
+ }
+ if (width > 0) {
+ if (width > buflen)
+ width = buflen;
+ if ((n = width - len) > 0) {
+ buflen -= n;
+ for (; n > 0; --n)
+ *buf++ = fillch;
+ }
+ }
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, str, len);
+ buf += len;
+ buflen -= len;
+ }
+ *buf = 0;
+ return buf - buf0;
+}
+
+/*
+ * vslp_printer - used in processing a %P format
+ */
+static void
+vslp_printer __V((void *arg, char *fmt, ...))
+{
+ int n;
+ va_list pvar;
+ struct buffer_info *bi;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ void *arg;
+ char *fmt;
+ va_start(pvar);
+ arg = va_arg(pvar, void *);
+ fmt = va_arg(pvar, char *);
+#endif
+
+ bi = (struct buffer_info *) arg;
+ n = vslprintf(bi->ptr, bi->len, fmt, pvar);
+ va_end(pvar);
+
+ bi->ptr += n;
+ bi->len -= n;
+}
+
+#ifdef unused
+/*
+ * log_packet - format a packet and log it.
+ */
+
+void
+log_packet(p, len, prefix, level)
+ u_char *p;
+ int len;
+ char *prefix;
+ int level;
+{
+ init_pr_log(prefix, level);
+ format_packet(p, len, pr_log, &level);
+ end_pr_log();
+}
+#endif /* unused */
+
+/*
+ * format_packet - make a readable representation of a packet,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void
+format_packet(p, len, printer, arg)
+ u_char *p;
+ int len;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int i, n;
+ u_short proto;
+ struct protent *protp;
+
+ if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
+ p += 2;
+ GETSHORT(proto, p);
+ len -= PPP_HDRLEN;
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (proto == protp->protocol)
+ break;
+ if (protp != NULL) {
+ printer(arg, "[%s", protp->name);
+ n = (*protp->printpkt)(p, len, printer, arg);
+ printer(arg, "]");
+ p += n;
+ len -= n;
+ } else {
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (proto == (protp->protocol & ~0x8000))
+ break;
+ if (protp != 0 && protp->data_name != 0) {
+ printer(arg, "[%s data]", protp->data_name);
+ if (len > 8)
+ printer(arg, "%.8B ...", p);
+ else
+ printer(arg, "%.*B", len, p);
+ len = 0;
+ } else
+ printer(arg, "[proto=0x%x]", proto);
+ }
+ }
+
+ if (len > 32)
+ printer(arg, "%.32B ...", p);
+ else
+ printer(arg, "%.*B", len, p);
+}
+
+/*
+ * init_pr_log, end_pr_log - initialize and finish use of pr_log.
+ */
+
+static char line[256]; /* line to be logged accumulated here */
+static char *linep; /* current pointer within line */
+static int llevel; /* level for logging */
+
+void
+init_pr_log(prefix, level)
+ char *prefix;
+ int level;
+{
+ linep = line;
+ if (prefix != NULL) {
+ strlcpy(line, prefix, sizeof(line));
+ linep = line + strlen(line);
+ }
+ llevel = level;
+}
+
+void
+end_pr_log()
+{
+ if (linep != line) {
+ *linep = 0;
+ log_write(llevel, line);
+ }
+}
+
+/*
+ * pr_log - printer routine for outputting to syslog
+ */
+void
+pr_log __V((void *arg, char *fmt, ...))
+{
+ int l, n;
+ va_list pvar;
+ char *p, *eol;
+ char buf[256];
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ void *arg;
+ char *fmt;
+ va_start(pvar);
+ arg = va_arg(pvar, void *);
+ fmt = va_arg(pvar, char *);
+#endif
+
+ n = vslprintf(buf, sizeof(buf), fmt, pvar);
+ va_end(pvar);
+
+ p = buf;
+ eol = strchr(buf, '\n');
+ if (linep != line) {
+ l = (eol == NULL)? n: eol - buf;
+ if (linep + l < line + sizeof(line)) {
+ if (l > 0) {
+ memcpy(linep, buf, l);
+ linep += l;
+ }
+ if (eol == NULL)
+ return;
+ p = eol + 1;
+ eol = strchr(p, '\n');
+ }
+ *linep = 0;
+ log_write(llevel, line);
+ linep = line;
+ }
+
+ while (eol != NULL) {
+ *eol = 0;
+ log_write(llevel, p);
+ p = eol + 1;
+ eol = strchr(p, '\n');
+ }
+
+ /* assumes sizeof(buf) <= sizeof(line) */
+ l = buf + n - p;
+ if (l > 0) {
+ memcpy(line, p, n);
+ linep = line + l;
+ }
+}
+
+/*
+ * print_string - print a readable representation of a string using
+ * printer.
+ */
+void
+print_string(p, len, printer, arg)
+ char *p;
+ int len;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int c;
+
+ printer(arg, "\"");
+ for (; len > 0; --len) {
+ c = *p++;
+ if (' ' <= c && c <= '~') {
+ if (c == '\\' || c == '"')
+ printer(arg, "\\");
+ printer(arg, "%c", c);
+ } else {
+ switch (c) {
+ case '\n':
+ printer(arg, "\\n");
+ break;
+ case '\r':
+ printer(arg, "\\r");
+ break;
+ case '\t':
+ printer(arg, "\\t");
+ break;
+ default:
+ printer(arg, "\\%.3o", c);
+ }
+ }
+ }
+ printer(arg, "\"");
+}
+
+/*
+ * logit - does the hard work for fatal et al.
+ */
+static void
+logit(level, fmt, args)
+ int level;
+ char *fmt;
+ va_list args;
+{
+ int n;
+ char buf[1024];
+
+ n = vslprintf(buf, sizeof(buf), fmt, args);
+ log_write(level, buf);
+}
+
+static void
+log_write(level, buf)
+ int level;
+ char *buf;
+{
+ syslog(level, "%s", buf);
+ if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) {
+ int n = strlen(buf);
+
+ if (n > 0 && buf[n-1] == '\n')
+ --n;
+ if (write(log_to_fd, buf, n) != n
+ || write(log_to_fd, "\n", 1) != 1)
+ log_to_fd = -1;
+ }
+}
+
+/*
+ * fatal - log an error message and die horribly.
+ */
+void
+fatal __V((char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, char *);
+#endif
+
+ logit(LOG_ERR, fmt, pvar);
+ va_end(pvar);
+
+ die(1); /* as promised */
+}
+
+/*
+ * error - log an error message.
+ */
+void
+error __V((char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, char *);
+#endif
+
+ logit(LOG_ERR, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * warn - log a warning message.
+ */
+void
+warn __V((char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, char *);
+#endif
+
+ logit(LOG_WARNING, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * notice - log a notice-level message.
+ */
+void
+notice __V((char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, char *);
+#endif
+
+ logit(LOG_NOTICE, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * info - log an informational message.
+ */
+void
+info __V((char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, char *);
+#endif
+
+ logit(LOG_INFO, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * dbglog - log a debug message.
+ */
+void
+dbglog __V((char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, char *);
+#endif
+
+ logit(LOG_DEBUG, fmt, pvar);
+ va_end(pvar);
+}
+
+/* Procedures for locking the serial device using a lock file. */
+#ifndef LOCK_DIR
+#ifdef _linux_
+#define LOCK_DIR "/var/lock"
+#else
+#ifdef SVR4
+#define LOCK_DIR "/var/spool/locks"
+#else
+#define LOCK_DIR "/var/spool/lock"
+#endif
+#endif
+#endif /* LOCK_DIR */
+
+static char lock_file[MAXPATHLEN];
+
+/*
+ * lock - create a lock file for the named device
+ */
+int
+lock(dev)
+ char *dev;
+{
+#ifdef LOCKLIB
+ int result;
+
+ result = mklock (dev, (void *) 0);
+ if (result == 0) {
+ strlcpy(lock_file, sizeof(lock_file), dev);
+ return 0;
+ }
+
+ if (result > 0)
+ notice("Device %s is locked by pid %d", dev, result);
+ else
+ error("Can't create lock file %s", lock_file);
+ return -1;
+
+#else /* LOCKLIB */
+
+ char lock_buffer[12];
+ int fd, pid, n;
+
+#ifdef SVR4
+ struct stat sbuf;
+
+ if (stat(dev, &sbuf) < 0) {
+ error("Can't get device number for %s: %m", dev);
+ return -1;
+ }
+ if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
+ error("Can't lock %s: not a character device", dev);
+ return -1;
+ }
+ slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d",
+ LOCK_DIR, major(sbuf.st_dev),
+ major(sbuf.st_rdev), minor(sbuf.st_rdev));
+#else
+ char *p;
+
+ if ((p = strrchr(dev, '/')) != NULL)
+ dev = p + 1;
+ slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev);
+#endif
+
+ while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+ if (errno != EEXIST) {
+ error("Can't create lock file %s: %m", lock_file);
+ break;
+ }
+
+ /* Read the lock file to find out who has the device locked. */
+ fd = open(lock_file, O_RDONLY, 0);
+ if (fd < 0) {
+ if (errno == ENOENT) /* This is just a timing problem. */
+ continue;
+ error("Can't open existing lock file %s: %m", lock_file);
+ break;
+ }
+#ifndef LOCK_BINARY
+ n = read(fd, lock_buffer, 11);
+#else
+ n = read(fd, &pid, sizeof(pid));
+#endif /* LOCK_BINARY */
+ close(fd);
+ fd = -1;
+ if (n <= 0) {
+ error("Can't read pid from lock file %s", lock_file);
+ break;
+ }
+
+ /* See if the process still exists. */
+#ifndef LOCK_BINARY
+ lock_buffer[n] = 0;
+ pid = atoi(lock_buffer);
+#endif /* LOCK_BINARY */
+ if (pid == getpid())
+ return 1; /* somebody else locked it for us */
+ if (pid == 0
+ || (kill(pid, 0) == -1 && errno == ESRCH)) {
+ if (unlink (lock_file) == 0) {
+ notice("Removed stale lock on %s (pid %d)", dev, pid);
+ continue;
+ }
+ warn("Couldn't remove stale lock on %s", dev);
+ } else
+ notice("Device %s is locked by pid %d", dev, pid);
+ break;
+ }
+
+ if (fd < 0) {
+ lock_file[0] = 0;
+ return -1;
+ }
+
+ pid = getpid();
+#ifndef LOCK_BINARY
+ slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
+ write (fd, lock_buffer, 11);
+#else
+ write(fd, &pid, sizeof (pid));
+#endif
+ close(fd);
+ return 0;
+
+#endif
+}
+
+/*
+ * relock - called to update our lockfile when we are about to detach,
+ * thus changing our pid (we fork, the child carries on, and the parent dies).
+ * Note that this is called by the parent, with pid equal to the pid
+ * of the child. This avoids a potential race which would exist if
+ * we had the child rewrite the lockfile (the parent might die first,
+ * and another process could think the lock was stale if it checked
+ * between when the parent died and the child rewrote the lockfile).
+ */
+int
+relock(pid)
+ int pid;
+{
+#ifdef LOCKLIB
+ /* XXX is there a way to do this? */
+ return -1;
+#else /* LOCKLIB */
+
+ int fd;
+ char lock_buffer[12];
+
+ if (lock_file[0] == 0)
+ return -1;
+ fd = open(lock_file, O_WRONLY, 0);
+ if (fd < 0) {
+ error("Couldn't reopen lock file %s: %m", lock_file);
+ lock_file[0] = 0;
+ return -1;
+ }
+
+#ifndef LOCK_BINARY
+ slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
+ write (fd, lock_buffer, 11);
+#else
+ write(fd, &pid, sizeof(pid));
+#endif /* LOCK_BINARY */
+ close(fd);
+ return 0;
+
+#endif /* LOCKLIB */
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+ if (lock_file[0]) {
+#ifdef LOCKLIB
+ (void) rmlock(lock_file, (void *) 0);
+#else
+ unlink(lock_file);
+#endif
+ lock_file[0] = 0;
+ }
+}
+
diff --git a/mdk-stage1/ppp/pppdump/Makefile.linux b/mdk-stage1/ppp/pppdump/Makefile.linux
new file mode 100644
index 000000000..1d8d78ff7
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/Makefile.linux
@@ -0,0 +1,17 @@
+CFLAGS= -I../include/net $(RPM_OPT_FLAGS)
+OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
+
+INSTALL= install
+
+all: pppdump
+
+pppdump: $(OBJS)
+ $(CC) $(RPM_OPT_FLAGS) -o pppdump $(OBJS)
+
+clean:
+ rm -f pppdump $(OBJS) *~
+
+install:
+ mkdir -p $(BINDIR) $(MANDIR)/man8
+ $(INSTALL) -s -c pppdump $(BINDIR)
+ $(INSTALL) -c pppdump.8 $(MANDIR)/man8
diff --git a/mdk-stage1/ppp/pppdump/Makefile.linux.makeopt b/mdk-stage1/ppp/pppdump/Makefile.linux.makeopt
new file mode 100644
index 000000000..d02fecde8
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/Makefile.linux.makeopt
@@ -0,0 +1,17 @@
+CFLAGS= -O -I../include/net
+OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
+
+INSTALL= install
+
+all: pppdump
+
+pppdump: $(OBJS)
+ $(CC) -o pppdump $(OBJS)
+
+clean:
+ rm -f pppdump $(OBJS) *~
+
+install:
+ mkdir -p $(BINDIR) $(MANDIR)/man8
+ $(INSTALL) -s -c pppdump $(BINDIR)
+ $(INSTALL) -c -m 444 pppdump.8 $(MANDIR)/man8
diff --git a/mdk-stage1/ppp/pppdump/Makefile.linux.pppdump-Makefile b/mdk-stage1/ppp/pppdump/Makefile.linux.pppdump-Makefile
new file mode 100644
index 000000000..4c98b6c6d
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/Makefile.linux.pppdump-Makefile
@@ -0,0 +1,17 @@
+CFLAGS= -O -I../include/net
+OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
+
+INSTALL= install
+
+all: pppdump
+
+pppdump: $(OBJS)
+ $(CC) $(RPM_OPT_FLAGS) -o pppdump $(OBJS)
+
+clean:
+ rm -f pppdump $(OBJS) *~
+
+install:
+ mkdir -p $(BINDIR) $(MANDIR)/man8
+ $(INSTALL) -s -c pppdump $(BINDIR)
+ $(INSTALL) -c -m 444 pppdump.8 $(MANDIR)/man8
diff --git a/mdk-stage1/ppp/pppdump/Makefile.sol2 b/mdk-stage1/ppp/pppdump/Makefile.sol2
new file mode 100644
index 000000000..d7e6b413e
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/Makefile.sol2
@@ -0,0 +1,21 @@
+#
+# pppdump Makefile for SVR4 systems
+# $Id$
+#
+
+include ../solaris/Makedefs
+
+CFLAGS= $(COPTS) -I../include/net
+OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
+
+all: pppdump
+
+pppdump: $(OBJS)
+ $(CC) -o pppdump $(OBJS)
+
+clean:
+ rm -f $(OBJS) pppdump *~
+
+install:
+ $(INSTALL) -f $(BINDIR) pppdump
+ $(INSTALL) -m 444 -f $(MANDIR)/man8 pppdump.8
diff --git a/mdk-stage1/ppp/pppdump/Makefile.sunos4 b/mdk-stage1/ppp/pppdump/Makefile.sunos4
new file mode 100644
index 000000000..915c8267d
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/Makefile.sunos4
@@ -0,0 +1,21 @@
+#
+# pppstats makefile
+# $Id$
+#
+
+include ../sunos4/Makedefs
+
+OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
+CFLAGS = $(COPTS) -I../include/net
+
+all: pppdump
+
+pppdump: $(OBJS)
+ $(CC) -o pppdump $(OBJS)
+
+clean:
+ rm -f pppdump $(OBJS) *~
+
+install: pppdump
+ $(INSTALL) -c pppdump $(BINDIR)/pppdump
+ $(INSTALL) -c -m 444 pppdump.8 $(MANDIR)/man8/pppdump.8
diff --git a/mdk-stage1/ppp/pppdump/bsd-comp.c b/mdk-stage1/ppp/pppdump/bsd-comp.c
new file mode 100644
index 000000000..1e14e98bb
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/bsd-comp.c
@@ -0,0 +1,750 @@
+/* Because this code is derived from the 4.3BSD compress source:
+ *
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include "ppp_defs.h"
+#include "ppp-comp.h"
+
+#if DO_BSD_COMPRESS
+
+/*
+ * PPP "BSD compress" compression
+ * The differences between this compression and the classic BSD LZW
+ * source are obvious from the requirement that the classic code worked
+ * with files while this handles arbitrarily long streams that
+ * are broken into packets. They are:
+ *
+ * When the code size expands, a block of junk is not emitted by
+ * the compressor and not expected by the decompressor.
+ *
+ * New codes are not necessarily assigned every time an old
+ * code is output by the compressor. This is because a packet
+ * end forces a code to be emitted, but does not imply that a
+ * new sequence has been seen.
+ *
+ * The compression ratio is checked at the first end of a packet
+ * after the appropriate gap. Besides simplifying and speeding
+ * things up, this makes it more likely that the transmitter
+ * and receiver will agree when the dictionary is cleared when
+ * compression is not going well.
+ */
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+struct bsd_db {
+ int totlen; /* length of this structure */
+ u_int hsize; /* size of the hash table */
+ u_char hshift; /* used in hash function */
+ u_char n_bits; /* current bits/code */
+ u_char maxbits;
+ u_char debug;
+ u_char unit;
+ u_short seqno; /* sequence number of next packet */
+ u_int hdrlen; /* header length to preallocate */
+ u_int mru;
+ u_int maxmaxcode; /* largest valid code */
+ u_int max_ent; /* largest code in use */
+ u_int in_count; /* uncompressed bytes, aged */
+ u_int bytes_out; /* compressed bytes, aged */
+ u_int ratio; /* recent compression ratio */
+ u_int checkpoint; /* when to next check the ratio */
+ u_int clear_count; /* times dictionary cleared */
+ u_int incomp_count; /* incompressible packets */
+ u_int incomp_bytes; /* incompressible bytes */
+ u_int uncomp_count; /* uncompressed packets */
+ u_int uncomp_bytes; /* uncompressed bytes */
+ u_int comp_count; /* compressed packets */
+ u_int comp_bytes; /* compressed bytes */
+ u_short *lens; /* array of lengths of codes */
+ struct bsd_dict {
+ union { /* hash value */
+ u_int32_t fcode;
+ struct {
+#ifdef BSD_LITTLE_ENDIAN
+ u_short prefix; /* preceding code */
+ u_char suffix; /* last character of new code */
+ u_char pad;
+#else
+ u_char pad;
+ u_char suffix; /* last character of new code */
+ u_short prefix; /* preceding code */
+#endif
+ } hs;
+ } f;
+ u_short codem1; /* output of hash table -1 */
+ u_short cptr; /* map code to hash table entry */
+ } dict[1];
+};
+
+#define BSD_OVHD 2 /* BSD compress overhead/packet */
+#define BSD_INIT_BITS BSD_MIN_BITS
+
+static void *bsd_decomp_alloc __P((u_char *options, int opt_len));
+static void bsd_free __P((void *state));
+static int bsd_decomp_init __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+static void bsd_incomp __P((void *state, u_char *dmsg, int len));
+static int bsd_decompress __P((void *state, u_char *cmp, int inlen,
+ u_char *dmp, int *outlen));
+static void bsd_reset __P((void *state));
+static void bsd_comp_stats __P((void *state, struct compstat *stats));
+
+/*
+ * Exported procedures.
+ */
+struct compressor ppp_bsd_compress = {
+ CI_BSD_COMPRESS, /* compress_proto */
+ bsd_decomp_alloc, /* decomp_alloc */
+ bsd_free, /* decomp_free */
+ bsd_decomp_init, /* decomp_init */
+ bsd_reset, /* decomp_reset */
+ bsd_decompress, /* decompress */
+ bsd_incomp, /* incomp */
+ bsd_comp_stats, /* decomp_stat */
+};
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR 256 /* table clear output code */
+#define FIRST 257 /* first free entry */
+#define LAST 255
+
+#define MAXCODE(b) ((1 << (b)) - 1)
+#define BADCODEM1 MAXCODE(BSD_MAX_BITS)
+
+#define BSD_HASH(prefix,suffix,hshift) ((((u_int32_t)(suffix)) << (hshift)) \
+ ^ (u_int32_t)(prefix))
+#define BSD_KEY(prefix,suffix) ((((u_int32_t)(suffix)) << 16) \
+ + (u_int32_t)(prefix))
+
+#define CHECK_GAP 10000 /* Ratio check interval */
+
+#define RATIO_SCALE_LOG 8
+#define RATIO_SCALE (1<<RATIO_SCALE_LOG)
+#define RATIO_MAX (0x7fffffff>>RATIO_SCALE_LOG)
+
+/*
+ * clear the dictionary
+ */
+static void
+bsd_clear(db)
+ struct bsd_db *db;
+{
+ db->clear_count++;
+ db->max_ent = FIRST-1;
+ db->n_bits = BSD_INIT_BITS;
+ db->ratio = 0;
+ db->bytes_out = 0;
+ db->in_count = 0;
+ db->checkpoint = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+static int /* 1=output CLEAR */
+bsd_check(db)
+ struct bsd_db *db;
+{
+ u_int new_ratio;
+
+ if (db->in_count >= db->checkpoint) {
+ /* age the ratio by limiting the size of the counts */
+ if (db->in_count >= RATIO_MAX
+ || db->bytes_out >= RATIO_MAX) {
+ db->in_count -= db->in_count/4;
+ db->bytes_out -= db->bytes_out/4;
+ }
+
+ db->checkpoint = db->in_count + CHECK_GAP;
+
+ if (db->max_ent >= db->maxmaxcode) {
+ /* Reset the dictionary only if the ratio is worse,
+ * or if it looks as if it has been poisoned
+ * by incompressible data.
+ *
+ * This does not overflow, because
+ * db->in_count <= RATIO_MAX.
+ */
+ new_ratio = db->in_count << RATIO_SCALE_LOG;
+ if (db->bytes_out != 0)
+ new_ratio /= db->bytes_out;
+
+ if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE) {
+ bsd_clear(db);
+ return 1;
+ }
+ db->ratio = new_ratio;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return statistics.
+ */
+static void
+bsd_comp_stats(state, stats)
+ void *state;
+ struct compstat *stats;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ u_int out;
+
+ stats->unc_bytes = db->uncomp_bytes;
+ stats->unc_packets = db->uncomp_count;
+ stats->comp_bytes = db->comp_bytes;
+ stats->comp_packets = db->comp_count;
+ stats->inc_bytes = db->incomp_bytes;
+ stats->inc_packets = db->incomp_count;
+ stats->ratio = db->in_count;
+ out = db->bytes_out;
+ if (stats->ratio <= 0x7fffff)
+ stats->ratio <<= 8;
+ else
+ out >>= 8;
+ if (out != 0)
+ stats->ratio /= out;
+}
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+static void
+bsd_reset(state)
+ void *state;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ db->seqno = 0;
+ bsd_clear(db);
+ db->clear_count = 0;
+}
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+static void *
+bsd_alloc(options, opt_len, decomp)
+ u_char *options;
+ int opt_len, decomp;
+{
+ int bits;
+ u_int newlen, hsize, hshift, maxmaxcode;
+ struct bsd_db *db;
+
+ if (opt_len != 3 || options[0] != CI_BSD_COMPRESS || options[1] != 3
+ || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION)
+ return NULL;
+
+ bits = BSD_NBITS(options[2]);
+ switch (bits) {
+ case 9: /* needs 82152 for both directions */
+ case 10: /* needs 84144 */
+ case 11: /* needs 88240 */
+ case 12: /* needs 96432 */
+ hsize = 5003;
+ hshift = 4;
+ break;
+ case 13: /* needs 176784 */
+ hsize = 9001;
+ hshift = 5;
+ break;
+ case 14: /* needs 353744 */
+ hsize = 18013;
+ hshift = 6;
+ break;
+ case 15: /* needs 691440 */
+ hsize = 35023;
+ hshift = 7;
+ break;
+ case 16: /* needs 1366160--far too much, */
+ /* hsize = 69001; */ /* and 69001 is too big for cptr */
+ /* hshift = 8; */ /* in struct bsd_db */
+ /* break; */
+ default:
+ return NULL;
+ }
+
+ maxmaxcode = MAXCODE(bits);
+ newlen = sizeof(*db) + (hsize-1) * (sizeof(db->dict[0]));
+ db = (struct bsd_db *) malloc(newlen);
+ if (!db)
+ return NULL;
+ memset(db, 0, sizeof(*db) - sizeof(db->dict));
+
+ if (!decomp) {
+ db->lens = NULL;
+ } else {
+ db->lens = (u_short *) malloc((maxmaxcode+1) * sizeof(db->lens[0]));
+ if (!db->lens) {
+ free(db);
+ return NULL;
+ }
+ }
+
+ db->totlen = newlen;
+ db->hsize = hsize;
+ db->hshift = hshift;
+ db->maxmaxcode = maxmaxcode;
+ db->maxbits = bits;
+
+ return (void *) db;
+}
+
+static void
+bsd_free(state)
+ void *state;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ if (db->lens)
+ free(db->lens);
+ free(db);
+}
+
+static void *
+bsd_decomp_alloc(options, opt_len)
+ u_char *options;
+ int opt_len;
+{
+ return bsd_alloc(options, opt_len, 1);
+}
+
+/*
+ * Initialize the database.
+ */
+static int
+bsd_init(db, options, opt_len, unit, hdrlen, mru, debug, decomp)
+ struct bsd_db *db;
+ u_char *options;
+ int opt_len, unit, hdrlen, mru, debug, decomp;
+{
+ int i;
+
+ if (opt_len < CILEN_BSD_COMPRESS
+ || options[0] != CI_BSD_COMPRESS || options[1] != CILEN_BSD_COMPRESS
+ || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION
+ || BSD_NBITS(options[2]) != db->maxbits
+ || decomp && db->lens == NULL)
+ return 0;
+
+ if (decomp) {
+ i = LAST+1;
+ while (i != 0)
+ db->lens[--i] = 1;
+ }
+ i = db->hsize;
+ while (i != 0) {
+ db->dict[--i].codem1 = BADCODEM1;
+ db->dict[i].cptr = 0;
+ }
+
+ db->unit = unit;
+ db->hdrlen = hdrlen;
+ db->mru = mru;
+ if (debug)
+ db->debug = 1;
+
+ bsd_reset(db);
+
+ return 1;
+}
+
+static int
+bsd_decomp_init(state, options, opt_len, unit, hdrlen, mru, debug)
+ void *state;
+ u_char *options;
+ int opt_len, unit, hdrlen, mru, debug;
+{
+ return bsd_init((struct bsd_db *) state, options, opt_len,
+ unit, hdrlen, mru, debug, 1);
+}
+
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+static void
+bsd_incomp(state, dmsg, mlen)
+ void *state;
+ u_char *dmsg;
+ int mlen;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ u_int hshift = db->hshift;
+ u_int max_ent = db->max_ent;
+ u_int n_bits = db->n_bits;
+ struct bsd_dict *dictp;
+ u_int32_t fcode;
+ u_char c;
+ long hval, disp;
+ int slen, ilen;
+ u_int bitno = 7;
+ u_char *rptr;
+ u_int ent;
+
+ rptr = dmsg;
+ ent = rptr[0]; /* get the protocol */
+ if (ent == 0) {
+ ++rptr;
+ --mlen;
+ ent = rptr[0];
+ }
+ if ((ent & 1) == 0 || ent < 0x21 || ent > 0xf9)
+ return;
+
+ db->seqno++;
+ ilen = 1; /* count the protocol as 1 byte */
+ ++rptr;
+ slen = dmsg + mlen - rptr;
+ ilen += slen;
+ for (; slen > 0; --slen) {
+ c = *rptr++;
+ fcode = BSD_KEY(ent, c);
+ hval = BSD_HASH(ent, c, hshift);
+ dictp = &db->dict[hval];
+
+ /* validate and then check the entry */
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ if (dictp->f.fcode == fcode) {
+ ent = dictp->codem1+1;
+ continue; /* found (prefix,suffix) */
+ }
+
+ /* continue probing until a match or invalid entry */
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = &db->dict[hval];
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ } while (dictp->f.fcode != fcode);
+ ent = dictp->codem1+1;
+ continue; /* finally found (prefix,suffix) */
+
+ nomatch: /* output (count) the prefix */
+ bitno += n_bits;
+
+ /* code -> hashtable */
+ if (max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ /* expand code size if needed */
+ if (max_ent >= MAXCODE(n_bits))
+ db->n_bits = ++n_bits;
+
+ /* Invalidate previous hash table entry
+ * assigned this code, and then take it over.
+ */
+ dictp2 = &db->dict[max_ent+1];
+ if (db->dict[dictp2->cptr].codem1 == max_ent)
+ db->dict[dictp2->cptr].codem1 = BADCODEM1;
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->f.fcode = fcode;
+
+ db->max_ent = ++max_ent;
+ db->lens[max_ent] = db->lens[ent]+1;
+ }
+ ent = c;
+ }
+ bitno += n_bits; /* output (count) the last code */
+ db->bytes_out += bitno/8;
+ db->in_count += ilen;
+ (void)bsd_check(db);
+
+ ++db->incomp_count;
+ db->incomp_bytes += ilen;
+ ++db->uncomp_count;
+ db->uncomp_bytes += ilen;
+
+ /* Increase code size if we would have without the packet
+ * boundary and as the decompressor will.
+ */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode)
+ db->n_bits++;
+}
+
+
+/*
+ * Decompress "BSD Compress"
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression. For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+static int
+bsd_decompress(state, cmsg, inlen, dmp, outlenp)
+ void *state;
+ u_char *cmsg, *dmp;
+ int inlen, *outlenp;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ u_int max_ent = db->max_ent;
+ u_int32_t accm = 0;
+ u_int bitno = 32; /* 1st valid bit in accm */
+ u_int n_bits = db->n_bits;
+ u_int tgtbitno = 32-n_bits; /* bitno when we have a code */
+ struct bsd_dict *dictp;
+ int explen, i, seq, len;
+ u_int incode, oldcode, finchar;
+ u_char *p, *rptr, *wptr;
+ int ilen;
+ int dlen, space, codelen, extra;
+
+ rptr = cmsg;
+ if (*rptr == 0)
+ ++rptr;
+ ++rptr; /* skip protocol (assumed 0xfd) */
+ seq = (rptr[0] << 8) + rptr[1];
+ rptr += BSD_OVHD;
+ ilen = len = cmsg + inlen - rptr;
+
+ /*
+ * Check the sequence number and give up if it is not what we expect.
+ */
+ if (seq != db->seqno++) {
+ if (db->debug)
+ printf("bsd_decomp%d: bad sequence # %d, expected %d\n",
+ db->unit, seq, db->seqno - 1);
+ return DECOMP_ERROR;
+ }
+
+ wptr = dmp + db->hdrlen;
+
+ oldcode = CLEAR;
+ explen = 0;
+ while (len > 0) {
+ /*
+ * Accumulate bytes until we have a complete code.
+ * Then get the next code, relying on the 32-bit,
+ * unsigned accm to mask the result.
+ */
+ bitno -= 8;
+ accm |= *rptr++ << bitno;
+ --len;
+ if (tgtbitno < bitno)
+ continue;
+ incode = accm >> tgtbitno;
+ accm <<= n_bits;
+ bitno += n_bits;
+
+ if (incode == CLEAR) {
+ /*
+ * The dictionary must only be cleared at
+ * the end of a packet. But there could be an
+ * empty message block at the end.
+ */
+ if (len > 0) {
+ if (db->debug)
+ printf("bsd_decomp%d: bad CLEAR\n", db->unit);
+ return DECOMP_FATALERROR;
+ }
+ bsd_clear(db);
+ explen = ilen = 0;
+ break;
+ }
+
+ if (incode > max_ent + 2 || incode > db->maxmaxcode
+ || incode > max_ent && oldcode == CLEAR) {
+ if (db->debug) {
+ printf("bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+ db->unit, incode, oldcode);
+ printf("max_ent=0x%x dlen=%d seqno=%d\n",
+ max_ent, dlen, db->seqno);
+ }
+ return DECOMP_FATALERROR; /* probably a bug */
+ }
+
+ /* Special case for KwKwK string. */
+ if (incode > max_ent) {
+ finchar = oldcode;
+ extra = 1;
+ } else {
+ finchar = incode;
+ extra = 0;
+ }
+
+ codelen = db->lens[finchar];
+ explen += codelen + extra;
+ if (explen > db->mru + 1) {
+ if (db->debug)
+ printf("bsd_decomp%d: ran out of mru\n", db->unit);
+ return DECOMP_FATALERROR;
+ }
+
+ /*
+ * Decode this code and install it in the decompressed buffer.
+ */
+ p = (wptr += codelen);
+ while (finchar > LAST) {
+ dictp = &db->dict[db->dict[finchar].cptr];
+#ifdef DEBUG
+ --codelen;
+ if (codelen <= 0) {
+ printf("bsd_decomp%d: fell off end of chain ", db->unit);
+ printf("0x%x at 0x%x by 0x%x, max_ent=0x%x\n",
+ incode, finchar, db->dict[finchar].cptr, max_ent);
+ return DECOMP_FATALERROR;
+ }
+ if (dictp->codem1 != finchar-1) {
+ printf("bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",
+ db->unit, incode, finchar);
+ printf("oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode,
+ db->dict[finchar].cptr, dictp->codem1);
+ return DECOMP_FATALERROR;
+ }
+#endif
+ *--p = dictp->f.hs.suffix;
+ finchar = dictp->f.hs.prefix;
+ }
+ *--p = finchar;
+
+#ifdef DEBUG
+ if (--codelen != 0)
+ printf("bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n",
+ db->unit, codelen, incode, max_ent);
+#endif
+
+ if (extra) /* the KwKwK case again */
+ *wptr++ = finchar;
+
+ /*
+ * If not first code in a packet, and
+ * if not out of code space, then allocate a new code.
+ *
+ * Keep the hash table correct so it can be used
+ * with uncompressed packets.
+ */
+ if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ u_int32_t fcode;
+ int hval, disp;
+
+ fcode = BSD_KEY(oldcode,finchar);
+ hval = BSD_HASH(oldcode,finchar,db->hshift);
+ dictp = &db->dict[hval];
+
+ /* look for a free hash table entry */
+ if (dictp->codem1 < max_ent) {
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = &db->dict[hval];
+ } while (dictp->codem1 < max_ent);
+ }
+
+ /*
+ * Invalidate previous hash table entry
+ * assigned this code, and then take it over
+ */
+ dictp2 = &db->dict[max_ent+1];
+ if (db->dict[dictp2->cptr].codem1 == max_ent) {
+ db->dict[dictp2->cptr].codem1 = BADCODEM1;
+ }
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->f.fcode = fcode;
+
+ db->max_ent = ++max_ent;
+ db->lens[max_ent] = db->lens[oldcode]+1;
+
+ /* Expand code size if needed. */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
+ db->n_bits = ++n_bits;
+ tgtbitno = 32-n_bits;
+ }
+ }
+ oldcode = incode;
+ }
+ *outlenp = wptr - (dmp + db->hdrlen);
+
+ /*
+ * Keep the checkpoint right so that incompressible packets
+ * clear the dictionary at the right times.
+ */
+ db->bytes_out += ilen;
+ db->in_count += explen;
+ if (bsd_check(db) && db->debug) {
+ printf("bsd_decomp%d: peer should have cleared dictionary\n",
+ db->unit);
+ }
+
+ ++db->comp_count;
+ db->comp_bytes += ilen + BSD_OVHD;
+ ++db->uncomp_count;
+ db->uncomp_bytes += explen;
+
+ return DECOMP_OK;
+}
+#endif /* DO_BSD_COMPRESS */
diff --git a/mdk-stage1/ppp/pppdump/deflate.c b/mdk-stage1/ppp/pppdump/deflate.c
new file mode 100644
index 000000000..08e8abc0d
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/deflate.c
@@ -0,0 +1,344 @@
+/*
+ * ppp_deflate.c - interface the zlib procedures for Deflate compression
+ * and decompression (as used by gzip) to the PPP code.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include "ppp_defs.h"
+#include "ppp-comp.h"
+#include "zlib.h"
+
+#if DO_DEFLATE
+
+#define DEFLATE_DEBUG 1
+
+/*
+ * State for a Deflate (de)compressor.
+ */
+struct deflate_state {
+ int seqno;
+ int w_size;
+ int unit;
+ int hdrlen;
+ int mru;
+ int debug;
+ z_stream strm;
+ struct compstat stats;
+};
+
+#define DEFLATE_OVHD 2 /* Deflate overhead/packet */
+
+static void *z_alloc __P((void *, u_int items, u_int size));
+static void z_free __P((void *, void *ptr, u_int nb));
+static void *z_decomp_alloc __P((u_char *options, int opt_len));
+static void z_decomp_free __P((void *state));
+static int z_decomp_init __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+static void z_incomp __P((void *state, u_char *dmsg, int len));
+static int z_decompress __P((void *state, u_char *cmp, int inlen,
+ u_char *dmp, int *outlenp));
+static void z_decomp_reset __P((void *state));
+static void z_comp_stats __P((void *state, struct compstat *stats));
+
+/*
+ * Procedures exported to if_ppp.c.
+ */
+struct compressor ppp_deflate = {
+ CI_DEFLATE, /* compress_proto */
+ z_decomp_alloc, /* decomp_alloc */
+ z_decomp_free, /* decomp_free */
+ z_decomp_init, /* decomp_init */
+ z_decomp_reset, /* decomp_reset */
+ z_decompress, /* decompress */
+ z_incomp, /* incomp */
+ z_comp_stats, /* decomp_stat */
+};
+
+/*
+ * Space allocation and freeing routines for use by zlib routines.
+ */
+static void *
+z_alloc(notused, items, size)
+ void *notused;
+ u_int items, size;
+{
+ return malloc(items * size);
+}
+
+static void
+z_free(notused, ptr, nbytes)
+ void *notused;
+ void *ptr;
+ u_int nbytes;
+{
+ free(ptr);
+}
+
+static void
+z_comp_stats(arg, stats)
+ void *arg;
+ struct compstat *stats;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ u_int out;
+
+ *stats = state->stats;
+ stats->ratio = stats->unc_bytes;
+ out = stats->comp_bytes + stats->unc_bytes;
+ if (stats->ratio <= 0x7ffffff)
+ stats->ratio <<= 8;
+ else
+ out >>= 8;
+ if (out != 0)
+ stats->ratio /= out;
+}
+
+/*
+ * Allocate space for a decompressor.
+ */
+static void *
+z_decomp_alloc(options, opt_len)
+ u_char *options;
+ int opt_len;
+{
+ struct deflate_state *state;
+ int w_size;
+
+ if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return NULL;
+ w_size = DEFLATE_SIZE(options[2]);
+ if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
+ return NULL;
+
+ state = (struct deflate_state *) malloc(sizeof(*state));
+ if (state == NULL)
+ return NULL;
+
+ state->strm.next_out = NULL;
+ state->strm.zalloc = (alloc_func) z_alloc;
+ state->strm.zfree = (free_func) z_free;
+ if (inflateInit2(&state->strm, -w_size) != Z_OK) {
+ free(state);
+ return NULL;
+ }
+
+ state->w_size = w_size;
+ memset(&state->stats, 0, sizeof(state->stats));
+ return (void *) state;
+}
+
+static void
+z_decomp_free(arg)
+ void *arg;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ inflateEnd(&state->strm);
+ free(state);
+}
+
+static int
+z_decomp_init(arg, options, opt_len, unit, hdrlen, mru, debug)
+ void *arg;
+ u_char *options;
+ int opt_len, unit, hdrlen, mru, debug;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(options[2]) != state->w_size
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+
+ state->seqno = 0;
+ state->unit = unit;
+ state->hdrlen = hdrlen;
+ state->debug = debug;
+ state->mru = mru;
+
+ inflateReset(&state->strm);
+
+ return 1;
+}
+
+static void
+z_decomp_reset(arg)
+ void *arg;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ state->seqno = 0;
+ inflateReset(&state->strm);
+}
+
+/*
+ * Decompress a Deflate-compressed packet.
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression. For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+static int
+z_decompress(arg, mi, inlen, mo, outlenp)
+ void *arg;
+ u_char *mi, *mo;
+ int inlen, *outlenp;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ u_char *rptr, *wptr;
+ int rlen, olen, ospace;
+ int seq, i, flush, r, decode_proto;
+
+ rptr = mi;
+ if (*rptr == 0)
+ ++rptr;
+ ++rptr;
+
+ /* Check the sequence number. */
+ seq = (rptr[0] << 8) + rptr[1];
+ rptr += 2;
+ if (seq != state->seqno) {
+#if !DEFLATE_DEBUG
+ if (state->debug)
+#endif
+ printf("z_decompress%d: bad seq # %d, expected %d\n",
+ state->unit, seq, state->seqno);
+ return DECOMP_ERROR;
+ }
+ ++state->seqno;
+
+ /*
+ * Set up to call inflate.
+ */
+ wptr = mo;
+ state->strm.next_in = rptr;
+ state->strm.avail_in = mi + inlen - rptr;
+ rlen = state->strm.avail_in + PPP_HDRLEN + DEFLATE_OVHD;
+ state->strm.next_out = wptr;
+ state->strm.avail_out = state->mru + 2;
+
+ r = inflate(&state->strm, Z_PACKET_FLUSH);
+ if (r != Z_OK) {
+#if !DEFLATE_DEBUG
+ if (state->debug)
+#endif
+ printf("z_decompress%d: inflate returned %d (%s)\n",
+ state->unit, r, (state->strm.msg? state->strm.msg: ""));
+ return DECOMP_FATALERROR;
+ }
+ olen = state->mru + 2 - state->strm.avail_out;
+ *outlenp = olen;
+
+ if ((wptr[0] & 1) != 0)
+ ++olen; /* for suppressed protocol high byte */
+ olen += 2; /* for address, control */
+
+#if DEFLATE_DEBUG
+ if (olen > state->mru + PPP_HDRLEN)
+ printf("ppp_deflate%d: exceeded mru (%d > %d)\n",
+ state->unit, olen, state->mru + PPP_HDRLEN);
+#endif
+
+ state->stats.unc_bytes += olen;
+ state->stats.unc_packets++;
+ state->stats.comp_bytes += rlen;
+ state->stats.comp_packets++;
+
+ return DECOMP_OK;
+}
+
+/*
+ * Incompressible data has arrived - add it to the history.
+ */
+static void
+z_incomp(arg, mi, mlen)
+ void *arg;
+ u_char *mi;
+ int mlen;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ u_char *rptr;
+ int rlen, proto, r;
+
+ /*
+ * Check that the protocol is one we handle.
+ */
+ rptr = mi;
+ proto = rptr[0];
+ if ((proto & 1) == 0)
+ proto = (proto << 8) + rptr[1];
+ if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+ return;
+
+ ++state->seqno;
+
+ if (rptr[0] == 0)
+ ++rptr;
+ rlen = mi + mlen - rptr;
+ state->strm.next_in = rptr;
+ state->strm.avail_in = rlen;
+ r = inflateIncomp(&state->strm);
+ if (r != Z_OK) {
+ /* gak! */
+#if !DEFLATE_DEBUG
+ if (state->debug)
+#endif
+ printf("z_incomp%d: inflateIncomp returned %d (%s)\n",
+ state->unit, r, (state->strm.msg? state->strm.msg: ""));
+ return;
+ }
+
+ /*
+ * Update stats.
+ */
+ if (proto <= 0xff)
+ ++rlen;
+ rlen += 2;
+ state->stats.inc_bytes += rlen;
+ state->stats.inc_packets++;
+ state->stats.unc_bytes += rlen;
+ state->stats.unc_packets++;
+}
+
+#endif /* DO_DEFLATE */
diff --git a/mdk-stage1/ppp/pppdump/ppp-comp.h b/mdk-stage1/ppp/pppdump/ppp-comp.h
new file mode 100644
index 000000000..9221c0db4
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/ppp-comp.h
@@ -0,0 +1,150 @@
+/*
+ * ppp-comp.h - Definitions for doing PPP packet compression.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+#ifndef _NET_PPP_COMP_H
+#define _NET_PPP_COMP_H
+
+/*
+ * The following symbols control whether we include code for
+ * various compression methods.
+ */
+#ifndef DO_BSD_COMPRESS
+#define DO_BSD_COMPRESS 1 /* by default, include BSD-Compress */
+#endif
+#ifndef DO_DEFLATE
+#define DO_DEFLATE 1 /* by default, include Deflate */
+#endif
+#define DO_PREDICTOR_1 0
+#define DO_PREDICTOR_2 0
+
+/*
+ * Structure giving methods for compression/decompression.
+ */
+struct compressor {
+ int compress_proto; /* CCP compression protocol number */
+
+ /* Allocate space for a decompressor (receive side) */
+ void *(*decomp_alloc) __P((u_char *options, int opt_len));
+ /* Free space used by a decompressor */
+ void (*decomp_free) __P((void *state));
+ /* Initialize a decompressor */
+ int (*decomp_init) __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+ /* Reset a decompressor */
+ void (*decomp_reset) __P((void *state));
+ /* Decompress a packet. */
+ int (*decompress) __P((void *state, u_char *mp, int inlen,
+ u_char *dmp, int *outlen));
+ /* Update state for an incompressible packet received */
+ void (*incomp) __P((void *state, u_char *mp, int len));
+ /* Return decompression statistics */
+ void (*decomp_stat) __P((void *state, struct compstat *stats));
+};
+
+/*
+ * Return values for decompress routine.
+ * We need to make these distinctions so that we can disable certain
+ * useful functionality, namely sending a CCP reset-request as a result
+ * of an error detected after decompression. This is to avoid infringing
+ * a patent held by Motorola.
+ * Don't you just lurve software patents.
+ */
+#define DECOMP_OK 0 /* everything went OK */
+#define DECOMP_ERROR 1 /* error detected before decomp. */
+#define DECOMP_FATALERROR 2 /* error detected after decomp. */
+
+/*
+ * CCP codes.
+ */
+#define CCP_CONFREQ 1
+#define CCP_CONFACK 2
+#define CCP_CONFNAK 3
+#define CCP_CONFREJ 4
+#define CCP_TERMREQ 5
+#define CCP_TERMACK 6
+#define CCP_RESETREQ 14
+#define CCP_RESETACK 15
+
+/*
+ * Max # bytes for a CCP option
+ */
+#define CCP_MAX_OPTION_LENGTH 32
+
+/*
+ * Parts of a CCP packet.
+ */
+#define CCP_CODE(dp) ((dp)[0])
+#define CCP_ID(dp) ((dp)[1])
+#define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN 4
+
+#define CCP_OPT_CODE(dp) ((dp)[0])
+#define CCP_OPT_LENGTH(dp) ((dp)[1])
+#define CCP_OPT_MINLEN 2
+
+/*
+ * Definitions for BSD-Compress.
+ */
+#define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS 3 /* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */
+#define BSD_VERSION(x) ((x) >> 5) /* version of option format */
+#define BSD_CURRENT_VERSION 1 /* current version number */
+#define BSD_MAKE_OPT(v, n) (((v) << 5) | (n))
+
+#define BSD_MIN_BITS 9 /* smallest code size supported */
+#define BSD_MAX_BITS 15 /* largest code size supported */
+
+/*
+ * Definitions for Deflate.
+ */
+#define CI_DEFLATE 26 /* config option for Deflate */
+#define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */
+#define CILEN_DEFLATE 4 /* length of its config option */
+
+#define DEFLATE_MIN_SIZE 8
+#define DEFLATE_MAX_SIZE 15
+#define DEFLATE_METHOD_VAL 8
+#define DEFLATE_SIZE(x) (((x) >> 4) + DEFLATE_MIN_SIZE)
+#define DEFLATE_METHOD(x) ((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w) ((((w) - DEFLATE_MIN_SIZE) << 4) \
+ + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE 0
+
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+#define CI_PREDICTOR_1 1 /* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1 2 /* length of its config option */
+#define CI_PREDICTOR_2 2 /* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2 2 /* length of its config option */
+
+#endif /* _NET_PPP_COMP_H */
diff --git a/mdk-stage1/ppp/pppdump/pppdump.8 b/mdk-stage1/ppp/pppdump/pppdump.8
new file mode 100644
index 000000000..c0172f519
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/pppdump.8
@@ -0,0 +1,62 @@
+.\" @(#) $Id$
+.TH PPPDUMP 8 "1 April 1999"
+.SH NAME
+pppdump \- convert PPP record file to readable format
+.SH SYNOPSIS
+.B pppdump
+[
+.B -h
+|
+.B -p
+[
+.B -d
+]] [
+.B -r
+] [
+.B -m \fImru
+] [
+.I file \fR...
+]
+.ti 12
+.SH DESCRIPTION
+The
+.B pppdump
+utility converts the files written using the \fIrecord\fR option of
+.B pppd
+into a human-readable format. If one or more filenames are specified,
+.B pppdump
+will read each in turn; otherwise it will read its standard input. In
+each case the result is written to standard output.
+.PP
+The options are as follows:
+.TP
+.B -h
+Prints the bytes sent and received in hexadecimal. If neither this
+option nor the \fB-p\fR option is specified, the bytes are printed as
+the characters themselves, with non-printing and non-ASCII characters
+printed as escape sequences.
+.TP
+.B -p
+Collects the bytes sent and received into PPP packets, interpreting
+the async HDLC framing and escape characters and checking the FCS
+(frame check sequence) of each packet. The packets are printed as hex
+values and as characters (non-printable characters are printed as
+`.').
+.TP
+.B -d
+With the \fB-p\fR option, this option causes
+.B pppdump
+to decompress packets which have been compressed with the BSD-Compress
+or Deflate methods.
+.TP
+.B -r
+Reverses the direction indicators, so that `sent' is printed for
+bytes or packets received, and `rcvd' is printed for bytes or packets
+sent.
+.TP
+.B -m \fImru
+Use \fImru\fR as the MRU (maximum receive unit) for both directions of
+the link when checking for over-length PPP packets (with the \fB-p\fR
+option).
+.SH SEE ALSO
+pppd(8)
diff --git a/mdk-stage1/ppp/pppdump/pppdump.c b/mdk-stage1/ppp/pppdump/pppdump.c
new file mode 100644
index 000000000..a8e69d4bf
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/pppdump.c
@@ -0,0 +1,502 @@
+/*
+ * pppdump - print out the contents of a record file generated by
+ * pppd in readable form.
+ *
+ * Copyright (C) 1999 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms. The name of the author
+ * may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include "ppp_defs.h"
+#include "ppp-comp.h"
+
+int hexmode;
+int pppmode;
+int reverse;
+int decompress;
+int mru = 1500;
+int abs_times;
+time_t start_time;
+int start_time_tenths;
+int tot_sent, tot_rcvd;
+
+extern int optind;
+extern char *optarg;
+
+main(ac, av)
+ int ac;
+ char **av;
+{
+ int i;
+ char *p;
+ FILE *f;
+
+ while ((i = getopt(ac, av, "hprdm:a")) != -1) {
+ switch (i) {
+ case 'h':
+ hexmode = 1;
+ break;
+ case 'p':
+ pppmode = 1;
+ break;
+ case 'r':
+ reverse = 1;
+ break;
+ case 'd':
+ decompress = 1;
+ break;
+ case 'm':
+ mru = atoi(optarg);
+ break;
+ case 'a':
+ abs_times = 1;
+ break;
+ default:
+ fprintf(stderr, "Usage: %s [-h | -p[d]] [-r] [-m mru] [-a] [file ...]\n", av[0]);
+ exit(1);
+ }
+ }
+ if (optind >= ac)
+ dumplog(stdin);
+ else {
+ for (i = optind; i < ac; ++i) {
+ p = av[i];
+ if ((f = fopen(p, "r")) == NULL) {
+ perror(p);
+ exit(1);
+ }
+ if (pppmode)
+ dumpppp(f);
+ else
+ dumplog(f);
+ fclose(f);
+ }
+ }
+ exit(0);
+}
+
+dumplog(f)
+ FILE *f;
+{
+ int c, n, k, col;
+ int nb, c2;
+ unsigned char buf[16];
+
+ while ((c = getc(f)) != EOF) {
+ switch (c) {
+ case 1:
+ case 2:
+ if (reverse)
+ c = 3 - c;
+ printf("%s %c", c==1? "sent": "rcvd", hexmode? ' ': '"');
+ col = 6;
+ n = getc(f);
+ n = (n << 8) + getc(f);
+ *(c==1? &tot_sent: &tot_rcvd) += n;
+ nb = 0;
+ for (; n > 0; --n) {
+ c = getc(f);
+ if (c == EOF) {
+ printf("\nEOF\n");
+ exit(0);
+ }
+ if (hexmode) {
+ if (nb >= 16) {
+ printf(" ");
+ for (k = 0; k < nb; ++k) {
+ c2 = buf[k];
+ putchar((' ' <= c2 && c2 <= '~')? c2: '.');
+ }
+ printf("\n ");
+ nb = 0;
+ }
+ buf[nb++] = c;
+ printf(" %.2x", c);
+ } else {
+ k = (' ' <= c && c <= '~')? (c != '\\' && c != '"')? 1: 2: 3;
+ if ((col += k) >= 78) {
+ printf("\n ");
+ col = 6 + k;
+ }
+ switch (k) {
+ case 1:
+ putchar(c);
+ break;
+ case 2:
+ printf("\\%c", c);
+ break;
+ case 3:
+ printf("\\%.2x", c);
+ break;
+ }
+ }
+ }
+ if (hexmode) {
+ for (k = nb; k < 16; ++k)
+ printf(" ");
+ printf(" ");
+ for (k = 0; k < nb; ++k) {
+ c2 = buf[k];
+ putchar((' ' <= c2 && c2 <= '~')? c2: '.');
+ }
+ } else
+ putchar('"');
+ printf("\n");
+ break;
+ case 3:
+ case 4:
+ printf("end %s\n", c==3? "send": "recv");
+ break;
+ case 5:
+ case 6:
+ case 7:
+ show_time(f, c);
+ break;
+ default:
+ printf("?%.2x\n");
+ }
+ }
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+struct pkt {
+ int cnt;
+ int esc;
+ int flags;
+ struct compressor *comp;
+ void *state;
+ unsigned char buf[8192];
+} spkt, rpkt;
+
+/* Values for flags */
+#define CCP_ISUP 1
+#define CCP_ERROR 2
+#define CCP_FATALERROR 4
+#define CCP_ERR (CCP_ERROR | CCP_FATALERROR)
+#define CCP_DECOMP_RUN 8
+
+unsigned char dbuf[8192];
+
+dumpppp(f)
+ FILE *f;
+{
+ int c, n, k;
+ int nb, nl, dn, proto, rv;
+ char *dir, *q;
+ unsigned char *p, *r, *endp;
+ unsigned char *d;
+ unsigned short fcs;
+ struct pkt *pkt;
+
+ spkt.cnt = rpkt.cnt = 0;
+ spkt.esc = rpkt.esc = 0;
+ while ((c = getc(f)) != EOF) {
+ switch (c) {
+ case 1:
+ case 2:
+ if (reverse)
+ c = 3 - c;
+ dir = c==1? "sent": "rcvd";
+ pkt = c==1? &spkt: &rpkt;
+ n = getc(f);
+ n = (n << 8) + getc(f);
+ *(c==1? &tot_sent: &tot_rcvd) += n;
+ for (; n > 0; --n) {
+ c = getc(f);
+ switch (c) {
+ case EOF:
+ printf("\nEOF\n");
+ if (spkt.cnt > 0)
+ printf("[%d bytes in incomplete send packet]\n",
+ spkt.cnt);
+ if (rpkt.cnt > 0)
+ printf("[%d bytes in incomplete recv packet]\n",
+ rpkt.cnt);
+ exit(0);
+ case '~':
+ if (pkt->cnt > 0) {
+ q = dir;
+ if (pkt->esc) {
+ printf("%s aborted packet:\n ", dir);
+ q = " ";
+ }
+ nb = pkt->cnt;
+ p = pkt->buf;
+ pkt->cnt = 0;
+ pkt->esc = 0;
+ if (nb <= 2) {
+ printf("%s short packet [%d bytes]:", q, nb);
+ for (k = 0; k < nb; ++k)
+ printf(" %.2x", p[k]);
+ printf("\n");
+ break;
+ }
+ fcs = PPP_INITFCS;
+ for (k = 0; k < nb; ++k)
+ fcs = PPP_FCS(fcs, p[k]);
+ fcs &= 0xFFFF;
+ nb -= 2;
+ endp = p + nb;
+ r = p;
+ if (r[0] == 0xff && r[1] == 3)
+ r += 2;
+ if ((r[0] & 1) == 0)
+ ++r;
+ ++r;
+ if (endp - r > mru)
+ printf(" ERROR: length (%d) > MRU (%d)\n",
+ endp - r, mru);
+ if (decompress && fcs == PPP_GOODFCS) {
+ /* See if this is a CCP or compressed packet */
+ d = dbuf;
+ r = p;
+ if (r[0] == 0xff && r[1] == 3) {
+ *d++ = *r++;
+ *d++ = *r++;
+ }
+ proto = r[0];
+ if ((proto & 1) == 0)
+ proto = (proto << 8) + r[1];
+ if (proto == PPP_CCP) {
+ handle_ccp(pkt, r + 2, endp - r - 2);
+ } else if (proto == PPP_COMP) {
+ if ((pkt->flags & CCP_ISUP)
+ && (pkt->flags & CCP_DECOMP_RUN)
+ && pkt->state
+ && (pkt->flags & CCP_ERR) == 0) {
+ rv = pkt->comp->decompress(pkt->state, r,
+ endp - r, d, &dn);
+ switch (rv) {
+ case DECOMP_OK:
+ p = dbuf;
+ nb = d + dn - p;
+ if ((d[0] & 1) == 0)
+ --dn;
+ --dn;
+ if (dn > mru)
+ printf(" ERROR: decompressed length (%d) > MRU (%d)\n", dn, mru);
+ break;
+ case DECOMP_ERROR:
+ printf(" DECOMPRESSION ERROR\n");
+ pkt->flags |= CCP_ERROR;
+ break;
+ case DECOMP_FATALERROR:
+ printf(" FATAL DECOMPRESSION ERROR\n");
+ pkt->flags |= CCP_FATALERROR;
+ break;
+ }
+ }
+ } else if (pkt->state
+ && (pkt->flags & CCP_DECOMP_RUN)) {
+ pkt->comp->incomp(pkt->state, r, endp - r);
+ }
+ }
+ do {
+ nl = nb < 16? nb: 16;
+ printf("%s ", q);
+ for (k = 0; k < nl; ++k)
+ printf(" %.2x", p[k]);
+ for (; k < 16; ++k)
+ printf(" ");
+ printf(" ");
+ for (k = 0; k < nl; ++k) {
+ c = p[k];
+ putchar((' ' <= c && c <= '~')? c: '.');
+ }
+ printf("\n");
+ q = " ";
+ p += nl;
+ nb -= nl;
+ } while (nb > 0);
+ if (fcs != PPP_GOODFCS)
+ printf(" BAD FCS: (residue = %x)\n", fcs);
+ }
+ break;
+ case '}':
+ if (!pkt->esc) {
+ pkt->esc = 1;
+ break;
+ }
+ /* else fall through */
+ default:
+ if (pkt->esc) {
+ c ^= 0x20;
+ pkt->esc = 0;
+ }
+ pkt->buf[pkt->cnt++] = c;
+ break;
+ }
+ }
+ break;
+ case 3:
+ case 4:
+ if (reverse)
+ c = 7 - c;
+ dir = c==3? "send": "recv";
+ pkt = c==3? &spkt: &rpkt;
+ printf("end %s", dir);
+ if (pkt->cnt > 0)
+ printf(" [%d bytes in incomplete packet]", pkt->cnt);
+ printf("\n");
+ break;
+ case 5:
+ case 6:
+ case 7:
+ show_time(f, c);
+ break;
+ default:
+ printf("?%.2x\n");
+ }
+ }
+}
+
+extern struct compressor ppp_bsd_compress, ppp_deflate;
+
+struct compressor *compressors[] = {
+#if DO_BSD_COMPRESS
+ &ppp_bsd_compress,
+#endif
+#if DO_DEFLATE
+ &ppp_deflate,
+#endif
+ NULL
+};
+
+handle_ccp(cp, dp, len)
+ struct pkt *cp;
+ u_char *dp;
+ int len;
+{
+ int clen;
+ struct compressor **comp;
+
+ if (len < CCP_HDRLEN)
+ return;
+ clen = CCP_LENGTH(dp);
+ if (clen > len)
+ return;
+
+ switch (CCP_CODE(dp)) {
+ case CCP_CONFACK:
+ cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP);
+ if (clen < CCP_HDRLEN + CCP_OPT_MINLEN
+ || clen < CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN))
+ break;
+ dp += CCP_HDRLEN;
+ clen -= CCP_HDRLEN;
+ for (comp = compressors; *comp != NULL; ++comp) {
+ if ((*comp)->compress_proto == dp[0]) {
+ if (cp->state != NULL) {
+ (*cp->comp->decomp_free)(cp->state);
+ cp->state = NULL;
+ }
+ cp->comp = *comp;
+ cp->state = (*comp)->decomp_alloc(dp, CCP_OPT_LENGTH(dp));
+ cp->flags |= CCP_ISUP;
+ if (cp->state != NULL
+ && (*cp->comp->decomp_init)
+ (cp->state, dp, clen, 0, 0, 8192, 1))
+ cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
+ break;
+ }
+ }
+ break;
+
+ case CCP_CONFNAK:
+ case CCP_CONFREJ:
+ cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP);
+ break;
+
+ case CCP_RESETACK:
+ if (cp->flags & CCP_ISUP) {
+ if (cp->state && (cp->flags & CCP_DECOMP_RUN)) {
+ (*cp->comp->decomp_reset)(cp->state);
+ cp->flags &= ~CCP_ERROR;
+ }
+ }
+ break;
+ }
+}
+
+show_time(f, c)
+ FILE *f;
+ int c;
+{
+ time_t t;
+ int n;
+ struct tm *tm;
+
+ if (c == 7) {
+ t = getc(f);
+ t = (t << 8) + getc(f);
+ t = (t << 8) + getc(f);
+ t = (t << 8) + getc(f);
+ printf("start %s", ctime(&t));
+ start_time = t;
+ start_time_tenths = 0;
+ tot_sent = tot_rcvd = 0;
+ } else {
+ n = getc(f);
+ if (c == 5) {
+ for (c = 3; c > 0; --c)
+ n = (n << 8) + getc(f);
+ }
+ if (abs_times) {
+ n += start_time_tenths;
+ start_time += n / 10;
+ start_time_tenths = n % 10;
+ tm = localtime(&start_time);
+ printf("time %.2d:%.2d:%.2d.%d", tm->tm_hour, tm->tm_min,
+ tm->tm_sec, start_time_tenths);
+ printf(" (sent %d, rcvd %d)\n", tot_sent, tot_rcvd);
+ } else
+ printf("time %.1fs\n", (double) n / 10);
+ }
+}
diff --git a/mdk-stage1/ppp/pppdump/zlib.c b/mdk-stage1/ppp/pppdump/zlib.c
new file mode 100644
index 000000000..5b8372719
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/zlib.c
@@ -0,0 +1,4614 @@
+/*
+ * This file is derived from various .h and .c files from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets. See zlib.h for conditions of
+ * distribution and use.
+ *
+ * Changes that have been made include:
+ * - changed functions not used outside this file to "local"
+ * - added minCompression parameter to deflateInit2
+ * - added Z_PACKET_FLUSH (see zlib.h for details)
+ * - added inflateIncomp
+ *
+ * $Id$
+ */
+
+
+/*+++++*/
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */
+
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#ifdef STDC
+# include <string.h>
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+#define FAR
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern char *z_errmsg[]; /* indexed by 1-zlib_error */
+
+#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err)
+/* To be used only when the state is known to be valid */
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+ /* common constants */
+
+#define DEFLATED 8
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+ /* functions */
+
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# define zmemcpy memcpy
+# define zmemzero(dest, len) memset(dest, 0, len)
+#else
+# define zmemcpy(d, s, n) bcopy((s), (d), (n))
+# define zmemzero bzero
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG_ZLIB
+# include <stdio.h>
+# ifndef verbose
+# define verbose 0
+# endif
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) fprintf x
+# define Tracev(x) {if (verbose) fprintf x ;}
+# define Tracevv(x) {if (verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len));
+
+/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */
+/* void zcfree OF((voidpf opaque, voidpf ptr)); */
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr, size) \
+ (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size))
+#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);}
+
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+
+/*+++++*/
+/* From: deflate.h,v 1.5 1995/05/03 17:27:09 jloup Exp */
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+/* Data type */
+#define BINARY 0
+#define ASCII 1
+#define UNKNOWN 2
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE 42
+#define BUSY_STATE 113
+#define FLUSH_STATE 124
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct deflate_state {
+ z_stream *strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ int pending; /* nb of bytes in the pending buffer */
+ uLong adler; /* adler32 of uncompressed data */
+ int noheader; /* suppress zlib header and adler32 */
+ Byte data_type; /* UNKNOWN, BINARY or ASCII */
+ Byte method; /* STORED (for zip only) or DEFLATED */
+ int minCompr; /* min size decrease for Z_FLUSH_NOSTORE */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to supress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *l_buf; /* buffer for literals or lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt last_lit; /* running index in l_buf */
+
+ ushf *d_buf;
+ /* Buffer for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ ulg compressed_len; /* total bit length of compressed file */
+ uInt matches; /* number of string matches in current block */
+ int last_eob_len; /* bit length of EOB code for last block */
+
+#ifdef DEBUG_ZLIB
+ ulg bits_sent; /* bit length of the compressed data */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+ uInt blocks_in_packet;
+ /* Number of blocks produced since the last time Z_PACKET_FLUSH
+ * was used.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+ /* in trees.c */
+local void ct_init OF((deflate_state *s));
+local int ct_tally OF((deflate_state *s, int dist, int lc));
+local ulg ct_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int flush));
+local void ct_align OF((deflate_state *s));
+local void ct_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+local void ct_stored_type_only OF((deflate_state *s));
+
+
+/*+++++*/
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* From: deflate.c,v 1.8 1995/05/03 17:27:08 jloup Exp */
+
+local char zlib_copyright[] = " deflate Copyright 1995 Jean-loup Gailly ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+} config;
+
+local config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0}, /* store only */
+/* 1 */ {4, 4, 8, 4}, /* maximum speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8},
+/* 3 */ {4, 6, 32, 32},
+
+/* 4 */ {4, 4, 16, 16}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32},
+/* 6 */ {8, 16, 128, 128},
+/* 7 */ {8, 32, 128, 256},
+/* 8 */ {32, 128, 258, 1024},
+/* 9 */ {32, 258, 258, 4096}}; /* maximum compression */
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+/* ===========================================================================
+ * Prototypes for local functions.
+ */
+
+local void fill_window OF((deflate_state *s));
+local int deflate_fast OF((deflate_state *s, int flush));
+local int deflate_slow OF((deflate_state *s, int flush));
+local void lm_init OF((deflate_state *s));
+local int longest_match OF((deflate_state *s, IPos cur_match));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_stream *strm));
+local int read_buf OF((z_stream *strm, charf *buf, unsigned size));
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+#endif
+
+#ifdef DEBUG_ZLIB
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (str))
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((charf *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int deflateInit (strm, level)
+ z_stream *strm;
+ int level;
+{
+ return deflateInit2 (strm, level, DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ 0, 0);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int deflateInit2 (strm, level, method, windowBits, memLevel,
+ strategy, minCompression)
+ z_stream *strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ int minCompression;
+{
+ deflate_state *s;
+ int noheader = 0;
+
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+/* if (strm->zalloc == Z_NULL) strm->zalloc = zcalloc; */
+/* if (strm->zfree == Z_NULL) strm->zfree = zcfree; */
+
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+
+ if (windowBits < 0) { /* undocumented feature: suppress zlib header */
+ noheader = 1;
+ windowBits = -windowBits;
+ }
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 1 || level > 9) {
+ return Z_STREAM_ERROR;
+ }
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->noheader = noheader;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, 2*sizeof(ush));
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ strm->msg = z_errmsg[1-Z_MEM_ERROR];
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->d_buf = (ushf *) &(s->pending_buf[s->lit_bufsize]);
+ s->l_buf = (uchf *) &(s->pending_buf[3*s->lit_bufsize]);
+ /* We overlay pending_buf and d_buf+l_buf. This works since the average
+ * output size for (length,distance) codes is <= 32 bits (worst case
+ * is 15+15+13=33).
+ */
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+ s->minCompr = minCompression;
+ s->blocks_in_packet = 0;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int deflateReset (strm)
+ z_stream *strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->noheader < 0) {
+ s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */
+ }
+ s->status = s->noheader ? BUSY_STATE : INIT_STATE;
+ s->adler = 1;
+
+ ct_init(s);
+ lm_init(s);
+
+ return Z_OK;
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible.
+ */
+local void flush_pending(strm)
+ z_stream *strm;
+{
+ deflate_state *state = (deflate_state *) strm->state;
+ unsigned len = state->pending;
+
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ if (strm->next_out != NULL) {
+ zmemcpy(strm->next_out, state->pending_out, len);
+ strm->next_out += len;
+ }
+ state->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ state->pending -= len;
+ if (state->pending == 0) {
+ state->pending_out = state->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int deflate (strm, flush)
+ z_stream *strm;
+ int flush;
+{
+ deflate_state *state = (deflate_state *) strm->state;
+
+ if (strm == Z_NULL || state == Z_NULL) return Z_STREAM_ERROR;
+
+ if (strm->next_in == Z_NULL && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ state->strm = strm; /* just in case */
+
+ /* Write the zlib header */
+ if (state->status == INIT_STATE) {
+
+ uInt header = (DEFLATED + ((state->w_bits-8)<<4)) << 8;
+ uInt level_flags = (state->level-1) >> 1;
+
+ if (level_flags > 3) level_flags = 3;
+ header |= (level_flags << 6);
+ header += 31 - (header % 31);
+
+ state->status = BUSY_STATE;
+ putShortMSB(state, header);
+ }
+
+ /* Flush as much pending output as possible */
+ if (state->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) return Z_OK;
+ }
+
+ /* If we came back in here to get the last output from
+ * a previous flush, we're done for now.
+ */
+ if (state->status == FLUSH_STATE) {
+ state->status = BUSY_STATE;
+ if (flush != Z_NO_FLUSH && flush != Z_FINISH)
+ return Z_OK;
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (state->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || state->lookahead != 0 ||
+ (flush == Z_FINISH && state->status != FINISH_STATE)) {
+ int quit;
+
+ if (flush == Z_FINISH) {
+ state->status = FINISH_STATE;
+ }
+ if (state->level <= 3) {
+ quit = deflate_fast(state, flush);
+ } else {
+ quit = deflate_slow(state, flush);
+ }
+ if (quit || strm->avail_out == 0)
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+
+ /* If a flush was requested, we have a little more to output now. */
+ if (flush != Z_NO_FLUSH && flush != Z_FINISH
+ && state->status != FINISH_STATE) {
+ switch (flush) {
+ case Z_PARTIAL_FLUSH:
+ ct_align(state);
+ break;
+ case Z_PACKET_FLUSH:
+ /* Output just the 3-bit `stored' block type value,
+ but not a zero length. */
+ ct_stored_type_only(state);
+ break;
+ default:
+ ct_stored_block(state, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(state); /* forget history */
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* We'll have to come back to get the rest of the output;
+ * this ensures we don't output a second zero-length stored
+ * block (or whatever).
+ */
+ state->status = FLUSH_STATE;
+ return Z_OK;
+ }
+ }
+
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (state->noheader) return Z_STREAM_END;
+
+ /* Write the zlib trailer (adler32) */
+ putShortMSB(state, (uInt)(state->adler >> 16));
+ putShortMSB(state, (uInt)(state->adler & 0xffff));
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ state->noheader = -1; /* write the trailer only once! */
+ return state->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int deflateEnd (strm)
+ z_stream *strm;
+{
+ deflate_state *state = (deflate_state *) strm->state;
+
+ if (strm == Z_NULL || state == Z_NULL) return Z_STREAM_ERROR;
+
+ TRY_FREE(strm, state->window, state->w_size * 2 * sizeof(Byte));
+ TRY_FREE(strm, state->prev, state->w_size * sizeof(Pos));
+ TRY_FREE(strm, state->head, state->hash_size * sizeof(Pos));
+ TRY_FREE(strm, state->pending_buf, state->lit_bufsize * 2 * sizeof(ush));
+
+ ZFREE(strm, state, sizeof(deflate_state));
+ strm->state = Z_NULL;
+
+ return Z_OK;
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read.
+ */
+local int read_buf(strm, buf, size)
+ z_stream *strm;
+ charf *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+ deflate_state *state = (deflate_state *) strm->state;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ if (!state->noheader) {
+ state->adler = adler32(state->adler, strm->next_in, len);
+ }
+ zmemcpy(buf, strm->next_in, len);
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->match_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+}
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local int longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2:
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= s->nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ return best_len;
+}
+#endif /* ASMV */
+
+#ifdef DEBUG_ZLIB
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (memcmp((charf *)s->window + match,
+ (charf *)s->window + start, length) != EQUAL) {
+ fprintf(stderr,
+ " start %u, match %u, length %d\n",
+ start, match, length);
+ do { fprintf(stderr, "%c%c", s->window[match++],
+ s->window[start++]); } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if strstart == 0
+ * and lookahead == 1 (input done one byte at time)
+ */
+ more--;
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ } else if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ /* By the IN assertion, the window is not empty so we can't confuse
+ * more == 0 with more == 64K on a 16 bit machine.
+ */
+ zmemcpy((charf *)s->window, (charf *)s->window+wsize,
+ (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage):
+ */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, (charf *)s->window + s->strstart + s->lookahead,
+ more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead >= MIN_MATCH) {
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, flush) { \
+ ct_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), (long)s->strstart - s->block_start, (flush)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, flush) { \
+ FLUSH_BLOCK_ONLY(s, flush); \
+ if (s->strm->avail_out == 0) return 1; \
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return true if
+ * processing was terminated prematurely (no more input or output space).
+ * This function does not perform lazy evaluationof matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local int deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ s->prev_length = MIN_MATCH-1;
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+
+ if (s->match_length > s->lookahead) s->match_length = s->lookahead;
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ bflush = ct_tally(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in hash table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ bflush = ct_tally (s, 0, s->window[s->strstart]);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, Z_NO_FLUSH);
+ }
+ FLUSH_BLOCK(s, flush);
+ return 0; /* normal exit */
+}
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local int deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+ if (s->match_length > s->lookahead) s->match_length = s->lookahead;
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED ||
+ (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR))) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ bflush = ct_tally(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, Z_NO_FLUSH);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ if (ct_tally (s, 0, s->window[s->strstart-1])) {
+ FLUSH_BLOCK_ONLY(s, Z_NO_FLUSH);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return 1;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ ct_tally (s, 0, s->window[s->strstart-1]);
+ s->match_available = 0;
+ }
+ FLUSH_BLOCK(s, flush);
+ return 0;
+}
+
+
+/*+++++*/
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* From: trees.c,v 1.5 1995/05/03 17:27:12 jloup Exp */
+
+#ifdef DEBUG_ZLIB
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ * To do: initialize at compile time to be completely reentrant. ???
+ */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see ct_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+local uch dist_code[512];
+/* distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+local uch length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+struct static_tree_desc_s {
+ ct_data *static_tree; /* static tree or NULL */
+ intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local static_tree_desc static_bl_desc =
+{(ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void ct_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+ ct_data *dtree));
+local void set_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifndef DEBUG_ZLIB
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG_ZLIB */
+# define send_code(s, c, tree) \
+ { if (verbose>1) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+#define d_code(dist) \
+ ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. dist_code[256] and dist_code[257] are never
+ * used.
+ */
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG_ZLIB
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracev((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (value << s->bi_valid);
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG_ZLIB */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (val << s->bi_valid);\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG_ZLIB */
+
+
+#define MAX(a,b) (a >= b ? a : b)
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ * To do: do this at compile time.
+ */
+local void ct_static_init()
+{
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "ct_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "ct_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "ct_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse(n, 5);
+ }
+}
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+local void ct_init(s)
+ deflate_state *s;
+{
+ if (static_dtree[0].Len == 0) {
+ ct_static_init(); /* To do: at compile time */
+ }
+
+ s->compressed_len = 0L;
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = &static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = &static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = &static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG_ZLIB
+ s->bits_sent = 0L;
+#endif
+ s->blocks_in_packet = 0;
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ ct_data *stree = desc->stat_desc->static_tree;
+ intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if (tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracec(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+local void ct_stored_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
+ s->compressed_len = (s->compressed_len + 3 + 7) & ~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* Send just the `stored block' type code without any length bytes or data.
+ */
+local void ct_stored_type_only(s)
+ deflate_state *s;
+{
+ send_bits(s, (STORED_BLOCK << 1), 3);
+ bi_windup(s);
+ s->compressed_len = (s->compressed_len + 3) & ~7L;
+}
+
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the EOB
+ * code for the previous block was coded on 5 bits or less, inflate
+ * may have only 5+3 bits of lookahead to decode this EOB.
+ * (There are no problems if the previous block is stored or fixed.)
+ */
+local void ct_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+ bi_flush(s);
+ /* Of the 10 bits for the empty block, we have already sent
+ * (10 - bi_valid) bits. The lookahead for the EOB of the previous
+ * block was thus its length plus what we have just sent.
+ */
+ if (s->last_eob_len + 10 - s->bi_valid < 9) {
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+ s->compressed_len += 10L;
+ bi_flush(s);
+ }
+ s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file. This function
+ * returns the total compressed length for the file so far.
+ */
+local ulg ct_flush_block(s, buf, stored_len, flush)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int flush; /* Z_FINISH if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex; /* index of last bit length code of non zero freq */
+ int eof = flush == Z_FINISH;
+
+ ++s->blocks_in_packet;
+
+ /* Check if the file is ascii or binary */
+ if (s->data_type == UNKNOWN) set_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute first the block length in bytes */
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->last_lit));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ /* If compression failed and this is the first and last block,
+ * and if the .zip file can be seeked (to rewrite the local header),
+ * the whole file is transformed into a stored file:
+ */
+#ifdef STORED_FILE_OK
+# ifdef FORCE_STORED_FILE
+ if (eof && compressed_len == 0L) /* force stored file */
+# else
+ if (stored_len <= opt_lenb && eof && s->compressed_len==0L && seekable())
+# endif
+ {
+ /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */
+ if (buf == (charf*)0) error ("block vanished");
+
+ copy_block(buf, (unsigned)stored_len, 0); /* without header */
+ s->compressed_len = stored_len << 3;
+ s->method = STORED;
+ } else
+#endif /* STORED_FILE_OK */
+
+ /* For Z_PACKET_FLUSH, if we don't achieve the required minimum
+ * compression, and this block contains all the data since the last
+ * time we used Z_PACKET_FLUSH, then just omit this block completely
+ * from the output.
+ */
+ if (flush == Z_PACKET_FLUSH && s->blocks_in_packet == 1
+ && opt_lenb > stored_len - s->minCompr) {
+ s->blocks_in_packet = 0;
+ /* output nothing */
+ } else
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0)
+ /* 4: two words for the lengths */
+#endif
+ {
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ ct_stored_block(s, buf, stored_len, eof);
+ } else
+
+#ifdef FORCE_STATIC
+ if (static_lenb >= 0) /* force static trees */
+#else
+ if (static_lenb == opt_lenb)
+#endif
+ {
+ send_bits(s, (STATIC_TREES<<1)+eof, 3);
+ compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+ s->compressed_len += 3 + s->static_len;
+ } else {
+ send_bits(s, (DYN_TREES<<1)+eof, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+ s->compressed_len += 3 + s->opt_len;
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ init_block(s);
+
+ if (eof) {
+ bi_windup(s);
+ s->compressed_len += 7; /* align on byte boundary */
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*eof));
+
+ return s->compressed_len >> 3;
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+local int ct_tally (s, dist, lc)
+ deflate_state *s;
+ int dist; /* distance of matched string */
+ int lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->d_buf[s->last_lit] = (ush)dist;
+ s->l_buf[s->last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match");
+
+ s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+
+ /* Try to guess if it is profitable to stop the current block here */
+ if (s->level > 2 && (s->last_lit & 0xfff) == 0) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)s->last_lit*8L;
+ ulg in_length = (ulg)s->strstart - s->block_start;
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)s->dyn_dtree[dcode].Freq *
+ (5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+ s->last_lit, in_length, out_length,
+ 100L - out_length*100L/in_length));
+ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+ }
+ return (s->last_lit == s->lit_bufsize-1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ ct_data *ltree; /* literal tree */
+ ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->last_lit != 0) do {
+ dist = s->d_buf[lx];
+ lc = s->l_buf[lx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow");
+
+ } while (lx < s->last_lit);
+
+ send_code(s, END_BLOCK, ltree);
+ s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+local void set_data_type(s)
+ deflate_state *s;
+{
+ int n = 0;
+ unsigned ascii_freq = 0;
+ unsigned bin_freq = 0;
+ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq;
+ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq;
+ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
+ s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII);
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG_ZLIB
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG_ZLIB
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG_ZLIB
+ s->bits_sent += (ulg)len<<3;
+#endif
+ while (len--) {
+ put_byte(s, *buf++);
+ }
+}
+
+
+/*+++++*/
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+local inflate_blocks_statef * inflate_blocks_new OF((
+ z_stream *z,
+ check_func c, /* check function */
+ uInt w)); /* window size */
+
+local int inflate_blocks OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ int)); /* initial return code */
+
+local void inflate_blocks_reset OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ uLongf *)); /* check value on output */
+
+local int inflate_blocks_free OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ uLongf *)); /* check value on output */
+
+local int inflate_addhistory OF((
+ inflate_blocks_statef *,
+ z_stream *));
+
+local int inflate_packet_flush OF((
+ inflate_blocks_statef *));
+
+/*+++++*/
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+ that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+ union {
+ struct {
+ Byte Exop; /* number of extra bits or operation */
+ Byte Bits; /* number of bits in this code or subcode */
+ } what;
+ uInt Nalloc; /* number of these allocated here */
+ Bytef *pad; /* pad structure to a power of 2 (4 bytes for */
+ } word; /* 16-bit, 8 bytes for 32-bit machines) */
+ union {
+ uInt Base; /* literal, length base, or distance base */
+ inflate_huft *Next; /* pointer to next level of table */
+ } more;
+};
+
+#ifdef DEBUG_ZLIB
+ local uInt inflate_hufts;
+#endif
+
+local int inflate_trees_bits OF((
+ uIntf *, /* 19 code lengths */
+ uIntf *, /* bits tree desired/actual depth */
+ inflate_huft * FAR *, /* bits tree result */
+ z_stream *)); /* for zalloc, zfree functions */
+
+local int inflate_trees_dynamic OF((
+ uInt, /* number of literal/length codes */
+ uInt, /* number of distance codes */
+ uIntf *, /* that many (total) code lengths */
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *, /* distance tree result */
+ z_stream *)); /* for zalloc, zfree functions */
+
+local int inflate_trees_fixed OF((
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *)); /* distance tree result */
+
+local int inflate_trees_free OF((
+ inflate_huft *, /* tables to free */
+ z_stream *)); /* for zfree function */
+
+
+/*+++++*/
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+local inflate_codes_statef *inflate_codes_new OF((
+ uInt, uInt,
+ inflate_huft *, inflate_huft *,
+ z_stream *));
+
+local int inflate_codes OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ int));
+
+local void inflate_codes_free OF((
+ inflate_codes_statef *,
+ z_stream *));
+
+
+/*+++++*/
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* inflate private state */
+struct internal_state {
+
+ /* mode */
+ enum {
+ METHOD, /* waiting for method byte */
+ FLAG, /* waiting for flag byte */
+ BLOCKS, /* decompressing blocks */
+ CHECK4, /* four check bytes to go */
+ CHECK3, /* three check bytes to go */
+ CHECK2, /* two check bytes to go */
+ CHECK1, /* one check byte to go */
+ DONE, /* finished check, done */
+ BAD} /* got an error--stay here */
+ mode; /* current inflate mode */
+
+ /* mode dependent information */
+ union {
+ uInt method; /* if FLAGS, method byte */
+ struct {
+ uLong was; /* computed check value */
+ uLong need; /* stream check value */
+ } check; /* if CHECK, check values to compare */
+ uInt marker; /* if BAD, inflateSync's marker bytes count */
+ } sub; /* submode */
+
+ /* mode independent information */
+ int nowrap; /* flag for no wrapper */
+ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
+ inflate_blocks_statef
+ *blocks; /* current inflate_blocks state */
+
+};
+
+
+int inflateReset(z)
+z_stream *z;
+{
+ uLong c;
+
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->total_in = z->total_out = 0;
+ z->msg = Z_NULL;
+ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+ inflate_blocks_reset(z->state->blocks, z, &c);
+ Trace((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+
+int inflateEnd(z)
+z_stream *z;
+{
+ uLong c;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->blocks != Z_NULL)
+ inflate_blocks_free(z->state->blocks, z, &c);
+ ZFREE(z, z->state, sizeof(struct internal_state));
+ z->state = Z_NULL;
+ Trace((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+
+int inflateInit2(z, w)
+z_stream *z;
+int w;
+{
+ /* initialize state */
+ if (z == Z_NULL)
+ return Z_STREAM_ERROR;
+/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */
+/* if (z->zfree == Z_NULL) z->zfree = zcfree; */
+ if ((z->state = (struct internal_state FAR *)
+ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
+ return Z_MEM_ERROR;
+ z->state->blocks = Z_NULL;
+
+ /* handle undocumented nowrap option (no zlib header or check) */
+ z->state->nowrap = 0;
+ if (w < 0)
+ {
+ w = - w;
+ z->state->nowrap = 1;
+ }
+
+ /* set window size */
+ if (w < 8 || w > 15)
+ {
+ inflateEnd(z);
+ return Z_STREAM_ERROR;
+ }
+ z->state->wbits = (uInt)w;
+
+ /* create inflate_blocks state */
+ if ((z->state->blocks =
+ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w))
+ == Z_NULL)
+ {
+ inflateEnd(z);
+ return Z_MEM_ERROR;
+ }
+ Trace((stderr, "inflate: allocated\n"));
+
+ /* reset state */
+ inflateReset(z);
+ return Z_OK;
+}
+
+
+int inflateInit(z)
+z_stream *z;
+{
+ return inflateInit2(z, DEF_WBITS);
+}
+
+
+#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;}
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+int inflate(z, f)
+z_stream *z;
+int f;
+{
+ int r;
+ uInt b;
+
+ if (z == Z_NULL || z->next_in == Z_NULL)
+ return Z_STREAM_ERROR;
+ r = Z_BUF_ERROR;
+ while (1) switch (z->state->mode)
+ {
+ case METHOD:
+ NEEDBYTE
+ if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED)
+ {
+ z->state->mode = BAD;
+ z->msg = "unknown compression method";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+ {
+ z->state->mode = BAD;
+ z->msg = "invalid window size";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ z->state->mode = FLAG;
+ case FLAG:
+ NEEDBYTE
+ if ((b = NEXTBYTE) & 0x20)
+ {
+ z->state->mode = BAD;
+ z->msg = "invalid reserved bit";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ if (((z->state->sub.method << 8) + b) % 31)
+ {
+ z->state->mode = BAD;
+ z->msg = "incorrect header check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Trace((stderr, "inflate: zlib header ok\n"));
+ z->state->mode = BLOCKS;
+ case BLOCKS:
+ r = inflate_blocks(z->state->blocks, z, r);
+ if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0)
+ r = inflate_packet_flush(z->state->blocks);
+ if (r == Z_DATA_ERROR)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0; /* can try inflateSync */
+ break;
+ }
+ if (r != Z_STREAM_END)
+ return r;
+ r = Z_OK;
+ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+ if (z->state->nowrap)
+ {
+ z->state->mode = DONE;
+ break;
+ }
+ z->state->mode = CHECK4;
+ case CHECK4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = CHECK3;
+ case CHECK3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = CHECK2;
+ case CHECK2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = CHECK1;
+ case CHECK1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+
+ if (z->state->sub.check.was != z->state->sub.check.need)
+ {
+ z->state->mode = BAD;
+ z->msg = "incorrect data check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Trace((stderr, "inflate: zlib check ok\n"));
+ z->state->mode = DONE;
+ case DONE:
+ return Z_STREAM_END;
+ case BAD:
+ return Z_DATA_ERROR;
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ empty:
+ if (f != Z_PACKET_FLUSH)
+ return r;
+ z->state->mode = BAD;
+ z->state->sub.marker = 0; /* can try inflateSync */
+ return Z_DATA_ERROR;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output. The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS). On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+
+int inflateIncomp(z)
+z_stream *z;
+{
+ if (z->state->mode != BLOCKS)
+ return Z_DATA_ERROR;
+ return inflate_addhistory(z->state->blocks, z);
+}
+
+
+int inflateSync(z)
+z_stream *z;
+{
+ uInt n; /* number of bytes to look at */
+ Bytef *p; /* pointer to bytes */
+ uInt m; /* number of marker bytes found in a row */
+ uLong r, w; /* temporaries to save total_in and total_out */
+
+ /* set up */
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->mode != BAD)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0;
+ }
+ if ((n = z->avail_in) == 0)
+ return Z_BUF_ERROR;
+ p = z->next_in;
+ m = z->state->sub.marker;
+
+ /* search */
+ while (n && m < 4)
+ {
+ if (*p == (Byte)(m < 2 ? 0 : 0xff))
+ m++;
+ else if (*p)
+ m = 0;
+ else
+ m = 4 - m;
+ p++, n--;
+ }
+
+ /* restore */
+ z->total_in += p - z->next_in;
+ z->next_in = p;
+ z->avail_in = n;
+ z->state->sub.marker = m;
+
+ /* return no joy or set up to restart on a new block */
+ if (m != 4)
+ return Z_DATA_ERROR;
+ r = z->total_in; w = z->total_out;
+ inflateReset(z);
+ z->total_in = r; z->total_out = w;
+ z->state->mode = BLOCKS;
+ return Z_OK;
+}
+
+#undef NEEDBYTE
+#undef NEXTBYTE
+
+/*+++++*/
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+ /* mode */
+ enum {
+ TYPE, /* get type bits (3, including end bit) */
+ LENS, /* get lengths for stored */
+ STORED, /* processing stored block */
+ TABLE, /* get table lengths */
+ BTREE, /* get bit lengths tree for a dynamic block */
+ DTREE, /* get length, distance trees for a dynamic block */
+ CODES, /* processing fixed or dynamic block */
+ DRY, /* output remaining window bytes */
+ DONEB, /* finished last block, done */
+ BADB} /* got a data error--stuck here */
+ mode; /* current inflate_block mode */
+
+ /* mode dependent information */
+ union {
+ uInt left; /* if STORED, bytes left to copy */
+ struct {
+ uInt table; /* table lengths (14 bits) */
+ uInt index; /* index into blens (or border) */
+ uIntf *blens; /* bit lengths of codes */
+ uInt bb; /* bit length tree depth */
+ inflate_huft *tb; /* bit length decoding tree */
+ int nblens; /* # elements allocated at blens */
+ } trees; /* if DTREE, decoding info for trees */
+ struct {
+ inflate_huft *tl, *td; /* trees to free */
+ inflate_codes_statef
+ *codes;
+ } decode; /* if CODES, current state */
+ } sub; /* submode */
+ uInt last; /* true if this block is the last block */
+
+ /* mode independent information */
+ uInt bitk; /* bits in bit buffer */
+ uLong bitb; /* bit buffer */
+ Bytef *window; /* sliding window */
+ Bytef *end; /* one byte after sliding window */
+ Bytef *read; /* window read pointer */
+ Bytef *write; /* window write pointer */
+ check_func checkfn; /* check function */
+ uLong check; /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/* update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/* get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/* output bytes */
+#define WAVAIL (q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=WAVAIL;}
+#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/* load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/* And'ing with mask[n] masks the lower n bits */
+local uInt inflate_mask[] = {
+ 0x0000,
+ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ int));
+
+/*+++++*/
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+local int inflate_fast OF((
+ uInt,
+ uInt,
+ inflate_huft *,
+ inflate_huft *,
+ inflate_blocks_statef *,
+ z_stream *));
+
+
+/*+++++*/
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local uInt border[] = { /* Order of the bit length code lengths */
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+ Notes beyond the 1.93a appnote.txt:
+
+ 1. Distance pointers never point before the beginning of the output
+ stream.
+ 2. Distance pointers can point back across blocks, up to 32k away.
+ 3. There is an implied maximum of 7 bits for the bit length table and
+ 15 bits for the actual data.
+ 4. If only one code exists, then it is encoded using one bit. (Zero
+ would be more efficient, but perhaps a little confusing.) If two
+ codes exist, they are coded using one bit each (0 and 1).
+ 5. There is no way of sending zero distance codes--a dummy must be
+ sent if there are none. (History: a pre 2.0 version of PKZIP would
+ store blocks with no distance codes, but this was discovered to be
+ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
+ zero distance codes, which is sent as one code of zero bits in
+ length.
+ 6. There are up to 286 literal/length codes. Code 256 represents the
+ end-of-block. Note however that the static length tree defines
+ 288 codes just to fill out the Huffman codes. Codes 286 and 287
+ cannot be used though, since there is no length base or extra bits
+ defined for them. Similarily, there are up to 30 distance codes.
+ However, static trees define 32 codes (all 5 bits) to fill out the
+ Huffman codes, but the last two had better not show up in the data.
+ 7. Unzip can check dynamic Huffman blocks for complete code sets.
+ The exception is that a single code would not be complete (see #4).
+ 8. The five bits following the block type is really the number of
+ literal codes sent minus 257.
+ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+ (1+6+6). Therefore, to output three times the length, you output
+ three codes (1+1+1), whereas to output four times the same length,
+ you only need two codes (1+3). Hmm.
+ 10. In the tree reconstruction algorithm, Code = Code + Increment
+ only if BitLength(i) is not zero. (Pretty obvious.)
+ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
+ 12. Note: length code 284 can represent 227-258, but length code 285
+ really is 258. The last length deserves its own, short code
+ since it gets used a lot in very redundant files. The length
+ 258 is special since 258 - 3 (the min match length) is 255.
+ 13. The literal/length and distance code bit lengths are read as a
+ single stream of lengths. It is possible (and advantageous) for
+ a repeat code (16, 17, or 18) to go across the boundary between
+ the two sets of lengths.
+ */
+
+
+local void inflate_blocks_reset(s, z, c)
+inflate_blocks_statef *s;
+z_stream *z;
+uLongf *c;
+{
+ if (s->checkfn != Z_NULL)
+ *c = s->check;
+ if (s->mode == BTREE || s->mode == DTREE)
+ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
+ if (s->mode == CODES)
+ {
+ inflate_codes_free(s->sub.decode.codes, z);
+ inflate_trees_free(s->sub.decode.td, z);
+ inflate_trees_free(s->sub.decode.tl, z);
+ }
+ s->mode = TYPE;
+ s->bitk = 0;
+ s->bitb = 0;
+ s->read = s->write = s->window;
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(0L, Z_NULL, 0);
+ Trace((stderr, "inflate: blocks reset\n"));
+}
+
+
+local inflate_blocks_statef *inflate_blocks_new(z, c, w)
+z_stream *z;
+check_func c;
+uInt w;
+{
+ inflate_blocks_statef *s;
+
+ if ((s = (inflate_blocks_statef *)ZALLOC
+ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
+ return s;
+ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
+ {
+ ZFREE(z, s, sizeof(struct inflate_blocks_state));
+ return Z_NULL;
+ }
+ s->end = s->window + w;
+ s->checkfn = c;
+ s->mode = TYPE;
+ Trace((stderr, "inflate: blocks allocated\n"));
+ inflate_blocks_reset(s, z, &s->check);
+ return s;
+}
+
+
+local int inflate_blocks(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+ uInt t; /* temporary storage */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input based on current state */
+ while (1) switch (s->mode)
+ {
+ case TYPE:
+ NEEDBITS(3)
+ t = (uInt)b & 7;
+ s->last = t & 1;
+ switch (t >> 1)
+ {
+ case 0: /* stored */
+ Trace((stderr, "inflate: stored block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ t = k & 7; /* go to byte boundary */
+ DUMPBITS(t)
+ s->mode = LENS; /* get length of stored block */
+ break;
+ case 1: /* fixed */
+ Trace((stderr, "inflate: fixed codes block%s\n",
+ s->last ? " (last)" : ""));
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+
+ inflate_trees_fixed(&bl, &bd, &tl, &td);
+ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+ if (s->sub.decode.codes == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.decode.tl = Z_NULL; /* don't try to free these */
+ s->sub.decode.td = Z_NULL;
+ }
+ DUMPBITS(3)
+ s->mode = CODES;
+ break;
+ case 2: /* dynamic */
+ Trace((stderr, "inflate: dynamic codes block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ s->mode = TABLE;
+ break;
+ case 3: /* illegal */
+ DUMPBITS(3)
+ s->mode = BADB;
+ z->msg = "invalid block type";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ break;
+ case LENS:
+ NEEDBITS(32)
+ if (((~b) >> 16) != (b & 0xffff))
+ {
+ s->mode = BADB;
+ z->msg = "invalid stored block lengths";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ s->sub.left = (uInt)b & 0xffff;
+ b = k = 0; /* dump bits */
+ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
+ s->mode = s->sub.left ? STORED : TYPE;
+ break;
+ case STORED:
+ if (n == 0)
+ LEAVE
+ NEEDOUT
+ t = s->sub.left;
+ if (t > n) t = n;
+ if (t > m) t = m;
+ zmemcpy(q, p, t);
+ p += t; n -= t;
+ q += t; m -= t;
+ if ((s->sub.left -= t) != 0)
+ break;
+ Tracev((stderr, "inflate: stored end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ s->mode = s->last ? DRY : TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14)
+ s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+ {
+ s->mode = BADB;
+ z->msg = "too many length or distance symbols";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+#endif
+ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+ if (t < 19)
+ t = 19;
+ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.trees.nblens = t;
+ DUMPBITS(14)
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ s->mode = BTREE;
+ case BTREE:
+ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+ {
+ NEEDBITS(3)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+ DUMPBITS(3)
+ }
+ while (s->sub.trees.index < 19)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+ s->sub.trees.bb = 7;
+ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+ &s->sub.trees.tb, z);
+ if (t != Z_OK)
+ {
+ r = t;
+ if (r == Z_DATA_ERROR)
+ s->mode = BADB;
+ LEAVE
+ }
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: bits tree ok\n"));
+ s->mode = DTREE;
+ case DTREE:
+ while (t = s->sub.trees.table,
+ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+ {
+ inflate_huft *h;
+ uInt i, j, c;
+
+ t = s->sub.trees.bb;
+ NEEDBITS(t)
+ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+ t = h->word.what.Bits;
+ c = h->more.Base;
+ if (c < 16)
+ {
+ DUMPBITS(t)
+ s->sub.trees.blens[s->sub.trees.index++] = c;
+ }
+ else /* c == 16..18 */
+ {
+ i = c == 18 ? 7 : c - 14;
+ j = c == 18 ? 11 : 3;
+ NEEDBITS(t + i)
+ DUMPBITS(t)
+ j += (uInt)b & inflate_mask[i];
+ DUMPBITS(i)
+ i = s->sub.trees.index;
+ t = s->sub.trees.table;
+ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+ (c == 16 && i < 1))
+ {
+ s->mode = BADB;
+ z->msg = "invalid bit length repeat";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+ do {
+ s->sub.trees.blens[i++] = c;
+ } while (--j);
+ s->sub.trees.index = i;
+ }
+ }
+ inflate_trees_free(s->sub.trees.tb, z);
+ s->sub.trees.tb = Z_NULL;
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+ inflate_codes_statef *c;
+
+ bl = 9; /* must be <= 9 for lookahead assumptions */
+ bd = 6; /* must be <= 9 for lookahead assumptions */
+ t = s->sub.trees.table;
+ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+ s->sub.trees.blens, &bl, &bd, &tl, &td, z);
+ if (t != Z_OK)
+ {
+ if (t == (uInt)Z_DATA_ERROR)
+ s->mode = BADB;
+ r = t;
+ LEAVE
+ }
+ Tracev((stderr, "inflate: trees ok\n"));
+ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+ {
+ inflate_trees_free(td, z);
+ inflate_trees_free(tl, z);
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
+ s->sub.decode.codes = c;
+ s->sub.decode.tl = tl;
+ s->sub.decode.td = td;
+ }
+ s->mode = CODES;
+ case CODES:
+ UPDATE
+ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+ return inflate_flush(s, z, r);
+ r = Z_OK;
+ inflate_codes_free(s->sub.decode.codes, z);
+ inflate_trees_free(s->sub.decode.td, z);
+ inflate_trees_free(s->sub.decode.tl, z);
+ LOAD
+ Tracev((stderr, "inflate: codes end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ if (!s->last)
+ {
+ s->mode = TYPE;
+ break;
+ }
+ if (k > 7) /* return unused byte, if any */
+ {
+ Assert(k < 16, "inflate_codes grabbed too many bytes")
+ k -= 8;
+ n++;
+ p--; /* can always return one */
+ }
+ s->mode = DRY;
+ case DRY:
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ s->mode = DONEB;
+ case DONEB:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADB:
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+local int inflate_blocks_free(s, z, c)
+inflate_blocks_statef *s;
+z_stream *z;
+uLongf *c;
+{
+ inflate_blocks_reset(s, z, c);
+ ZFREE(z, s->window, s->end - s->window);
+ ZFREE(z, s, sizeof(struct inflate_blocks_state));
+ Trace((stderr, "inflate: blocks freed\n"));
+ return Z_OK;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output. The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS). On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+local int inflate_addhistory(s, z)
+inflate_blocks_statef *s;
+z_stream *z;
+{
+ uLong b; /* bit buffer */ /* NOT USED HERE */
+ uInt k; /* bits in bit buffer */ /* NOT USED HERE */
+ uInt t; /* temporary storage */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ if (s->read != s->write)
+ return Z_STREAM_ERROR;
+ if (s->mode != TYPE)
+ return Z_DATA_ERROR;
+
+ /* we're ready to rock */
+ LOAD
+ /* while there is input ready, copy to output buffer, moving
+ * pointers as needed.
+ */
+ while (n) {
+ t = n; /* how many to do */
+ /* is there room until end of buffer? */
+ if (t > m) t = m;
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(s->check, q, t);
+ zmemcpy(q, p, t);
+ q += t;
+ p += t;
+ n -= t;
+ z->total_out += t;
+ s->read = q; /* drag read pointer forward */
+/* WRAP */ /* expand WRAP macro by hand to handle s->read */
+ if (q == s->end) {
+ s->read = q = s->window;
+ m = WAVAIL;
+ }
+ }
+ UPDATE
+ return Z_OK;
+}
+
+
+/*
+ * At the end of a Deflate-compressed PPP packet, we expect to have seen
+ * a `stored' block type value but not the (zero) length bytes.
+ */
+local int inflate_packet_flush(s)
+ inflate_blocks_statef *s;
+{
+ if (s->mode != LENS)
+ return Z_DATA_ERROR;
+ s->mode = TYPE;
+ return Z_OK;
+}
+
+
+/*+++++*/
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+ uIntf *, /* code lengths in bits */
+ uInt, /* number of codes */
+ uInt, /* number of "simple" codes */
+ uIntf *, /* list of base values for non-simple codes */
+ uIntf *, /* list of extra bits for non-simple codes */
+ inflate_huft * FAR*,/* result: starting table */
+ uIntf *, /* maximum lookup bits (returns actual) */
+ z_stream *)); /* for zalloc function */
+
+local voidpf falloc OF((
+ voidpf, /* opaque pointer (not used) */
+ uInt, /* number of items */
+ uInt)); /* size of item */
+
+local void ffree OF((
+ voidpf q, /* opaque pointer (not used) */
+ voidpf p, /* what to free (not used) */
+ uInt n)); /* number of bytes (not used) */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ /* actually lengths - 2; also see note #13 above about 258 */
+local uInt cplext[] = { /* Extra bits for literal codes 257..285 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */
+local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+local uInt cpdext[] = { /* Extra bits for distance codes */
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+
+/*
+ Huffman code decoding is performed using a multi-level table lookup.
+ The fastest way to decode is to simply build a lookup table whose
+ size is determined by the longest code. However, the time it takes
+ to build this table can also be a factor if the data being decoded
+ is not very long. The most common codes are necessarily the
+ shortest codes, so those codes dominate the decoding time, and hence
+ the speed. The idea is you can have a shorter table that decodes the
+ shorter, more probable codes, and then point to subsidiary tables for
+ the longer codes. The time it costs to decode the longer codes is
+ then traded against the time it takes to make longer tables.
+
+ This results of this trade are in the variables lbits and dbits
+ below. lbits is the number of bits the first level table for literal/
+ length codes can decode in one step, and dbits is the same thing for
+ the distance codes. Subsequent tables are also less than or equal to
+ those sizes. These values may be adjusted either when all of the
+ codes are shorter than that, in which case the longest code length in
+ bits is used, or when the shortest code is *longer* than the requested
+ table size, in which case the length of the shortest code in bits is
+ used.
+
+ There are two different values for the two tables, since they code a
+ different number of possibilities each. The literal/length table
+ codes 286 possible values, or in a flat code, a little over eight
+ bits. The distance table codes 30 possible values, or a little less
+ than five bits, flat. The optimum values for speed end up being
+ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+ The optimum values may differ though from machine to machine, and
+ possibly even between compilers. Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15 /* maximum bit length of any code */
+#define N_MAX 288 /* maximum number of codes in any set */
+
+#ifdef DEBUG_ZLIB
+ uInt inflate_hufts;
+#endif
+
+local int huft_build(b, n, s, d, e, t, m, zs)
+uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
+uInt n; /* number of codes (assumed <= N_MAX) */
+uInt s; /* number of simple-valued codes (0..s-1) */
+uIntf *d; /* list of base values for non-simple codes */
+uIntf *e; /* list of extra bits for non-simple codes */
+inflate_huft * FAR *t; /* result: starting table */
+uIntf *m; /* maximum lookup bits, returns actual */
+z_stream *zs; /* for zalloc function */
+/* Given a list of code lengths and a maximum table size, make a set of
+ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
+ if the given code set is incomplete (the tables are still built in this
+ case), Z_DATA_ERROR if the input is invalid (all zero length codes or an
+ over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */
+{
+
+ uInt a; /* counter for codes of length k */
+ uInt c[BMAX+1]; /* bit length count table */
+ uInt f; /* i repeats in table every f entries */
+ int g; /* maximum code length */
+ int h; /* table level */
+ register uInt i; /* counter, current code */
+ register uInt j; /* counter */
+ register int k; /* number of bits in current code */
+ int l; /* bits per table (returned in m) */
+ register uIntf *p; /* pointer into c[], b[], or v[] */
+ inflate_huft *q; /* points to current table */
+ struct inflate_huft_s r; /* table entry for structure assignment */
+ inflate_huft *u[BMAX]; /* table stack */
+ uInt v[N_MAX]; /* values in order of bit length */
+ register int w; /* bits before this table == (l * h) */
+ uInt x[BMAX+1]; /* bit offsets, then code stack */
+ uIntf *xp; /* pointer into x */
+ int y; /* number of dummy codes added */
+ uInt z; /* number of entries in current table */
+
+
+ /* Generate counts for each bit length */
+ p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+ C4 /* clear c[]--assume BMAX+1 is 16 */
+ p = b; i = n;
+ do {
+ c[*p++]++; /* assume all entries <= BMAX */
+ } while (--i);
+ if (c[0] == n) /* null input--all zero length codes */
+ {
+ *t = (inflate_huft *)Z_NULL;
+ *m = 0;
+ return Z_OK;
+ }
+
+
+ /* Find minimum and maximum length, bound *m by those */
+ l = *m;
+ for (j = 1; j <= BMAX; j++)
+ if (c[j])
+ break;
+ k = j; /* minimum code length */
+ if ((uInt)l < j)
+ l = j;
+ for (i = BMAX; i; i--)
+ if (c[i])
+ break;
+ g = i; /* maximum code length */
+ if ((uInt)l > i)
+ l = i;
+ *m = l;
+
+
+ /* Adjust last length count to fill out codes, if needed */
+ for (y = 1 << j; j < i; j++, y <<= 1)
+ if ((y -= c[j]) < 0)
+ return Z_DATA_ERROR;
+ if ((y -= c[i]) < 0)
+ return Z_DATA_ERROR;
+ c[i] += y;
+
+
+ /* Generate starting offsets into the value table for each length */
+ x[1] = j = 0;
+ p = c + 1; xp = x + 2;
+ while (--i) { /* note that i == g from above */
+ *xp++ = (j += *p++);
+ }
+
+
+ /* Make a table of values in order of bit lengths */
+ p = b; i = 0;
+ do {
+ if ((j = *p++) != 0)
+ v[x[j]++] = i;
+ } while (++i < n);
+
+
+ /* Generate the Huffman codes and for each, make the table entries */
+ x[0] = i = 0; /* first Huffman code is zero */
+ p = v; /* grab values in bit order */
+ h = -1; /* no tables yet--level -1 */
+ w = -l; /* bits decoded == (l * h) */
+ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
+ q = (inflate_huft *)Z_NULL; /* ditto */
+ z = 0; /* ditto */
+
+ /* go through the bit lengths (k already is bits in shortest code) */
+ for (; k <= g; k++)
+ {
+ a = c[k];
+ while (a--)
+ {
+ /* here i is the Huffman code of length k bits for value *p */
+ /* make tables up to required level */
+ while (k > w + l)
+ {
+ h++;
+ w += l; /* previous table always l bits */
+
+ /* compute minimum size table less than or equal to l bits */
+ z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */
+ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
+ { /* too few codes for k-w bit table */
+ f -= a + 1; /* deduct codes from patterns left */
+ xp = c + k;
+ if (j < z)
+ while (++j < z) /* try smaller tables up to z bits */
+ {
+ if ((f <<= 1) <= *++xp)
+ break; /* enough codes to use up j bits */
+ f -= *xp; /* else deduct codes from patterns */
+ }
+ }
+ z = 1 << j; /* table entries for j-bit table */
+
+ /* allocate and link in new table */
+ if ((q = (inflate_huft *)ZALLOC
+ (zs,z + 1,sizeof(inflate_huft))) == Z_NULL)
+ {
+ if (h)
+ inflate_trees_free(u[0], zs);
+ return Z_MEM_ERROR; /* not enough memory */
+ }
+ q->word.Nalloc = z + 1;
+#ifdef DEBUG_ZLIB
+ inflate_hufts += z + 1;
+#endif
+ *t = q + 1; /* link to list for huft_free() */
+ *(t = &(q->next)) = Z_NULL;
+ u[h] = ++q; /* table starts after link */
+
+ /* connect to last table, if there is one */
+ if (h)
+ {
+ x[h] = i; /* save pattern for backing up */
+ r.bits = (Byte)l; /* bits to dump before this table */
+ r.exop = (Byte)j; /* bits in this table */
+ r.next = q; /* pointer to this table */
+ j = i >> (w - l); /* (get around Turbo C bug) */
+ u[h-1][j] = r; /* connect to last table */
+ }
+ }
+
+ /* set up table entry in r */
+ r.bits = (Byte)(k - w);
+ if (p >= v + n)
+ r.exop = 128 + 64; /* out of values--invalid code */
+ else if (*p < s)
+ {
+ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
+ r.base = *p++; /* simple code is just the value */
+ }
+ else
+ {
+ r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */
+ r.base = d[*p++ - s];
+ }
+
+ /* fill code-like entries with r */
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f)
+ q[j] = r;
+
+ /* backwards increment the k-bit code i */
+ for (j = 1 << (k - 1); i & j; j >>= 1)
+ i ^= j;
+ i ^= j;
+
+ /* backup over finished tables */
+ while ((i & ((1 << w) - 1)) != x[h])
+ {
+ h--; /* don't need to update q */
+ w -= l;
+ }
+ }
+ }
+
+
+ /* Return Z_BUF_ERROR if we were given an incomplete table */
+ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+local int inflate_trees_bits(c, bb, tb, z)
+uIntf *c; /* 19 code lengths */
+uIntf *bb; /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+z_stream *z; /* for zfree function */
+{
+ int r;
+
+ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z);
+ if (r == Z_DATA_ERROR)
+ z->msg = "oversubscribed dynamic bit lengths tree";
+ else if (r == Z_BUF_ERROR)
+ {
+ inflate_trees_free(*tb, z);
+ z->msg = "incomplete dynamic bit lengths tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+}
+
+
+local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z)
+uInt nl; /* number of literal/length codes */
+uInt nd; /* number of distance codes */
+uIntf *c; /* that many (total) code lengths */
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+z_stream *z; /* for zfree function */
+{
+ int r;
+
+ /* build literal/length tree */
+ if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK)
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = "oversubscribed literal/length tree";
+ else if (r == Z_BUF_ERROR)
+ {
+ inflate_trees_free(*tl, z);
+ z->msg = "incomplete literal/length tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+ }
+
+ /* build distance tree */
+ if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK)
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = "oversubscribed literal/length tree";
+ else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+ r = Z_OK;
+ }
+#else
+ inflate_trees_free(*td, z);
+ z->msg = "incomplete literal/length tree";
+ r = Z_DATA_ERROR;
+ }
+ inflate_trees_free(*tl, z);
+ return r;
+#endif
+ }
+
+ /* done */
+ return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+local int fixed_lock = 0;
+local int fixed_built = 0;
+#define FIXEDH 530 /* number of hufts used by fixed tables */
+local uInt fixed_left = FIXEDH;
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+
+
+local voidpf falloc(q, n, s)
+voidpf q; /* opaque pointer (not used) */
+uInt n; /* number of items */
+uInt s; /* size of item */
+{
+ Assert(s == sizeof(inflate_huft) && n <= fixed_left,
+ "inflate_trees falloc overflow");
+ if (q) s++; /* to make some compilers happy */
+ fixed_left -= n;
+ return (voidpf)(fixed_mem + fixed_left);
+}
+
+
+local void ffree(q, p, n)
+voidpf q;
+voidpf p;
+uInt n;
+{
+ Assert(0, "inflate_trees ffree called!");
+ if (q) q = p; /* to make some compilers happy */
+}
+
+
+local int inflate_trees_fixed(bl, bd, tl, td)
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+{
+ /* build fixed tables if not built already--lock out other instances */
+ while (++fixed_lock > 1)
+ fixed_lock--;
+ if (!fixed_built)
+ {
+ int k; /* temporary variable */
+ unsigned c[288]; /* length list for huft_build */
+ z_stream z; /* for falloc function */
+
+ /* set up fake z_stream for memory routines */
+ z.zalloc = falloc;
+ z.zfree = ffree;
+ z.opaque = Z_NULL;
+
+ /* literal table */
+ for (k = 0; k < 144; k++)
+ c[k] = 8;
+ for (; k < 256; k++)
+ c[k] = 9;
+ for (; k < 280; k++)
+ c[k] = 7;
+ for (; k < 288; k++)
+ c[k] = 8;
+ fixed_bl = 7;
+ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z);
+
+ /* distance table */
+ for (k = 0; k < 30; k++)
+ c[k] = 5;
+ fixed_bd = 5;
+ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z);
+
+ /* done */
+ fixed_built = 1;
+ }
+ fixed_lock--;
+ *bl = fixed_bl;
+ *bd = fixed_bd;
+ *tl = fixed_tl;
+ *td = fixed_td;
+ return Z_OK;
+}
+
+
+local int inflate_trees_free(t, z)
+inflate_huft *t; /* table to free */
+z_stream *z; /* for zfree function */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+ list of the tables it made, with the links in a dummy first entry of
+ each table. */
+{
+ register inflate_huft *p, *q;
+
+ /* Go through linked list, freeing from the malloced (t[-1]) address. */
+ p = t;
+ while (p != Z_NULL)
+ {
+ q = (--p)->next;
+ ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft));
+ p = q;
+ }
+ return Z_OK;
+}
+
+/*+++++*/
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+ /* mode */
+ enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ START, /* x: set up for LEN */
+ LEN, /* i: get length/literal/eob next */
+ LENEXT, /* i: getting length extra (have base) */
+ DIST, /* i: get distance next */
+ DISTEXT, /* i: getting distance extra */
+ COPY, /* o: copying bytes in window, waiting for space */
+ LIT, /* o: got literal, waiting for output space */
+ WASH, /* o: got eob, possibly still output waiting */
+ END, /* x: got eob and all data flushed */
+ BADCODE} /* x: got error */
+ mode; /* current inflate_codes mode */
+
+ /* mode dependent information */
+ uInt len;
+ union {
+ struct {
+ inflate_huft *tree; /* pointer into tree */
+ uInt need; /* bits needed */
+ } code; /* if LEN or DIST, where in tree */
+ uInt lit; /* if LIT, literal */
+ struct {
+ uInt get; /* bits to get for extra */
+ uInt dist; /* distance back to copy from */
+ } copy; /* if EXT or COPY, where and how much */
+ } sub; /* submode */
+
+ /* mode independent information */
+ Byte lbits; /* ltree bits decoded per branch */
+ Byte dbits; /* dtree bits decoder per branch */
+ inflate_huft *ltree; /* literal/length/eob tree */
+ inflate_huft *dtree; /* distance tree */
+
+};
+
+
+local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl, *td;
+z_stream *z;
+{
+ inflate_codes_statef *c;
+
+ if ((c = (inflate_codes_statef *)
+ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+ {
+ c->mode = START;
+ c->lbits = (Byte)bl;
+ c->dbits = (Byte)bd;
+ c->ltree = tl;
+ c->dtree = td;
+ Tracev((stderr, "inflate: codes new\n"));
+ }
+ return c;
+}
+
+
+local int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+ uInt j; /* temporary storage */
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ Bytef *f; /* pointer to copy strings from */
+ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input and output based on current state */
+ while (1) switch (c->mode)
+ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ case START: /* x: set up for LEN */
+#ifndef SLOW
+ if (m >= 258 && n >= 10)
+ {
+ UPDATE
+ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+ LOAD
+ if (r != Z_OK)
+ {
+ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+ break;
+ }
+ }
+#endif /* !SLOW */
+ c->sub.code.need = c->lbits;
+ c->sub.code.tree = c->ltree;
+ c->mode = LEN;
+ case LEN: /* i: get length/literal/eob next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e == 0) /* literal */
+ {
+ c->sub.lit = t->base;
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", t->base));
+ c->mode = LIT;
+ break;
+ }
+ if (e & 16) /* length */
+ {
+ c->sub.copy.get = e & 15;
+ c->len = t->base;
+ c->mode = LENEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t->next;
+ break;
+ }
+ if (e & 32) /* end of block */
+ {
+ Tracevv((stderr, "inflate: end of block\n"));
+ c->mode = WASH;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = "invalid literal/length code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case LENEXT: /* i: getting length extra (have base) */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->len += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ c->sub.code.need = c->dbits;
+ c->sub.code.tree = c->dtree;
+ Tracevv((stderr, "inflate: length %u\n", c->len));
+ c->mode = DIST;
+ case DIST: /* i: get distance next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e & 16) /* distance */
+ {
+ c->sub.copy.get = e & 15;
+ c->sub.copy.dist = t->base;
+ c->mode = DISTEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t->next;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = "invalid distance code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case DISTEXT: /* i: getting distance extra */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->sub.copy.dist += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
+ c->mode = COPY;
+ case COPY: /* o: copying bytes in window, waiting for space */
+#ifndef __TURBOC__ /* Turbo C bug for following expression */
+ f = (uInt)(q - s->window) < c->sub.copy.dist ?
+ s->end - (c->sub.copy.dist - (q - s->window)) :
+ q - c->sub.copy.dist;
+#else
+ f = q - c->sub.copy.dist;
+ if ((uInt)(q - s->window) < c->sub.copy.dist)
+ f = s->end - (c->sub.copy.dist - (q - s->window));
+#endif
+ while (c->len)
+ {
+ NEEDOUT
+ OUTBYTE(*f++)
+ if (f == s->end)
+ f = s->window;
+ c->len--;
+ }
+ c->mode = START;
+ break;
+ case LIT: /* o: got literal, waiting for output space */
+ NEEDOUT
+ OUTBYTE(c->sub.lit)
+ c->mode = START;
+ break;
+ case WASH: /* o: got eob, possibly more output */
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ c->mode = END;
+ case END:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADCODE: /* x: got error */
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+local void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_stream *z;
+{
+ ZFREE(z, c, sizeof(struct inflate_codes_state));
+ Tracev((stderr, "inflate: codes free\n"));
+}
+
+/*+++++*/
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+ uInt n;
+ Bytef *p, *q;
+
+ /* local copies of source and destination pointers */
+ p = z->next_out;
+ q = s->read;
+
+ /* compute number of bytes to copy as far as end of window */
+ n = (uInt)((q <= s->write ? s->write : s->end) - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy as far as end of window */
+ if (p != NULL) {
+ zmemcpy(p, q, n);
+ p += n;
+ }
+ q += n;
+
+ /* see if more to copy at beginning of window */
+ if (q == s->end)
+ {
+ /* wrap pointers */
+ q = s->window;
+ if (s->write == s->end)
+ s->write = s->window;
+
+ /* compute bytes to copy */
+ n = (uInt)(s->write - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy */
+ if (p != NULL) {
+ zmemcpy(p, q, n);
+ p += n;
+ }
+ q += n;
+ }
+
+ /* update pointers */
+ z->next_out = p;
+ s->read = q;
+
+ /* done */
+ return r;
+}
+
+
+/*+++++*/
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;}
+
+/* Called with number of bytes left to write in window at least 258
+ (the maximum string length) and number of input bytes available
+ at least ten. The ten bytes are six bytes for the longest length/
+ distance pair plus four bytes for overloading the bit buffer. */
+
+local int inflate_fast(bl, bd, tl, td, s, z)
+uInt bl, bd;
+inflate_huft *tl, *td;
+inflate_blocks_statef *s;
+z_stream *z;
+{
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ uInt ml; /* mask for literal/length tree */
+ uInt md; /* mask for distance tree */
+ uInt c; /* bytes to copy */
+ uInt d; /* distance back to copy from */
+ Bytef *r; /* copy source pointer */
+
+ /* load input, output, bit values */
+ LOAD
+
+ /* initialize masks */
+ ml = inflate_mask[bl];
+ md = inflate_mask[bd];
+
+ /* do until not enough input or output space for fast loop */
+ do { /* assume called with m >= 258 && n >= 10 */
+ /* get literal/length code */
+ GRABBITS(20) /* max bits for literal/length code */
+ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ continue;
+ }
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits for length */
+ e &= 15;
+ c = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * length %u\n", c));
+
+ /* decode distance base of block to copy */
+ GRABBITS(15); /* max bits for distance code */
+ e = (t = td + ((uInt)b & md))->exop;
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits to add to distance base */
+ e &= 15;
+ GRABBITS(e) /* get extra bits (up to 13) */
+ d = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * distance %u\n", d));
+
+ /* do the copy */
+ m -= c;
+ if ((uInt)(q - s->window) >= d) /* offset before dest */
+ { /* just copy */
+ r = q - d;
+ *q++ = *r++; c--; /* minimum count is three, */
+ *q++ = *r++; c--; /* so unroll loop a little */
+ }
+ else /* else offset after destination */
+ {
+ e = d - (q - s->window); /* bytes from offset to end */
+ r = s->end - e; /* pointer to offset */
+ if (c > e) /* if source crosses, */
+ {
+ c -= e; /* copy to end of window */
+ do {
+ *q++ = *r++;
+ } while (--e);
+ r = s->window; /* copy rest from start of window */
+ }
+ }
+ do { /* copy all or what's left */
+ *q++ = *r++;
+ } while (--c);
+ break;
+ }
+ else if ((e & 64) == 0)
+ e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop;
+ else
+ {
+ z->msg = "invalid distance code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ break;
+ }
+ if ((e & 64) == 0)
+ {
+ if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ break;
+ }
+ }
+ else if (e & 32)
+ {
+ Tracevv((stderr, "inflate: * end of block\n"));
+ UNGRAB
+ UPDATE
+ return Z_STREAM_END;
+ }
+ else
+ {
+ z->msg = "invalid literal/length code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ } while (m >= 258 && n >= 10);
+
+ /* not enough input or output--restore pointers and return */
+ UNGRAB
+ UPDATE
+ return Z_OK;
+}
+
+
+/*+++++*/
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */
+
+char *zlib_version = ZLIB_VERSION;
+
+char *z_errmsg[] = {
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+""};
+
+
+/*+++++*/
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf) {s1 += *buf++; s2 += s1;}
+#define DO2(buf) DO1(buf); DO1(buf);
+#define DO4(buf) DO2(buf); DO2(buf);
+#define DO8(buf) DO4(buf); DO4(buf);
+#define DO16(buf) DO8(buf); DO8(buf);
+
+/* ========================================================================= */
+uLong adler32(adler, buf, len)
+ uLong adler;
+ Bytef *buf;
+ uInt len;
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == Z_NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ k -= 16;
+ }
+ if (k != 0) do {
+ DO1(buf);
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ return (s2 << 16) | s1;
+}
diff --git a/mdk-stage1/ppp/pppdump/zlib.h b/mdk-stage1/ppp/pppdump/zlib.h
new file mode 100644
index 000000000..9c7ac734d
--- /dev/null
+++ b/mdk-stage1/ppp/pppdump/zlib.h
@@ -0,0 +1,631 @@
+/* $Id$ */
+
+/*
+ * This file is derived from zlib.h and zconf.h from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.
+ */
+
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 0.95, Aug 16th, 1995.
+
+ Copyright (C) 1995 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ gzip@prep.ai.mit.edu madler@alumni.caltech.edu
+ */
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+/* #include "zconf.h" */ /* included directly here */
+
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */
+
+/*
+ The library does not install any signal handler. It is recommended to
+ add at least a handler for SIGSEGV when decompressing; the library checks
+ the consistency of the input data whenever possible but may go nuts
+ for some forms of corrupted input.
+ */
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints
+ * at addresses which are not a multiple of their size.
+ * Under DOS, -DFAR=far or -DFAR=__far may be needed.
+ */
+
+#ifndef STDC
+# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus)
+# define STDC
+# endif
+#endif
+
+#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */
+# include <unix.h>
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2 */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ 1 << (windowBits+2) + 1 << (memLevel+9)
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+typedef unsigned char Byte; /* 8 bits */
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+typedef Byte FAR Bytef;
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+/* end of original zconf.h */
+
+#define ZLIB_VERSION "0.95P"
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms may be added later and will have the same
+ stream interface.
+
+ For compression the application must provide the output buffer and
+ may optionally provide the input buffer for optimization. For decompression,
+ the application must provide the input buffer and may optionally provide
+ the output buffer for optimization.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidp opaque; /* private data object passed to zalloc and zfree */
+
+ Byte data_type; /* best guess about the data type: ascii or binary */
+
+} z_stream;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1
+#define Z_FULL_FLUSH 2
+#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */
+#define Z_FINISH 4
+#define Z_PACKET_FLUSH 5
+/* See deflate() below for the usage of these constants */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+/* error codes for the compression/decompression functions */
+
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_DEFAULT_STRATEGY 0
+
+#define Z_BINARY 0
+#define Z_ASCII 1
+#define Z_UNKNOWN 2
+/* Used to set the data_type field */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+extern char *zlib_version;
+/* The application can compare zlib_version and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ */
+
+ /* basic functions */
+
+extern int deflateInit OF((z_stream *strm, int level));
+/*
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 1 and 9:
+ 1 gives best speed, 9 gives best compression. Z_DEFAULT_COMPRESSION requests
+ a default compromise between speed and compression (currently equivalent
+ to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level.
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+extern int deflate OF((z_stream *strm, int flush));
+/*
+ Performs one or both of the following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate().
+
+ If the parameter flush is set to Z_PARTIAL_FLUSH, the current compression
+ block is terminated and flushed to the output buffer so that the
+ decompressor can get all input data available so far. For method 9, a future
+ variant on method 8, the current block will be flushed but not terminated.
+ If flush is set to Z_FULL_FLUSH, the compression block is terminated, a
+ special marker is output and the compression dictionary is discarded; this
+ is useful to allow the decompressor to synchronize if one compressed block
+ has been damaged (see inflateSync below). Flushing degrades compression and
+ so should be used only when necessary. Using Z_FULL_FLUSH too often can
+ seriously degrade the compression. If deflate returns with avail_out == 0,
+ this function must be called again with the same value of the flush
+ parameter and more output space (updated avail_out), until the flush is
+ complete (deflate returns with non-zero avail_out).
+
+ If the parameter flush is set to Z_PACKET_FLUSH, the compression
+ block is terminated, and a zero-length stored block is output,
+ omitting the length bytes (the effect of this is that the 3-bit type
+ code 000 for a stored block is output, and the output is then
+ byte-aligned). This is designed for use at the end of a PPP packet.
+ In addition, if the current compression block contains all the data
+ since the last Z_PACKET_FLUSH, it is never output as a stored block.
+ If the current compression block output as a static or dynamic block
+ would not be at least `minCompression' bytes smaller than the
+ original data, then nothing is output for that block. (The type
+ code for the zero-length stored block is still output, resulting in
+ a single zero byte being output for the whole packet.)
+ `MinCompression' is a parameter to deflateInit2, or 0 if deflateInit
+ is used.
+
+ If the parameter flush is set to Z_FINISH, all pending input is processed,
+ all pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ 0.1% larger than avail_in plus 12 bytes. If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() may update data_type if it can make a good guess about
+ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible.
+*/
+
+
+extern int deflateEnd OF((z_stream *strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent. In the error case, msg may be set
+ but then points to a static string (which must not be deallocated).
+*/
+
+
+extern int inflateInit OF((z_stream *strm));
+/*
+ Initializes the internal stream state for decompression. The fields
+ zalloc and zfree must be initialized before by the caller. If zalloc and
+ zfree are set to Z_NULL, inflateInit updates them to use default allocation
+ functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory. msg is set to null if there is no error message.
+ inflateInit does not perform any decompression: this will be done by
+ inflate().
+*/
+
+
+extern int inflate OF((z_stream *strm, int flush));
+/*
+ Performs one or both of the following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() always provides as much output as possible
+ (until there is no more input data or no more space in the output buffer).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate().
+
+ If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH,
+ inflate flushes as much output as possible to the output buffer. The
+ flushing behavior of inflate is not specified for values of the flush
+ parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the
+ current implementation actually flushes as much output as possible
+ anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data
+ has been consumed, it is expecting to see the length field of a stored
+ block; if not, it returns Z_DATA_ERROR.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster routine
+ may be used for the single inflate() call.
+
+ inflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if the end of the
+ compressed data has been reached and all uncompressed output has been
+ produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if
+ the stream structure was inconsistent (for example if next_in or next_out
+ was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no
+ progress is possible or if there was not enough room in the output buffer
+ when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then
+ call inflateSync to look for a good compression block. */
+
+
+extern int inflateEnd OF((z_stream *strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+extern int deflateInit2 OF((z_stream *strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy,
+ int minCompression));
+/*
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc and zfree must be initialized before by the caller.
+
+ The method parameter is the compression method. It must be 8 in this
+ version of the library. (Method 9 will allow a 64K history buffer and
+ partial block flushes.)
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library (the value 16 will be allowed for method 9). Larger
+ values of this parameter result in better compression at the expense of
+ memory usage. The default value is 15 if deflateInit is used instead.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use
+ the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data
+ produced by a filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman
+ encoding only (no string match). Filtered data consists mostly of small
+ values with a somewhat random distribution. In this case, the
+ compression algorithm is tuned to compress them better. The strategy
+ parameter only affects the compression ratio but not the correctness of
+ the compressed output even if it is not set appropriately.
+
+ The minCompression parameter specifies the minimum reduction in size
+ required for a compressed block to be output when Z_PACKET_FLUSH is
+ used (see the description of deflate above).
+
+ If next_in is not null, the library will use this buffer to hold also
+ some history information; the buffer must either hold the entire input
+ data, or have at least 1<<(windowBits+1) bytes and be writable. If next_in
+ is null, the library will allocate its own history buffer (and leave next_in
+ null). next_out need not be provided here but must be provided by the
+ application for the next call of deflate().
+
+ If the history buffer is provided by the application, next_in must
+ must never be changed by the application since the compressor maintains
+ information inside this buffer from call to call; the application
+ must provide more input only by increasing avail_in. next_in is always
+ reset by the library in this case.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+ an invalid method). msg is set to null if there is no error message.
+ deflateInit2 does not perform any compression: this will be done by
+ deflate().
+*/
+
+extern int deflateCopy OF((z_stream *dest,
+ z_stream *source));
+/*
+ Sets the destination stream as a complete copy of the source stream. If
+ the source stream is using an application-supplied history buffer, a new
+ buffer is allocated for the destination stream. The compressed output
+ buffer is always application-supplied. It's the responsibility of the
+ application to provide the correct values of next_out and avail_out for the
+ next call of deflate.
+
+ This function is useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+extern int deflateReset OF((z_stream *strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int inflateInit2 OF((z_stream *strm,
+ int windowBits));
+/*
+ This is another version of inflateInit with more compression options. The
+ fields next_out, zalloc and zfree must be initialized before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library (the value 16 will be allowed soon). The
+ default value is 15 if inflateInit is used instead. If a compressed stream
+ with a larger window size is given as input, inflate() will return with
+ the error code Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ If next_out is not null, the library will use this buffer for the history
+ buffer; the buffer must either be large enough to hold the entire output
+ data, or have at least 1<<windowBits bytes. If next_out is null, the
+ library will allocate its own buffer (and leave next_out null). next_in
+ need not be provided here but must be provided by the application for the
+ next call of inflate().
+
+ If the history buffer is provided by the application, next_out must
+ never be changed by the application since the decompressor maintains
+ history information inside this buffer from call to call; the application
+ can only reset next_out to the beginning of the history buffer when
+ avail_out is zero and all output has been consumed.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+ windowBits < 8). msg is set to null if there is no error message.
+ inflateInit2 does not perform any decompression: this will be done by
+ inflate().
+*/
+
+extern int inflateSync OF((z_stream *strm));
+/*
+ Skips invalid compressed data until the special marker (see deflate()
+ above) can be found, or until all available input is skipped. No output
+ is provided.
+
+ inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no marker has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+extern int inflateReset OF((z_stream *strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int inflateIncomp OF((z_stream *strm));
+/*
+ This function adds the data at next_in (avail_in bytes) to the output
+ history without performing any output. There must be no pending output,
+ and the decompressor must be expecting to see the start of a block.
+ Calling this function is equivalent to decompressing a stored block
+ containing the data at next_in (except that the data is not output).
+*/
+
+ /* checksum functions */
+
+/*
+ This function is not related to compression but is exported
+ anyway because it might be useful in applications using the
+ compression library.
+*/
+
+extern uLong adler32 OF((uLong adler, Bytef *buf, uInt len));
+
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+#ifndef _Z_UTIL_H
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+#endif /* _ZLIB_H */
diff --git a/mdk-stage1/ppp/pppstats/Makefile.linux b/mdk-stage1/ppp/pppstats/Makefile.linux
new file mode 100644
index 000000000..ef0764c5d
--- /dev/null
+++ b/mdk-stage1/ppp/pppstats/Makefile.linux
@@ -0,0 +1,32 @@
+#
+# pppstats makefile
+# $Id$
+#
+
+PPPSTATSRCS = pppstats.c
+PPPSTATOBJS = pppstats.o
+
+#CC = gcc
+COPTS = -O
+COMPILE_FLAGS = -D_linux_ -I../include
+LIBS =
+
+INSTALL= install -o root -g daemon
+
+CFLAGS = $(COPTS) $(COMPILE_FLAGS)
+
+all: pppstats
+
+install: pppstats
+ $(INSTALL) -s -c pppstats $(BINDIR)/pppstats
+ $(INSTALL) -c -m 444 pppstats.8 $(MANDIR)/man8/pppstats.8
+
+pppstats: $(PPPSTATSRCS)
+ $(CC) $(CFLAGS) -o pppstats pppstats.c $(LIBS)
+
+clean:
+ rm -f pppstats *~ #* core
+
+depend:
+ cpp -M $(CFLAGS) $(PPPSTATSRCS) >.depend
+# makedepend $(CFLAGS) $(PPPSTATSRCS)
diff --git a/mdk-stage1/ppp/pppstats/Makefile.sol2 b/mdk-stage1/ppp/pppstats/Makefile.sol2
new file mode 100644
index 000000000..a6544e61b
--- /dev/null
+++ b/mdk-stage1/ppp/pppstats/Makefile.sol2
@@ -0,0 +1,20 @@
+#
+# pppstats Makefile for SVR4 systems
+# $Id$
+#
+
+include ../solaris/Makedefs
+
+CFLAGS = -DSTREAMS -I../include $(COPTS)
+
+all: pppstats
+
+pppstats: pppstats.c
+ $(CC) $(CFLAGS) -o pppstats pppstats.c
+
+install: pppstats
+ $(INSTALL) -f $(BINDIR) pppstats
+ $(INSTALL) -m 444 -f $(MANDIR)/man8 pppstats.8
+
+clean:
+ rm -f pppstats *~ core
diff --git a/mdk-stage1/ppp/pppstats/Makefile.sunos4 b/mdk-stage1/ppp/pppstats/Makefile.sunos4
new file mode 100644
index 000000000..2a036f28f
--- /dev/null
+++ b/mdk-stage1/ppp/pppstats/Makefile.sunos4
@@ -0,0 +1,30 @@
+#
+# pppstats makefile
+# $Id$
+#
+
+include ../sunos4/Makedefs
+
+PPPSTATSRCS = pppstats.c
+PPPSTATOBJS = pppstats.o
+
+COMPILE_FLAGS = -DSTREAMS -DSUNOS4
+LIBS =
+
+CFLAGS = -I../include $(COPTS) $(COMPILE_FLAGS)
+
+all: pppstats
+
+install: pppstats
+ $(INSTALL) -c pppstats $(BINDIR)/pppstats
+ $(INSTALL) -c -m 444 pppstats.8 $(MANDIR)/man8/pppstats.8
+
+pppstats: $(PPPSTATSRCS)
+ $(CC) $(CFLAGS) -o pppstats pppstats.c $(LIBS)
+
+clean:
+ rm -f pppstats *~ #* core
+
+depend:
+ cpp -M $(CFLAGS) $(PPPSTATSRCS) >.depend
+# makedepend $(CFLAGS) $(PPPSTATSRCS)
diff --git a/mdk-stage1/ppp/pppstats/pppstats.8 b/mdk-stage1/ppp/pppstats/pppstats.8
new file mode 100644
index 000000000..1ba8d5779
--- /dev/null
+++ b/mdk-stage1/ppp/pppstats/pppstats.8
@@ -0,0 +1,217 @@
+.\" @(#) $Id$
+.TH PPPSTATS 8 "26 June 1995"
+.SH NAME
+pppstats \- print PPP statistics
+.SH SYNOPSIS
+.B pppstats
+[
+.B -a
+] [
+.B -v
+] [
+.B -r
+] [
+.B -z
+] [
+.B -c
+.I <count>
+] [
+.B -w
+.I <secs>
+] [
+.I interface
+]
+.ti 12
+.SH DESCRIPTION
+The
+.B pppstats
+utility reports PPP-related statistics at regular intervals for the
+specified PPP interface. If the interface is unspecified, it will
+default to ppp0.
+The display is split horizontally
+into input and output sections containing columns of statistics
+describing the properties and volume of packets received and
+transmitted by the interface.
+.PP
+The options are as follows:
+.TP
+.B -a
+Display absolute values rather than deltas. With this option, all
+reports show statistics for the time since the link was initiated.
+Without this option, the second and subsequent reports show statistics
+for the time since the last report.
+.TP
+.B -c \fIcount
+Repeat the display
+.I count
+times. If this option is not specified, the default repeat count is 1
+if the
+.B -w
+option is not specified, otherwise infinity.
+.TP
+.B -r
+Display additional statistics summarizing the compression ratio
+achieved by the packet compression algorithm in use.
+.TP
+.B -v
+Display additional statistics relating to the performance of the Van
+Jacobson TCP header compression algorithm.
+.TP
+.B -w \fIwait
+Pause
+.I wait
+seconds between each display. If this option is not specified, the
+default interval is 5 seconds.
+.TP
+.B -z
+Instead of the standard display, show statistics indicating the
+performance of the packet compression algorithm in use.
+.PP
+The following fields are printed on the input side when the
+.B -z
+option is not used:
+.TP
+.B IN
+The total number of bytes received by this interface.
+.TP
+.B PACK
+The total number of packets received by this interface.
+.TP
+.B VJCOMP
+The number of header-compressed TCP packets received by this interface.
+.TP
+.B VJUNC
+The number of header-uncompressed TCP packets received by this
+interface. Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJERR
+The number of corrupted or bogus header-compressed TCP packets
+received by this interface. Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJTOSS
+The number of VJ header-compressed TCP packets dropped on reception by
+this interface because of preceding errors. Only reported when the
+.B -v
+option is specified.
+.TP
+.B NON-VJ
+The total number of non-TCP packets received by this interface. Only
+reported when the
+.B -v
+option is specified.
+.TP
+.B RATIO
+The compression ratio achieved for received packets by the
+packet compression scheme in use, defined as the uncompressed size
+divided by the compressed size.
+Only reported when the
+.B -r
+option is specified.
+.TP
+.B UBYTE
+The total number of bytes received, after decompression of compressed
+packets. Only reported when the
+.B -r
+option is specified.
+.PP
+The following fields are printed on the output side:
+.TP
+.B OUT
+The total number of bytes transmitted from this interface.
+.TP
+.B PACK
+The total number of packets transmitted from this interface.
+.TP
+.B VJCOMP
+The number of TCP packets transmitted from this interface with
+VJ-compressed TCP headers.
+.TP
+.B VJUNC
+The number of TCP packets transmitted from this interface with
+VJ-uncompressed TCP headers.
+Not reported when the
+.B -r
+option is specified.
+.TP
+.B NON-VJ
+The total number of non-TCP packets transmitted from this interface.
+Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJSRCH
+The number of searches for the cached header entry for a VJ header
+compressed TCP packet. Only reported when the
+.B -v
+option is specified.
+.TP
+.B VJMISS
+The number of failed searches for the cached header entry for a
+VJ header compressed TCP packet. Only reported when the
+.B -v
+option is specified.
+.TP
+.B RATIO
+The compression ratio achieved for transmitted packets by the
+packet compression scheme in use, defined as the size
+before compression divided by the compressed size.
+Only reported when the
+.B -r
+option is specified.
+.TP
+.B UBYTE
+The total number of bytes to be transmitted, before packet compression
+is applied. Only reported when the
+.B -r
+option is specified.
+.PP
+When the
+.B -z
+option is specified,
+.Nm pppstats
+instead displays the following fields, relating to the packet
+compression algorithm currently in use. If packet compression is not
+in use, these fields will all display zeroes. The fields displayed on
+the input side are:
+.TP
+.B COMPRESSED BYTE
+The number of bytes of compressed packets received.
+.TP
+.B COMPRESSED PACK
+The number of compressed packets received.
+.TP
+.B INCOMPRESSIBLE BYTE
+The number of bytes of incompressible packets (that is, those which
+were transmitted in uncompressed form) received.
+.TP
+.B INCOMPRESSIBLE PACK
+The number of incompressible packets received.
+.TP
+.B COMP RATIO
+The recent compression ratio for incoming packets, defined as the
+uncompressed size divided by the compressed size (including both
+compressible and incompressible packets).
+.PP
+The fields displayed on the output side are:
+.TP
+.B COMPRESSED BYTE
+The number of bytes of compressed packets transmitted.
+.TP
+.B COMPRESSED PACK
+The number of compressed packets transmitted.
+.TP
+.B INCOMPRESSIBLE BYTE
+The number of bytes of incompressible packets transmitted (that is,
+those which were transmitted in uncompressed form).
+.TP
+.B INCOMPRESSIBLE PACK
+The number of incompressible packets transmitted.
+.TP
+.B COMP RATIO
+The recent compression ratio for outgoing packets.
+.SH SEE ALSO
+pppd(8)
diff --git a/mdk-stage1/ppp/pppstats/pppstats.c b/mdk-stage1/ppp/pppstats/pppstats.c
new file mode 100644
index 000000000..77b803723
--- /dev/null
+++ b/mdk-stage1/ppp/pppstats/pppstats.c
@@ -0,0 +1,557 @@
+/*
+ * print PPP statistics:
+ * pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]
+ *
+ * -a Show absolute values rather than deltas
+ * -d Show data rate (kB/s) rather than bytes
+ * -v Show more stats for VJ TCP header compression
+ * -r Show compression ratio
+ * -z Show compression statistics instead of default display
+ *
+ * History:
+ * perkins@cps.msu.edu: Added compression statistics and alternate
+ * display. 11/94
+ * Brad Parker (brad@cayman.com) 6/92
+ *
+ * from the original "slstats" by Van Jacobson
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef __STDC__
+#define const
+#endif
+
+#ifndef lint
+static const char rcsid[] = "$Id$";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#ifndef STREAMS
+#if defined(_linux_) && defined(__powerpc__) \
+ && (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
+/* kludge alert! */
+#undef __GLIBC__
+#endif
+#include <sys/socket.h> /* *BSD, Linux, NeXT, Ultrix etc. */
+#ifndef _linux_
+#include <net/if.h>
+#include <net/ppp_defs.h>
+#include <net/if_ppp.h>
+#else
+/* Linux */
+#if __GLIBC__ >= 2
+#include <asm/types.h> /* glibc 2 conflicts with linux/types.h */
+#include <net/if.h>
+#else
+#include <linux/types.h>
+#include <linux/if.h>
+#endif
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#endif /* _linux_ */
+
+#else /* STREAMS */
+#include <sys/stropts.h> /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+
+#endif /* STREAMS */
+
+int vflag, rflag, zflag; /* select type of display */
+int aflag; /* print absolute values, not deltas */
+int dflag; /* print data rates, not bytes */
+int interval, count;
+int infinite;
+int unit;
+int s; /* socket or /dev/ppp file descriptor */
+int signalled; /* set if alarm goes off "early" */
+char *progname;
+char *interface;
+
+#if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT)
+extern int optind;
+extern char *optarg;
+#endif
+
+/*
+ * If PPP_DRV_NAME is not defined, use the legacy "ppp" as the
+ * device name.
+ */
+#if !defined(PPP_DRV_NAME)
+#define PPP_DRV_NAME "ppp"
+#endif /* !defined(PPP_DRV_NAME) */
+
+static void usage __P((void));
+static void catchalarm __P((int));
+static void get_ppp_stats __P((struct ppp_stats *));
+static void get_ppp_cstats __P((struct ppp_comp_stats *));
+static void intpr __P((void));
+
+int main __P((int, char *argv[]));
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n",
+ progname);
+ exit(1);
+}
+
+/*
+ * Called if an interval expires before intpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+static void
+catchalarm(arg)
+ int arg;
+{
+ signalled = 1;
+}
+
+
+#ifndef STREAMS
+static void
+get_ppp_stats(curp)
+ struct ppp_stats *curp;
+{
+ struct ifpppstatsreq req;
+
+ memset (&req, 0, sizeof (req));
+
+#ifdef _linux_
+ req.stats_ptr = (caddr_t) &req.stats;
+#undef ifr_name
+#define ifr_name ifr__name
+#endif
+
+ strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
+ if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY)
+ fprintf(stderr, "kernel support missing\n");
+ else
+ perror("couldn't get PPP statistics");
+ exit(1);
+ }
+ *curp = req.stats;
+}
+
+static void
+get_ppp_cstats(csp)
+ struct ppp_comp_stats *csp;
+{
+ struct ifpppcstatsreq creq;
+
+ memset (&creq, 0, sizeof (creq));
+
+#ifdef _linux_
+ creq.stats_ptr = (caddr_t) &creq.stats;
+#undef ifr_name
+#define ifr_name ifr__name
+#endif
+
+ strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
+ if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY) {
+ fprintf(stderr, "no kernel compression support\n");
+ if (zflag)
+ exit(1);
+ rflag = 0;
+ } else {
+ perror("couldn't get PPP compression stats");
+ exit(1);
+ }
+ }
+
+#ifdef _linux_
+ if (creq.stats.c.bytes_out == 0) {
+ creq.stats.c.bytes_out = creq.stats.c.comp_bytes + creq.stats.c.inc_bytes;
+ creq.stats.c.in_count = creq.stats.c.unc_bytes;
+ }
+ if (creq.stats.c.bytes_out == 0)
+ creq.stats.c.ratio = 0.0;
+ else
+ creq.stats.c.ratio = 256.0 * creq.stats.c.in_count /
+ creq.stats.c.bytes_out;
+
+ if (creq.stats.d.bytes_out == 0) {
+ creq.stats.d.bytes_out = creq.stats.d.comp_bytes + creq.stats.d.inc_bytes;
+ creq.stats.d.in_count = creq.stats.d.unc_bytes;
+ }
+ if (creq.stats.d.bytes_out == 0)
+ creq.stats.d.ratio = 0.0;
+ else
+ creq.stats.d.ratio = 256.0 * creq.stats.d.in_count /
+ creq.stats.d.bytes_out;
+#endif
+
+ *csp = creq.stats;
+}
+
+#else /* STREAMS */
+
+int
+strioctl(fd, cmd, ptr, ilen, olen)
+ int fd, cmd, ilen, olen;
+ char *ptr;
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+ if (ioctl(fd, I_STR, &str) == -1)
+ return -1;
+ if (str.ic_len != olen)
+ fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n",
+ olen, str.ic_len, cmd);
+ return 0;
+}
+
+static void
+get_ppp_stats(curp)
+ struct ppp_stats *curp;
+{
+ if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == EINVAL)
+ fprintf(stderr, "kernel support missing\n");
+ else
+ perror("couldn't get PPP statistics");
+ exit(1);
+ }
+}
+
+static void
+get_ppp_cstats(csp)
+ struct ppp_comp_stats *csp;
+{
+ if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY) {
+ fprintf(stderr, "no kernel compression support\n");
+ if (zflag)
+ exit(1);
+ rflag = 0;
+ } else {
+ perror("couldn't get PPP compression statistics");
+ exit(1);
+ }
+ }
+}
+
+#endif /* STREAMS */
+
+#define MAX0(a) ((int)(a) > 0? (a): 0)
+#define V(offset) MAX0(cur.offset - old.offset)
+#define W(offset) MAX0(ccs.offset - ocs.offset)
+
+#define RATIO(c, i, u) ((c) == 0? 1.0: (u) / ((double)(c) + (i)))
+#define CRATE(x) RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
+
+#define KBPS(n) ((n) / (interval * 1000.0))
+
+/*
+ * Print a running summary of interface statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval. Assumes that interval is non-zero.
+ * First line printed is cumulative.
+ */
+static void
+intpr()
+{
+ register int line = 0;
+ sigset_t oldmask, mask;
+ char *bunit;
+ int ratef = 0;
+ struct ppp_stats cur, old;
+ struct ppp_comp_stats ccs, ocs;
+
+ memset(&old, 0, sizeof(old));
+ memset(&ocs, 0, sizeof(ocs));
+
+ while (1) {
+ get_ppp_stats(&cur);
+ if (zflag || rflag)
+ get_ppp_cstats(&ccs);
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if ((line % 20) == 0) {
+ if (zflag) {
+ printf("IN: COMPRESSED INCOMPRESSIBLE COMP | ");
+ printf("OUT: COMPRESSED INCOMPRESSIBLE COMP\n");
+ bunit = dflag? "KB/S": "BYTE";
+ printf(" %s PACK %s PACK RATIO | ", bunit, bunit);
+ printf(" %s PACK %s PACK RATIO", bunit, bunit);
+ } else {
+ printf("%8.8s %6.6s %6.6s",
+ "IN", "PACK", "VJCOMP");
+
+ if (!rflag)
+ printf(" %6.6s %6.6s", "VJUNC", "VJERR");
+ if (vflag)
+ printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
+ if (rflag)
+ printf(" %6.6s %6.6s", "RATIO", "UBYTE");
+ printf(" | %8.8s %6.6s %6.6s",
+ "OUT", "PACK", "VJCOMP");
+
+ if (!rflag)
+ printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
+ if (vflag)
+ printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
+ if (rflag)
+ printf(" %6.6s %6.6s", "RATIO", "UBYTE");
+ }
+ putchar('\n');
+ }
+
+ if (zflag) {
+ if (ratef) {
+ printf("%8.3f %6u %8.3f %6u %6.2f",
+ KBPS(W(d.comp_bytes)),
+ W(d.comp_packets),
+ KBPS(W(d.inc_bytes)),
+ W(d.inc_packets),
+ ccs.d.ratio / 256.0);
+ printf(" | %8.3f %6u %8.3f %6u %6.2f",
+ KBPS(W(c.comp_bytes)),
+ W(c.comp_packets),
+ KBPS(W(c.inc_bytes)),
+ W(c.inc_packets),
+ ccs.c.ratio / 256.0);
+ } else {
+ printf("%8u %6u %8u %6u %6.2f",
+ W(d.comp_bytes),
+ W(d.comp_packets),
+ W(d.inc_bytes),
+ W(d.inc_packets),
+ ccs.d.ratio / 256.0);
+ printf(" | %8u %6u %8u %6u %6.2f",
+ W(c.comp_bytes),
+ W(c.comp_packets),
+ W(c.inc_bytes),
+ W(c.inc_packets),
+ ccs.c.ratio / 256.0);
+ }
+
+ } else {
+ if (ratef)
+ printf("%8.3f", KBPS(V(p.ppp_ibytes)));
+ else
+ printf("%8u", V(p.ppp_ibytes));
+ printf(" %6u %6u",
+ V(p.ppp_ipackets),
+ V(vj.vjs_compressedin));
+ if (!rflag)
+ printf(" %6u %6u",
+ V(vj.vjs_uncompressedin),
+ V(vj.vjs_errorin));
+ if (vflag)
+ printf(" %6u %6u",
+ V(vj.vjs_tossed),
+ V(p.ppp_ipackets) - V(vj.vjs_compressedin)
+ - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin));
+ if (rflag) {
+ printf(" %6.2f ", CRATE(d));
+ if (ratef)
+ printf("%6.2f", KBPS(W(d.unc_bytes)));
+ else
+ printf("%6u", W(d.unc_bytes));
+ }
+ if (ratef)
+ printf(" | %8.3f", KBPS(V(p.ppp_obytes)));
+ else
+ printf(" | %8u", V(p.ppp_obytes));
+ printf(" %6u %6u",
+ V(p.ppp_opackets),
+ V(vj.vjs_compressed));
+ if (!rflag)
+ printf(" %6u %6u",
+ V(vj.vjs_packets) - V(vj.vjs_compressed),
+ V(p.ppp_opackets) - V(vj.vjs_packets));
+ if (vflag)
+ printf(" %6u %6u",
+ V(vj.vjs_searches),
+ V(vj.vjs_misses));
+ if (rflag) {
+ printf(" %6.2f ", CRATE(c));
+ if (ratef)
+ printf("%6.2f", KBPS(W(c.unc_bytes)));
+ else
+ printf("%6u", W(c.unc_bytes));
+ }
+
+ }
+
+ putchar('\n');
+ fflush(stdout);
+ line++;
+
+ count--;
+ if (!infinite && !count)
+ break;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask, &oldmask);
+ if (!signalled) {
+ sigemptyset(&mask);
+ sigsuspend(&mask);
+ }
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if (!aflag) {
+ old = cur;
+ ocs = ccs;
+ ratef = dflag;
+ }
+ }
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+#ifdef STREAMS
+ char *dev;
+#endif
+
+ interface = PPP_DRV_NAME "0";
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ ++progname;
+
+ while ((c = getopt(argc, argv, "advrzc:w:")) != -1) {
+ switch (c) {
+ case 'a':
+ ++aflag;
+ break;
+ case 'd':
+ ++dflag;
+ break;
+ case 'v':
+ ++vflag;
+ break;
+ case 'r':
+ ++rflag;
+ break;
+ case 'z':
+ ++zflag;
+ break;
+ case 'c':
+ count = atoi(optarg);
+ if (count <= 0)
+ usage();
+ break;
+ case 'w':
+ interval = atoi(optarg);
+ if (interval <= 0)
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!interval && count)
+ interval = 5;
+ if (interval && !count)
+ infinite = 1;
+ if (!interval && !count)
+ count = 1;
+ if (aflag)
+ dflag = 0;
+
+ if (argc > 1)
+ usage();
+ if (argc > 0)
+ interface = argv[0];
+
+ if (sscanf(interface, PPP_DRV_NAME "%d", &unit) != 1) {
+ fprintf(stderr, "%s: invalid interface '%s' specified\n",
+ progname, interface);
+ }
+
+#ifndef STREAMS
+ {
+ struct ifreq ifr;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ fprintf(stderr, "%s: ", progname);
+ perror("couldn't create IP socket");
+ exit(1);
+ }
+
+#ifdef _linux_
+#undef ifr_name
+#define ifr_name ifr_ifrn.ifrn_name
+#endif
+ strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ fprintf(stderr, "%s: nonexistent interface '%s' specified\n",
+ progname, interface);
+ exit(1);
+ }
+ }
+
+#else /* STREAMS */
+#ifdef __osf__
+ dev = "/dev/streams/ppp";
+#else
+ dev = "/dev/" PPP_DRV_NAME;
+#endif
+ if ((s = open(dev, O_RDONLY)) < 0) {
+ fprintf(stderr, "%s: couldn't open ", progname);
+ perror(dev);
+ exit(1);
+ }
+ if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) {
+ fprintf(stderr, "%s: ppp%d is not available\n", progname, unit);
+ exit(1);
+ }
+
+#endif /* STREAMS */
+
+ intpr();
+ exit(0);
+}
diff --git a/mdk-stage1/ppp/sample/auth-down b/mdk-stage1/ppp/sample/auth-down
new file mode 100644
index 000000000..edde65db1
--- /dev/null
+++ b/mdk-stage1/ppp/sample/auth-down
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# A program or script which is executed after the remote system
+# successfully authenticates itself. It is executed with the parameters
+# <interface-name> <peer-name> <user-name> <tty-device> <speed>
+#
+
+#
+# The environment is cleared before executing this script
+# so the path must be reset
+#
+PATH=/usr/sbin:/sbin:/usr/bin:/bin
+export PATH
+
+echo auth-down `date +'%y/%m/%d %T'` $* >> /var/log/pppstats
+
+# last line
diff --git a/mdk-stage1/ppp/sample/auth-up b/mdk-stage1/ppp/sample/auth-up
new file mode 100644
index 000000000..54722a3c0
--- /dev/null
+++ b/mdk-stage1/ppp/sample/auth-up
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# A program or script which is executed after the remote system
+# successfully authenticates itself. It is executed with the parameters
+# <interface-name> <peer-name> <user-name> <tty-device> <speed>
+#
+
+#
+# The environment is cleared before executing this script
+# so the path must be reset
+#
+PATH=/usr/sbin:/sbin:/usr/bin:/bin
+export PATH
+
+echo auth-up `date +'%y/%m/%d %T'` $* >> /var/log/pppstats
+
+# last line
diff --git a/mdk-stage1/ppp/sample/ip-down b/mdk-stage1/ppp/sample/ip-down
new file mode 100644
index 000000000..b771fb644
--- /dev/null
+++ b/mdk-stage1/ppp/sample/ip-down
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# This script is run by the pppd _after_ the link is brought down.
+# It should be used to delete routes, unset IP addresses etc.
+#
+# This script is called with the following arguments:
+# Arg Name Example
+# $1 Interface name ppp0
+# $2 The tty ttyS1
+# $3 The link speed 38400
+# $4 Local IP number 12.34.56.78
+# $5 Peer IP number 12.34.56.99
+#
+
+#
+# The environment is cleared before executing this script
+# so the path must be reset
+#
+PATH=/usr/sbin:/sbin:/usr/bin:/bin
+export PATH
+
+# last line
diff --git a/mdk-stage1/ppp/sample/ip-up b/mdk-stage1/ppp/sample/ip-up
new file mode 100644
index 000000000..02bb71c44
--- /dev/null
+++ b/mdk-stage1/ppp/sample/ip-up
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# This script is run by the pppd after the link is established.
+# It should be used to add routes, set IP address, run the mailq
+# etc.
+#
+# This script is called with the following arguments:
+# Arg Name Example
+# $1 Interface name ppp0
+# $2 The tty ttyS1
+# $3 The link speed 38400
+# $4 Local IP number 12.34.56.78
+# $5 Peer IP number 12.34.56.99
+#
+
+#
+# The environment is cleared before executing this script
+# so the path must be reset
+#
+PATH=/usr/sbin:/sbin:/usr/bin:/bin
+export PATH
+
+# last line
diff --git a/mdk-stage1/ppp/sample/options b/mdk-stage1/ppp/sample/options
new file mode 100644
index 000000000..8d0a3f963
--- /dev/null
+++ b/mdk-stage1/ppp/sample/options
@@ -0,0 +1,153 @@
+# /etc/ppp/options
+
+# The name of this server. Often, the FQDN is used here.
+#name <host>
+
+# Enforce the use of the hostname as the name of the local system for
+# authentication purposes (overrides the name option).
+usehostname
+
+# If no local IP address is given, pppd will use the first IP address
+# that belongs to the local hostname. If "noipdefault" is given, this
+# is disabled and the peer will have to supply an IP address.
+noipdefault
+
+# With this option, pppd will accept the peer's idea of our local IP
+# address, even if the local IP address was specified in an option.
+#ipcp-accept-local
+
+# With this option, pppd will accept the peer's idea of its (remote) IP
+# address, even if the remote IP address was specified in an option.
+#ipcp-accept-remote
+
+# Specify which DNS Servers the incoming Win95 or WinNT Connection should use
+# Two Servers can be remotely configured
+#ms-dns 192.168.1.1
+#ms-dns 192.168.1.2
+
+# Specify which WINS Servers the incoming connection Win95 or WinNT should use
+#wins-addr 192.168.1.50
+#wins-addr 192.168.1.51
+
+# enable this on a server that already has a permanent default route
+#nodefaultroute
+
+# Run the executable or shell command specified after pppd has terminated
+# the link. This script could, for example, issue commands to the modem
+# to cause it to hang up if hardware modem control signals were not
+# available.
+# If mgetty is running, it will reset the modem anyway. So there is no need
+# to do it here.
+#disconnect "chat -- \d+++\d\c OK ath0 OK"
+
+# Increase debugging level (same as -d). The debug output is written
+# to syslog LOG_LOCAL2.
+debug
+
+# Enable debugging code in the kernel-level PPP driver. The argument n
+# is a number which is the sum of the following values: 1 to enable
+# general debug messages, 2 to request that the contents of received
+# packets be printed, and 4 to request that the contents of transmitted
+# packets be printed.
+#kdebug n
+
+# Require the peer to authenticate itself before allowing network
+# packets to be sent or received.
+# Please do not disable this setting. It is expected to be standard in
+# future releases of pppd. Use the call option (see manpage) to disable
+# authentication for specific peers.
+#auth
+
+# authentication can either be pap or chap. As most people only want to
+# use pap, you can also disable chap:
+#require-pap
+#refuse-chap
+
+# Use hardware flow control (i.e. RTS/CTS) to control the flow of data
+# on the serial port.
+crtscts
+
+# Specifies that pppd should use a UUCP-style lock on the serial device
+# to ensure exclusive access to the device.
+lock
+
+# Use the modem control lines.
+modem
+
+# async character map -- 32-bit hex; each bit is a character
+# that needs to be escaped for pppd to receive it. 0x00000001
+# represents '\x01', and 0x80000000 represents '\x1f'.
+# To allow pppd to work over a rlogin/telnet connection, ou should escape
+# XON (^Q), XOFF (^S) and ^]: (The peer should use "escape ff".)
+#asyncmap 200a0000
+asyncmap 0
+
+# Specifies that certain characters should be escaped on transmission
+# (regardless of whether the peer requests them to be escaped with its
+# async control character map). The characters to be escaped are
+# specified as a list of hex numbers separated by commas. Note that
+# almost any character can be specified for the escape option, unlike
+# the asyncmap option which only allows control characters to be
+# specified. The characters which may not be escaped are those with hex
+# values 0x20 - 0x3f or 0x5e.
+#escape 11,13,ff
+
+# Set the MRU [Maximum Receive Unit] value to <n> for negotiation. pppd
+# will ask the peer to send packets of no more than <n> bytes. The
+# minimum MRU value is 128. The default MRU value is 1500. A value of
+# 296 is recommended for slow links (40 bytes for TCP/IP header + 256
+# bytes of data).
+#mru 542
+
+# Set the MTU [Maximum Transmit Unit] value to <n>. Unless the peer
+# requests a smaller value via MRU negotiation, pppd will request that
+# the kernel networking code send data packets of no more than n bytes
+# through the PPP network interface.
+#mtu <n>
+
+# Set the interface netmask to <n>, a 32 bit netmask in "decimal dot"
+# notation (e.g. 255.255.255.0).
+#netmask 255.255.255.0
+
+# Don't fork to become a background process (otherwise pppd will do so
+# if a serial device is specified).
+nodetach
+
+# Set the assumed name of the remote system for authentication purposes
+# to <n>.
+#remotename <n>
+
+# Add an entry to this system's ARP [Address Resolution Protocol]
+# table with the IP address of the peer and the Ethernet address of this
+# system. {proxyarp,noproxyarp}
+proxyarp
+
+# Use the system password database for authenticating the peer using
+# PAP. Note: mgetty already provides this option. If this is specified
+# then dialin from users using a script under Linux to fire up ppp wont work.
+#login
+
+# If this option is given, pppd will send an LCP echo-request frame to
+# the peer every n seconds. Under Linux, the echo-request is sent when
+# no packets have been received from the peer for n seconds. Normally
+# the peer should respond to the echo-request by sending an echo-reply.
+# This option can be used with the lcp-echo-failure option to detect
+# that the peer is no longer connected.
+lcp-echo-interval 30
+
+# If this option is given, pppd will presume the peer to be dead if n
+# LCP echo-requests are sent without receiving a valid LCP echo-reply.
+# If this happens, pppd will terminate the connection. Use of this
+# option requires a non-zero value for the lcp-echo-interval parameter.
+# This option can be used to enable pppd to terminate after the physical
+# connection has been broken (e.g., the modem has hung up) in
+# situations where no hardware modem control lines are available.
+lcp-echo-failure 4
+
+# Specifies that pppd should disconnect if the link is idle for n seconds.
+idle 600
+
+# Disable the IPXCP and IPX protocols.
+noipx
+
+# ---<End of File>---
diff --git a/mdk-stage1/ppp/sample/options.ttyXX b/mdk-stage1/ppp/sample/options.ttyXX
new file mode 100644
index 000000000..d4202f534
--- /dev/null
+++ b/mdk-stage1/ppp/sample/options.ttyXX
@@ -0,0 +1,14 @@
+# If you need to set up multiple serial lines then copy this file to
+# options.<ttyname> for each tty with a modem on it.
+#
+# The options.tty file will assign an IP address to each PPP connection
+# as it comes up. They must all be distinct!
+#
+# Example:
+# options.ttyS1 for com2 under DOS.
+#
+# Edit the following line so that the first IP address
+# mentioned is the ip address of the serial port while the second
+# is the IP address of your host
+#
+hostname-s1:hostname
diff --git a/mdk-stage1/ppp/sample/pap-secrets b/mdk-stage1/ppp/sample/pap-secrets
new file mode 100644
index 000000000..098971b9f
--- /dev/null
+++ b/mdk-stage1/ppp/sample/pap-secrets
@@ -0,0 +1,28 @@
+# Secrets for authentication using PAP
+# client server secret IP addresses
+
+# OUTBOUND CONNECTIONS
+# Here you should add your userid password to connect to your providers via
+# pap. The * means that the password is to be used for ANY host you connect
+# to. Thus you do not have to worry about the foreign machine name. Just
+# replace password with your password.
+# If you have different providers with different passwords then you better
+# remove the following line.
+#hostname * password
+
+# INBOUND CONNECTIONS
+#client hostname <password> 192.168.1.1
+
+# If you add "auth login -chap +pap" to /etc/mgetty+sendfax/login.config,
+# all users in /etc/passwd can use their password for pap-authentication.
+#
+# Every regular user can use PPP and has to use passwords from /etc/passwd
+#* hostname ""
+# UserIDs that cannot use PPP at all. Check your /etc/passwd and add any
+# other accounts that should not be able to use pppd! Replace hostname
+# with your local hostname.
+#guest hostname "*" -
+#master hostname "*" -
+#root hostname "*" -
+#support hostname "*" -
+#stats hostname "*" -
diff --git a/mdk-stage1/ppp/scripts/README b/mdk-stage1/ppp/scripts/README
new file mode 100644
index 000000000..00e032ca6
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/README
@@ -0,0 +1,143 @@
+This directory contains a set of scripts which have been used on Linux
+as well as Solaris 2.x systems to initiate or maintain a connection
+with PPP. The files in this directory were contributed by Al Longyear
+(longyear@netcom.com) and Adi Masputra (adi.masputra@sun.com)
+
+------------------------------------------------------------------------
+
+1. README
+
+This file. You are reading it. It is just documentation.
+
+------------------------------------------------------------------------
+
+2. ppp-on
+
+This script will initiate a connection to the PPP system. It will run
+the chat program with the connection script as a parameter. This is a
+possible security hole. However, it is simple. It is meant to replace
+the previous version of ppp-on which was not very functional.
+
+The ppp-on script has entries for the account name, password, IP
+addresses, and telephone numbers. The parameters are passed to the
+pppd process and, then in turn, to the second part of the connect
+script, as a set of environment variables.
+
+Please make sure that you put the full path name to the ppp-on-dialer
+script in the reference to it in ppp-on.
+
+------------------------------------------------------------------------
+
+3. ppp-on-dialer
+
+This is the second part to the simple calling script, ppp-on. It
+executes the chat program to connect the user with a standard UNIX
+style getty/login connection sequence.
+
+------------------------------------------------------------------------
+
+4. callback
+
+This script may be used in lieu of the ppp-on-dialer to permit the
+common modem callback sequence. You may need to make changes to the
+expected prompt string for the modem.
+
+The script works by disabling the system's detection of the DCD
+condition and working on the modem status message "NO CARRIER" which
+is generated when the modem disconnects.
+
+It is crude. It does work for my modem connection. Use as you see fit.
+
+------------------------------------------------------------------------
+
+5. redialer
+
+The redialer script is a replacement for the ppp-on-dialer script. It
+will do 'attack dialing' or 'demon dialing' of one or more telephone
+numbers. The first number which responds will be used for a
+connection.
+
+There is a limit of ten attempts and a 15 second delay between dialing
+attempts. Both values are set in the script.
+
+------------------------------------------------------------------------
+
+6. ppp-off
+
+This is a script which will terminate the active ppp connection. Use
+as either "ppp-off" to terminate ppp0, or "ppp-off <device>" to
+terminate the connection on <device>. For example, "ppp-off ppp2" will
+terminate the ppp2 connection.
+
+------------------------------------------------------------------------
+
+7. secure-card
+
+This script was written by Jim Isaacson <jcisaac@crl.com>. It is a script
+for the 'expect' programming language used with Tcl. You need to have
+expect and Tcl installed before this script may be used.
+
+This script will operate with a device marketed under the name "SecureCARD".
+This little device is mated with its controller. On the credit card size
+device, there is a sequence number which changes on a random basis. In order
+for you to connect you need to enter a fixed portion of your account name
+and the number which is displayed on this card device. The number must match
+the value at the controller in order for the account name to be used.
+
+The problem is that chat uses fixed response strings. In addition, the
+timing for running the script may prevent the use of a script that reads the
+value before it starts the dial sequence. What was needed was a script which
+asked the user at the user's console at the time that it is needed.
+
+This led to the use of expect.
+
+------------------------------------------------------------------------
+
+8. ppp-on-rsh
+
+This script will initiate a PPP connection to a remote machine using rsh.
+This is implemented by creating a master/slave pseudo-tty with the slave
+pointing to rsh, specifically with the 'pty' and 'notty' options of pppd.
+It is assumed that the remote machine contains some sort of trust
+mechanisms (such as ~/.rhosts, et al) to allow the local machine to
+connect via rsh as root.
+
+------------------------------------------------------------------------
+
+9. ppp-on-ssh
+
+This script will initiate a PPP connection to a remote machine using the
+secure shell, or ssh. I've only tested this on ssh 1.x, so those of you
+who are running ssh 2.x mahy need to modify the ssh options slightly.
+This is implemented by creating a master/slave pseudo-ttyt with the slave
+pointing to ssh, specifically with the 'pty' and 'notty' options of pppd.
+It is assumed that the remote machine can accept the ssh connection from
+the local host, in the sense that all ssh authentication mechanisms have
+been properly configured, so that a remote root user can open a ssh
+connection.
+
+------------------------------------------------------------------------
+
+10. options-rsh-loc & options-rsh-rem
+
+These options files accompany the ppp-on-rsh script mentioned above. In
+theory, you'd want to copy the options-rsh-rem to the remote machine where
+in.rshd is running. The only extra option required on the remote machine
+options file is the 'notty' option. In addition, all ASCII control characters
+[0x00 to 0x1f], plus 0xff, are escaped. This may need to be modified
+depending on the rsh (or pseudo-tty) implementation which may differ across
+platforms, for further optimizations.
+
+------------------------------------------------------------------------
+
+11. options-ssh-loc & options-ssh-rem
+
+These options files accompany the ppp-on-ssh script mentioned above. I've
+only tested this on ssh 1.x, so those of you who are running ssh 2.x need
+to modify the ssh options slightly. In theory, you'd want to copy the
+options-ssh-rem to the remote machine where sshd daemon is running. The only
+extra options required on the remote machine options file is the 'notty'
+option. In addition, all ASCII control characters [0x00 to 0x1f], plus 0xff,
+are escaped. This may need to be modified depending on the ssh (or
+pseudo-tty) implementation which may differ across platforms, for further
+optimizations.
diff --git a/mdk-stage1/ppp/scripts/callback b/mdk-stage1/ppp/scripts/callback
new file mode 100755
index 000000000..3e74e10b2
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/callback
@@ -0,0 +1,77 @@
+#!/bin/sh
+###################################################################
+#
+# Script to dial the remote system, negotiate the connection, and send
+# it the id. Then wait for the modem to disconnect. Reset the modem
+# to answer mode and wait for the system to call back.
+#
+# The telephone number and modempass are used when establishing the
+# connection to the modem.
+#
+PHONE=555-1212
+MODEMPASS=modem_identifier
+#
+# Once the modem calls back, the account name and password are used for
+# a UNIX style login operation.
+#
+ACCOUNT=my_account_name
+PASSWORD=my_password
+
+###################################################################
+#
+# Step 1. Dial the modem and negotiate the initial dialog.
+# note: the modem is configured to ignore loss of DCD at this point.
+# it is important that this be performed because the loss of DCD
+# will normally prevent system from working since 'modem' is used
+# for pppd.
+#
+# The script is terminated normally when the carrier is lost.
+#
+chat -v \
+ TIMEOUT 3 \
+ ABORT '\nBUSY\r' \
+ ABORT '\nNO ANSWER\r' \
+ ABORT '\nRINGING\r\n\r\nRINGING\r' \
+ '' AT \
+ 'OK-+++\c-OK' 'AT&C0&D2S0=0H0 \
+ TIMEOUT 30 \
+ OK ATDT$TELEPHONE \
+ CONNECT '' \
+ assword: $MODEMPASS \
+ "\nNO CARRIER\r"
+
+if [ "$?" = "0" ]; then
+
+###################################################################
+#
+# Step 2. Wait for the call back from the remote. This will wait for at most
+# 30 seconds for the call back should the first attempt fail or
+# something happen with the callback logic at the remote.
+#
+# note: when the callback occurs, the DCD setting is re-enabled.
+#
+# If some voice call should happen during this period, the system will
+# answer the telephone and then hang up on them. I realize that this is
+# rude, but there is little that this script can do.
+#
+ chat -v \
+ TIMEOUT 30 \
+ ABORT '\nVOICE\r' \
+ '\nRING\r' 'AT&C1A' \
+ CONNECT '' \
+ TIMEOUT 10 \
+ ogin:--ogin: $ACCOUNT \
+ TIMEOUT 45 \
+ assword: $PASSWORD
+
+ if [ "$?" = "0" ]; then
+ exit 0
+ fi
+fi
+
+###################################################################
+#
+# The script has failed. Terminate the connection mode.
+#
+chat -v TIMEOUT 3 "" AT 'OK-+++\c-OK' 'AT&C1&D2S0=0H0' OK
+exit 1
diff --git a/mdk-stage1/ppp/scripts/chat-callback b/mdk-stage1/ppp/scripts/chat-callback
new file mode 100644
index 000000000..d014d6af3
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/chat-callback
@@ -0,0 +1,98 @@
+# =====================================================================================
+# Chat script to dial our Company PPP account.
+# They uses a call-back system to identify us and to reverse
+# charge the call cost.
+# =====================================================================================
+#
+ECHO OFF
+# All the usual abort strings
+ABORT "NO CARRIER"
+ABORT "VOICE"
+ABORT "BUSY"
+ABORT "NO DIALTONE"
+ABORT "NO ANSWER"
+#
+# If calling outside allowed time we get this:
+#
+ABORT "Access denied"
+#
+# Modem initialisation stuff
+#
+TIMEOUT 5
+SAY "Initialising modem ...\n"
+'' ATE1
+'OK\r\n' ATS0=1S11=60X4&K4S42.1=1
+#
+# Now dial our ISP and wait for connection
+#
+SAY "Dialling our ISP ...\n"
+'OK\r\n' ATDT09834657
+TIMEOUT 60
+CONNECT \c
+SAY "Connected ...\n"
+#
+# This is the first stage login, we identify ourself so that the remote
+# system will agree to call us back.
+#
+TIMEOUT 30
+SAY "Sending Callback login ID ...\n"
+name:-BREAK-name: callme
+#
+# From now on, we must assume no carrier is normal as well
+# as receiving a HANGUP signal because it will be the
+# case if our ISP clears the call to call us back.
+#
+CLR_ABORT "NO CARRIER"
+HANGUP OFF
+#
+ABORT "Invalid"
+#
+# Now send password and wait to see what happens
+#
+SAY "Sending Callback password ...\n"
+word:--word: xvsgsgs
+"You will be" \c
+#
+# What can happen now is:
+# either: we get "You will be called back..." which is the successful case
+# or: we get "Invalid login" and we abort (bad login ID or password)
+# or: we get "NO CARRIER" because of an error, this will not abort
+# and we will time out after 30 seconds
+# or: we get nothing and we will time out after 30 seconds
+#
+#
+# We reach here if we got "You will be called back..."
+#
+CLR_ABORT "Invalid"
+SAY "Now waiting for Call back ...\n"
+#
+# The remote system will now hangup and we will get both "NO CARRIER"
+# and a hangup signal which are ignored. We now wait for a connection
+# for up to 120 seconds. What happens here if somebody else calls before
+# the remote system is a bit dangerous:
+#
+# If a malicious user connects and says 'name:', he will see 'PPPuser'
+# If he then says 'word:' he will see the passowrd 'blipblop'. I may not
+# know to which systems these belong to, though. It is up to you to consider
+# that case and decide wether the risk is too big or not ....
+#
+TIMEOUT 120
+"CONNECT" \c
+#
+# We have been called, re-arm ABORT on NO CARRIER and normal hangup signal
+# behaviour
+#
+HANGUP ON
+ABORT "NO CARRIER"
+#
+# Second stage login in order to start PPP
+#
+SAY "Remote system called back, logging in ...\n"
+SAY "Sending login ID ...\n"
+name:-BREAK-name: PPPuser
+SAY "Sending password ...\n"
+word:--word: blipblop
+SAY "Asking to start PPP ...\n"
+'CnetSrv' "ppp default"
+"Entering PPP mode" \c
+SAY "ISP PPP started ...\n"
diff --git a/mdk-stage1/ppp/scripts/chatchat/README b/mdk-stage1/ppp/scripts/chatchat/README
new file mode 100644
index 000000000..88a4c6939
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/chatchat/README
@@ -0,0 +1,134 @@
+v 0.1 gpk@onramp.net 3/27/99
+
+I Intro
+
+ This document covers the use of the modified "chat" program and its
+adjunct "chatchat" to login using the Security Dynamics SecurID card
+on a linux system.
+
+ This set of files comprises a modified version of the chat program
+(the one distributed with ppp-2.3.5) and a new program called chatchat
+that allows you to supply data from the keyboard to the chat program.
+
+ The SecurID card generates passwords that have a lifetime of one
+minute and are used as a first layer in dial up security. The only
+software I know of for this card is for windows, so I wrote my own.
+This software allows you to type in the time-sensitive password right
+when your chat script is asked to supply the passcode by the remote
+system.
+
+
+II How It Works
+
+ This version of chat his an additional command that can be put into
+its options that says "Don't reply with this string. Open this pipe,
+read the contents, and reply with that instead." Chatchat creates a
+pipe and lets you type your passcode into it, then chat picks that up
+and sends it out just as though the passcode was hardcoded into the
+options.
+
+
+III Installation
+
+ I've provided intel binaries and source code the the modified chat
+program and the chatchat program. I'll recommend that you copy the
+chat.c program into your ppp-2.3.5/chat directory (save your original
+chat.c program first!) and re-make it using the Makefile that comes
+with chat. Copy the new chat somewhere into your path. (On my system
+chat lives in /usr/sbin/chat, so I've copied the modified one into
+/usr/sbin/chat.new and changed my dial in script to call chat.new
+instead of chat.
+
+ Second, compile chatchat.c and install it somewhere in your path:
+
+ gcc -g -o chatchat chatchat.c
+ cp chatchat /usr/sbin
+
+ Third, modify your chat script to use the chatchat program. Mine
+looks something like this:
+
+
+ --------------------
+
+#!/bin/sh
+#
+# This is part 2 of the ppp-on script. It will perform the connection
+# protocol for the desired connection.
+# use atm0 to turn down the speaker volume on my sportster x2 voice modem
+# gpk 11/2/97
+
+exec /usr/sbin/chat.new -V -v \
+ ABORT "BUSY" \
+ ABORT "NO DIAL TONE" \
+ ABORT "NO ANSWER" \
+ TIMEOUT 50 \
+ "" "atm0" \
+ OK ATDT$TELEPHONE \
+ CONNECT '' \
+ name: \\da0xxxxxx \
+ word: @/var/tmp/p \
+ compress. ''
+
+
+ -----------------------
+
+ This is a standard chat script:
+
+* abort if the modem is busy, you don't get a dial tone, no one
+ answers, or 50 seconds elapses.
+
+* use atm0 to mute the modem
+
+* dial the modem, when it connects, wait to be asked for account name
+
+* when we see "name:" prompt, delay briefly then respond with your
+ account name (fill in your account name)
+
+Now we get to the new stuff:
+
+* when we see "word:" in the password prompt, instead of responding
+ with "@/var/tmp/p", the modified chat program will open the pipe
+ /var/tmp/p, read the passcode out of there, and send it
+
+* when we see "compress." (the last word before ppp starts), reply
+ with nothing. The script ends and we start ppp.
+
+Note:
+
+* Make sure there is some whitespace between the filename and the \.
+
+
+IV Usage
+
+ To use this install the modified chat and chatchat programs, and
+modify your chat script similar to the above. Before you dial in,
+start that chatchat program giving it the same pipe as in your config
+file. In the above case:
+
+chatchat /var/tmp/p
+
+ Wait until you have one or two tick marks left on your card's
+current number, then start your dial up process that eventually calls
+chat. When chat goes to open and read the pipe, chatchat will prompt:
+
+
+type PIN into SecurID card and
+ enter resulting passcode:
+
+ At that point, type your PIN number into your Securid card, press
+the diamond, and type the resulting numbers in as your passcode. If
+you've left the -V -v options on your chat command you'll see
+everything so out, otherwise it works silently.
+
+ If you type the number wrong or run out of time, the server will
+respond with an authentication failure. In that case you will have to
+hang up and start again. I don't know how to build a conditional script
+that says either expect "compress" next, but if you see "name:" again,
+do this instead.
+
+
+V Additional Information
+
+ You can obtain additional information about chat and ppp from the
+man pages for chat and pppd, as well as the PPP-HOWTO.
+
diff --git a/mdk-stage1/ppp/scripts/chatchat/chatchat.c b/mdk-stage1/ppp/scripts/chatchat/chatchat.c
new file mode 100644
index 000000000..4534fb9e3
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/chatchat/chatchat.c
@@ -0,0 +1,409 @@
+/* *************************************************************************
+* NAME: chatchat.c
+*
+* DESCRIPTION:
+*
+* This program creates a pipe for the chat process to read. The user
+* can supply information (like a password) that will be picked up
+* by chat and sent just like the regular contents of a chat script.
+*
+* Usage is:
+*
+* chatchat <filename>
+*
+* where <filename> matches the option given in the chat script.
+*
+* for instance the chat script fragment:
+*
+* ...
+* name: \\dmyname \
+* word: @/var/tmp/p \
+* ...
+* ^
+* (note: leave some whitespace after the filename)
+*
+* expect "name:", reply with a delay followed by "myname"
+* expect "word:", reply with the data read from the pipe /var/tmp/p
+*
+* the matching usage of chatchat would be:
+*
+* chatchat /var/tmp/p
+*
+* eg:
+*
+* $chatchat /var/tmp/p
+* ...
+* some other process eventually starts:
+* chat ...
+* chat parses the "@/var/tmp/p" option and opens
+* /var/tmp/p
+* (chatchat prompts:)
+*
+* type PIN into SecurID card
+* enter resulting passcode: [user inputs something]
+*
+* chat reads /var/tmp/p & gets what the
+* user typed at chatchat's "enter string" prompt
+* chat removes the pipe file
+* chat sends the user's input as a response in
+* place of "@/var/tmp/p"
+*
+* PROCESS:
+*
+* gcc -g -o chatchat chatchat.c
+*
+*
+* GLOBALS: none
+*
+* REFERENCES:
+*
+* see the man pages and documentation that come with the 'chat' program
+* (part of the ppp package). you will need to use the modified chat
+* program that accepts the '@' operator.
+*
+* LIMITATIONS:
+*
+* REVISION HISTORY:
+*
+* STR Description Author
+*
+* 23-Mar-99 initial coding gpk
+* 12-May-99 unlink the pipe after closing paulus
+*
+* TARGET: ANSI C
+* This program is in the public domain.
+*
+*
+* ************************************************************************* */
+
+
+
+
+#include <sys/time.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+/* MAXINPUT - the data typed into chatchat must be fewer */
+/* characters than this. */
+
+#define MAXINPUT 80
+
+
+
+
+
+
+/* *************************************************************************
+
+
+ NAME: main
+
+
+ USAGE:
+
+ int argc;
+ char * argv[];
+
+ main(argc, argv[]);
+
+ returns: int
+
+ DESCRIPTION:
+ if the pipe file name is given on the command line,
+ create the pipe, prompt the user and put whatever
+ is typed into the pipe.
+
+ returns -1 on error
+ else # characters entered
+ REFERENCES:
+
+ LIMITATIONS:
+
+ GLOBAL VARIABLES:
+
+ accessed: none
+
+ modified: none
+
+ FUNCTIONS CALLED:
+
+ REVISION HISTORY:
+
+ STR Description of Revision Author
+
+ 25-Mar-99 initial coding gpk
+
+ ************************************************************************* */
+
+int main(int argc, char * argv[])
+{
+ int retval;
+
+ int create_and_write_pipe(char * pipename);
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "usage: %s pipename\n", argv[0]);
+ retval = -1;
+ }
+ else
+ {
+ retval = create_and_write_pipe(argv[1]);
+ }
+ return (retval);
+}
+
+
+
+
+/* *************************************************************************
+
+
+ NAME: create_and_write_pipe
+
+
+ USAGE:
+
+ int some_int;
+ char * pipename;
+
+ some_int = create_and_write_pipe(pipename);
+
+ returns: int
+
+ DESCRIPTION:
+ given the pipename, create the pipe, open it,
+ prompt the user for a string to put into the
+ pipe, write the string, and close the pipe
+
+ on error, print out an error message and return -1
+
+ returns -1 on error
+ else #bytes written into the pipe
+ REFERENCES:
+
+ LIMITATIONS:
+
+ GLOBAL VARIABLES:
+
+ accessed: none
+
+ modified: none
+
+ FUNCTIONS CALLED:
+
+ REVISION HISTORY:
+
+ STR Description of Revision Author
+
+ 25-Mar-99 initial coding gpk
+ 12-May-99 remove pipe after closing paulus
+
+ ************************************************************************* */
+
+int create_and_write_pipe(char * pipename)
+{
+ int retval, created, pipefd, nread, nwritten;
+ char input[MAXINPUT];
+ char errstring[180];
+
+ int create_pipe(char * pipename);
+ int write_to_pipe(int pipefd, char * input, int nchar);
+
+ created = create_pipe(pipename);
+
+ if (-1 == created)
+ {
+ sprintf(errstring, "unable to create pipe '%s'", pipename);
+ perror(errstring);
+ retval = -1;
+ }
+ else
+ {
+
+ /* note: this open won't succeed until chat has the pipe */
+ /* open and ready to read. this makes for nice timing. */
+
+ pipefd = open(pipename, O_WRONLY);
+
+ if (-1 == pipefd)
+ {
+ sprintf(errstring, "unable to open pipe '%s'", pipename);
+ perror(errstring);
+ retval = -1;
+ }
+ else
+ {
+ fprintf(stderr, "%s \n %s",
+ "type PIN into SecurID card and",
+ "enter resulting passcode:");
+ nread = read(STDIN_FILENO, (void *)input, MAXINPUT);
+
+
+ if (0 >= nread)
+ {
+ perror("unable to read from stdin");
+ retval = -1;
+ }
+ else
+ {
+ /* munch off the newline character, chat supplies */
+ /* a return when it sends the string out. */
+ input[nread -1] = 0;
+ nread--;
+ nwritten = write_to_pipe(pipefd, input, nread);
+ /* printf("wrote [%d]: '%s'\n", nwritten, input); */
+ retval = nwritten;
+ }
+ close(pipefd);
+
+ /* Now make the pipe go away. It won't actually go away
+ completely until chat closes it. */
+ if (unlink(pipename) < 0)
+ perror("Warning: couldn't remove pipe");
+ }
+ }
+ return(retval);
+}
+
+
+
+
+
+
+
+/* *************************************************************************
+
+
+ NAME: create_pipe
+
+
+ USAGE:
+
+ int some_int;
+ char * pipename;
+
+ some_int = create_pipe(pipename);
+
+ returns: int
+
+ DESCRIPTION:
+ create a pipe of the given name
+
+ if there is an error (like the pipe already exists)
+ print an error message and return
+
+ return -1 on failure else success
+
+ REFERENCES:
+
+ LIMITATIONS:
+
+ GLOBAL VARIABLES:
+
+ accessed: none
+
+ modified: none
+
+ FUNCTIONS CALLED:
+
+ REVISION HISTORY:
+
+ STR Description of Revision Author
+
+ 25-Mar-99 initial coding gpk
+
+ ************************************************************************* */
+
+int create_pipe(char * pipename)
+{
+ mode_t old_umask;
+ int created;
+
+ /* hijack the umask temporarily to get the mode I want */
+ /* on the pipe. */
+
+ old_umask = umask(000);
+
+ created = mknod(pipename, S_IFIFO | S_IRWXU | S_IWGRP | S_IWOTH,
+ (dev_t)NULL);
+
+ /* now restore umask. */
+
+ (void)umask(old_umask);
+
+ if (-1 == created)
+ {
+ perror("unable to create pipe");
+ }
+
+ return(created);
+}
+
+
+
+
+
+
+/* *************************************************************************
+
+
+ NAME: write_to_pipe
+
+
+ USAGE:
+
+ int some_int;
+ int pipefd;
+ char * input;
+ int nchar;
+
+ some_int = write_to_pipe(pipefd, input, nchar);
+
+ returns: int
+
+ DESCRIPTION:
+ write nchars of data from input to pipefd
+
+ on error print a message to stderr
+
+ return -1 on error, else # bytes written
+ REFERENCES:
+
+ LIMITATIONS:
+
+ GLOBAL VARIABLES:
+
+ accessed: none
+
+ modified: none
+
+ FUNCTIONS CALLED:
+
+ REVISION HISTORY:
+
+ STR Description of Revision Author
+
+ 25-Mar-99 initial coding gpk
+ 12-May-99 don't write count word first paulus
+
+ ************************************************************************* */
+
+int write_to_pipe(int pipefd, char * input, int nchar)
+{
+ int nwritten;
+
+ /* nwritten = write(pipefd, (void *)&nchar, sizeof(nchar)); */
+ nwritten = write(pipefd, (void *)input, nchar);
+
+ if (-1 == nwritten)
+ {
+ perror("unable to write to pipe");
+ }
+
+ return(nwritten);
+}
diff --git a/mdk-stage1/ppp/scripts/ip-down.local.add b/mdk-stage1/ppp/scripts/ip-down.local.add
new file mode 100644
index 000000000..b93590e49
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/ip-down.local.add
@@ -0,0 +1,20 @@
+
+#
+# This sample code shows you one way to modify your setup to allow automatic
+# configuration of your resolv.conf for peer supplied DNS addresses when using
+# the `usepeerdns' option.
+#
+# In my case I just added this to my /etc/ppp/ip-down.local script. You may need to
+# create an executable script if one does not exist.
+#
+# Nick Walker (nickwalker@email.com)
+#
+
+if [ -n "$USEPEERDNS" -a -f /etc/ppp/resolv.conf ]; then
+ if [ -f /etc/ppp/resolv.prev ]; then
+ cp -f /etc/ppp/resolv.prev /etc/resolv.conf
+ else
+ rm -f /etc/resolv.conf
+ fi
+fi
+
diff --git a/mdk-stage1/ppp/scripts/ip-up.local.add b/mdk-stage1/ppp/scripts/ip-up.local.add
new file mode 100644
index 000000000..80172093a
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/ip-up.local.add
@@ -0,0 +1,24 @@
+
+#
+# This sample code shows you one way to modify your setup to allow automatic
+# configuration of your resolv.conf for peer supplied DNS addresses when using
+# the `usepeerdns' option.
+#
+# In my case I just added this to my /etc/ppp/ip-up.local script. You may need to
+# create an executable script if one does not exist.
+#
+# Nick Walker (nickwalker@email.com)
+#
+
+if [ -n "$USEPEERDNS" -a -f /etc/ppp/resolv.conf ]; then
+ rm -f /etc/ppp/resolv.prev
+ if [ -f /etc/resolv.conf ]; then
+ cp /etc/resolv.conf /etc/ppp/resolv.prev
+ grep domain /etc/ppp/resolv.prev > /etc/resolv.conf
+ grep search /etc/ppp/resolv.prev >> /etc/resolv.conf
+ cat /etc/ppp/resolv.conf >> /etc/resolv.conf
+ else
+ cp /etc/ppp/resolv.conf /etc
+ fi
+fi
+
diff --git a/mdk-stage1/ppp/scripts/options-rsh-loc b/mdk-stage1/ppp/scripts/options-rsh-loc
new file mode 100644
index 000000000..b015b87fe
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/options-rsh-loc
@@ -0,0 +1 @@
+debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1460
diff --git a/mdk-stage1/ppp/scripts/options-rsh-rem b/mdk-stage1/ppp/scripts/options-rsh-rem
new file mode 100644
index 000000000..4b10bb9e9
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/options-rsh-rem
@@ -0,0 +1 @@
+notty debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1460
diff --git a/mdk-stage1/ppp/scripts/options-ssh-loc b/mdk-stage1/ppp/scripts/options-ssh-loc
new file mode 100644
index 000000000..add03d659
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/options-ssh-loc
@@ -0,0 +1 @@
+debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1400
diff --git a/mdk-stage1/ppp/scripts/options-ssh-rem b/mdk-stage1/ppp/scripts/options-ssh-rem
new file mode 100644
index 000000000..d690722c6
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/options-ssh-rem
@@ -0,0 +1 @@
+notty debug asyncmap FFFFFFFF escape FF kdebug 0 noipdefault nodefaultroute noauth mtu 1400
diff --git a/mdk-stage1/ppp/scripts/ppp-off b/mdk-stage1/ppp/scripts/ppp-off
new file mode 100755
index 000000000..a22b5ea3d
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/ppp-off
@@ -0,0 +1,34 @@
+#!/bin/sh
+######################################################################
+#
+# Determine the device to be terminated.
+#
+if [ "$1" = "" ]; then
+ DEVICE=ppp0
+else
+ DEVICE=$1
+fi
+
+######################################################################
+#
+# If the ppp0 pid file is present then the program is running. Stop it.
+if [ -r /var/run/$DEVICE.pid ]; then
+ kill -INT `cat /var/run/$DEVICE.pid`
+#
+# If the kill did not work then there is no process running for this
+# pid. It may also mean that the lock file will be left. You may wish
+# to delete the lock file at the same time.
+ if [ ! "$?" = "0" ]; then
+ rm -f /var/run/$DEVICE.pid
+ echo "ERROR: Removed stale pid file"
+ exit 1
+ fi
+#
+# Success. Let pppd clean up its own junk.
+ echo "PPP link to $DEVICE terminated."
+ exit 0
+fi
+#
+# The ppp process is not running for ppp0
+echo "ERROR: PPP link is not active on $DEVICE"
+exit 1
diff --git a/mdk-stage1/ppp/scripts/ppp-on b/mdk-stage1/ppp/scripts/ppp-on
new file mode 100755
index 000000000..ab79db471
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/ppp-on
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Script to initiate a ppp connection. This is the first part of the
+# pair of scripts. This is not a secure pair of scripts as the codes
+# are visible with the 'ps' command. However, it is simple.
+#
+# These are the parameters. Change as needed.
+TELEPHONE=555-1212 # The telephone number for the connection
+ACCOUNT=george # The account name for logon (as in 'George Burns')
+PASSWORD=gracie # The password for this account (and 'Gracie Allen')
+LOCAL_IP=0.0.0.0 # Local IP address if known. Dynamic = 0.0.0.0
+REMOTE_IP=0.0.0.0 # Remote IP address if desired. Normally 0.0.0.0
+NETMASK=255.255.255.0 # The proper netmask if needed
+#
+# Export them so that they will be available at 'ppp-on-dialer' time.
+export TELEPHONE ACCOUNT PASSWORD
+#
+# This is the location of the script which dials the phone and logs
+# in. Please use the absolute file name as the $PATH variable is not
+# used on the connect option. (To do so on a 'root' account would be
+# a security hole so don't ask.)
+#
+DIALER_SCRIPT=/etc/ppp/ppp-on-dialer
+#
+# Initiate the connection
+#
+# I put most of the common options on this command. Please, don't
+# forget the 'lock' option or some programs such as mgetty will not
+# work. The asyncmap and escape will permit the PPP link to work with
+# a telnet or rlogin connection. You are welcome to make any changes
+# as desired. Don't use the 'defaultroute' option if you currently
+# have a default route to an ethernet gateway.
+#
+exec /usr/sbin/pppd debug lock modem crtscts /dev/ttyS0 38400 \
+ asyncmap 20A0000 escape FF kdebug 0 $LOCAL_IP:$REMOTE_IP \
+ noipdefault netmask $NETMASK defaultroute connect $DIALER_SCRIPT
diff --git a/mdk-stage1/ppp/scripts/ppp-on-dialer b/mdk-stage1/ppp/scripts/ppp-on-dialer
new file mode 100755
index 000000000..7d66765f1
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/ppp-on-dialer
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# This is part 2 of the ppp-on script. It will perform the connection
+# protocol for the desired connection.
+#
+exec chat -v \
+ TIMEOUT 3 \
+ ABORT '\nBUSY\r' \
+ ABORT '\nNO ANSWER\r' \
+ ABORT '\nRINGING\r\n\r\nRINGING\r' \
+ '' \rAT \
+ 'OK-+++\c-OK' ATH0 \
+ TIMEOUT 30 \
+ OK ATDT$TELEPHONE \
+ CONNECT '' \
+ ogin:--ogin: $ACCOUNT \
+ assword: $PASSWORD
diff --git a/mdk-stage1/ppp/scripts/ppp-on-rsh b/mdk-stage1/ppp/scripts/ppp-on-rsh
new file mode 100755
index 000000000..30a50dba6
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/ppp-on-rsh
@@ -0,0 +1,72 @@
+#!/bin/sh
+#
+# A sample script to establish PPP session(s) via rsh
+#
+# Adi Masputra <adi.masputra@sun.com>
+# Jan 24, 2000
+#
+
+#
+# You'd definitely want to change the following addresses to suit
+# your network configuration
+#
+LOC_IP=10.0.0.1
+REM_IP=10.0.0.2
+NETMASK=255.255.0.0
+
+export LOC_IP REM_IP
+
+#
+# This is the remote peer where in.rshd is running, either
+# its hostname or IP address
+#
+PPPD_RHOST=myremotehost
+
+#
+# For this example, we assume that pppd on both local and remote
+# machines reside in the same place, /usr/local/bin/pppd
+#
+PPPD_LOC=/usr/local/bin/pppd
+
+#
+# The location of local options file (where rsh client is running).
+# Note that the sample options file included in the distribution
+# may need further customizations, depending on your needs. The 'noauth'
+# option specified in the file is there to simplify the example. In
+# reality, you'd probably want to remove such option.
+#
+PPPD_LOC_OPT=/etc/ppp/options-rsh-loc
+
+#
+# The location of remote options file (where in.rshd daemon is running).
+# Note that the sample options file included in the distribution
+# may need further customizations, depending on your needs. The 'noauth'
+# option specified in the file is there to simplify the example. In
+# reality, you'd probably want to remove such option. Also note that
+# the remote options file need to include the 'notty' option for this
+# to work
+#
+PPPD_REM_OPT=/etc/ppp/options-rsh-rem
+
+#
+# The location of rsh client on the local machine
+#
+RSH_LOC=/bin/rsh
+
+export PPPD_LOC PPPD_LOC_OPT PPPD_REM_OPT PPPD_RHOST RSH_LOC
+
+#
+# Uncomment the following to enable IPv6, note that the IPv6 support
+# needs to be enabled during compilation
+#
+# PPPD_IPV6='+ipv6 ipv6cp-use-ipaddr'
+export PPPD_IPV6
+
+#
+# And execute pppd with the pty option, specifying rsh client as the
+# slave side of the pseduo-tty master/slave pair.
+#
+exec $PPPD_LOC \
+ pty '$RSH_LOC $PPPD_RHOST $PPPD_LOC $REM_IP:$LOC_IP $PPPD_IPV6 file $PPPD_REM_OPT' \
+ $LOC_IP:$REM_IP netmask $NETMASK $PPPD_IPV6 file $PPPD_LOC_OPT
+
diff --git a/mdk-stage1/ppp/scripts/ppp-on-ssh b/mdk-stage1/ppp/scripts/ppp-on-ssh
new file mode 100755
index 000000000..0e41acac6
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/ppp-on-ssh
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+# A sample script to establish PPP session(s) via SSH 1.x
+#
+# Adi Masputra <adi.masputra@sun.com>
+# Jan 24, 2000
+#
+
+#
+# You'd definitely want to change the following addresses to suit
+# your network configuration
+#
+LOC_IP=10.0.0.1
+REM_IP=10.0.0.2
+NETMASK=255.255.0.0
+
+export LOC_IP REM_IP
+
+#
+# This is the remote peer where sshd is running, either
+# its hostname or IP address
+#
+PPPD_RHOST=myremotehost
+
+#
+# For this example, we assume that pppd on both local and remote
+# machines reside in the same place, /usr/local/bin/pppd
+#
+PPPD_LOC=/usr/local/bin/pppd
+
+#
+# The location of local options file (where ssh client is running).
+# Note that the sample options file included in the distribution
+# may need further customizations, depending on your needs. The 'noauth'
+# option specified in the file is there to simplify the example, although
+# some may choose to have it there and rely on ssh authentication
+# instead.
+#
+PPPD_LOC_OPT=/etc/ppp/options-ssh-loc
+
+#
+# The location of remote options file (where sshd daemon is running)
+# Note that the sample options file included in the distribution
+# may need further customizations, depending on your needs. The 'noauth'
+# option specified in the file is there to simplify the example, although
+# some may choose to have it there and rely on ssh authentication
+# instead. Also note that the remote options file need to include the 'notty'
+# options for this to work.
+#
+PPPD_REM_OPT=/etc/ppp/options-ssh-rem
+
+#
+# The location of ssh client on the local machine
+#
+SSH_LOC=/usr/local/bin/ssh
+
+export PPPD_LOC PPPD_LOC_OPT PPPD_REM_OPT PPPD_RHOST SSH_LOC
+
+#
+# Uncomment the following to enable IPv6, note that the IPv6 support
+# needs to be enabled during compilation
+#
+# PPPD_IPV6='+ipv6 ipv6cp-use-ipaddr'
+export PPPD_IPV6
+
+#
+# And execute pppd with the pty option, specifying ssh client as the
+# slave side of the pseudo-tty master/slave pair. Note that on this example,
+# ssh has been compiled to allow NULL encryption (thus the '-c none' option),
+# but in reality, you'd probably want to specify the encryption algorithm.
+# See the man page of ssh(1) for details.
+#
+exec $PPPD_LOC \
+ pty '$SSH_LOC -c none $PPPD_RHOST $PPPD_LOC $REM_IP:$LOC_IP $PPPD_IPV6 file $PPPD_REM_OPT' \
+ $LOC_IP:$REM_IP netmask $NETMASK $PPPD_IPV6 file $PPPD_LOC_OPT
+
diff --git a/mdk-stage1/ppp/scripts/redialer b/mdk-stage1/ppp/scripts/redialer
new file mode 100755
index 000000000..5bbde4e9d
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/redialer
@@ -0,0 +1,96 @@
+#!/bin/sh
+###################################################################
+#
+# These parameters control the attack dialing sequence.
+#
+# Maximum number of attempts to reach the telephone number(s)
+MAX_ATTEMPTS=10
+
+# Delay between each of the attempts. This is a parameter to sleep
+# so use "15s" for 15 seconds, "1m" for 1 minute, etc.
+SLEEP_DELAY=15s
+
+###################################################################
+#
+# This is a list of telephone numbers. Add new numbers if you wish
+# and see the function 'callall' below for the dial process.
+PHONE1=555-1212
+PHONE2=411
+
+###################################################################
+#
+# If you use the ppp-on script, then these are passed to this routine
+# automatically. There is no need to define them here. If not, then
+# you will need to set the values.
+#
+ACCOUNT=my_account_name
+PASSWORD=my_password
+
+###################################################################
+#
+# Function to initialize the modem and ensure that it is in command
+# state. This may not be needed, but it doesn't hurt.
+#
+function initialize
+{
+ chat -v TIMEOUT 3 '' AT 'OK-+++\c-OK'
+ return
+}
+
+###################################################################
+#
+# Script to dial a telephone
+#
+function callnumber
+{
+chat -v \
+ ABORT '\nBUSY\r' \
+ ABORT '\nNO ANSWER\r' \
+ ABORT '\nRINGING\r\n\r\nRINGING\r' \
+ '' ATDT$1 \
+ CONNECT '' \
+ ogin:--ogin: $ACCOUNT \
+ assword: $PASSWORD
+#
+# If the connection was successful then end the whole script with a
+# success.
+#
+ if [ "$?" = "0" ]; then
+ exit 0
+ fi
+
+ return
+}
+
+###################################################################
+#
+# Script to dial any telephone number
+#
+function callall
+{
+# echo "dialing attempt number: $1" >/dev/console
+ callnumber $PHONE1
+# callnumber $PHONE2
+}
+
+###################################################################
+#
+# Initialize the modem to ensure that it is in the command state
+#
+initialize
+if [ ! "$?" = "0" ]; then
+ exit 1
+fi
+
+#
+# Dial telephone numbers until one answers
+#
+attempt=0
+while : ; do
+ attempt=`expr $attempt + 1`
+ callall $attempt
+ if [ "$attempt" = "$MAX_ATTEMPTS" ]; then
+ exit 1
+ fi
+ sleep "$SLEEP_DELAY"
+done
diff --git a/mdk-stage1/ppp/scripts/secure-card b/mdk-stage1/ppp/scripts/secure-card
new file mode 100644
index 000000000..a32138b7d
--- /dev/null
+++ b/mdk-stage1/ppp/scripts/secure-card
@@ -0,0 +1,111 @@
+#!/usr/local/bin/expect -f
+#
+# This script was written by Jim Isaacson <jcisaac@crl.com>. It is
+# designed to work as a script to use the SecureCARD(tm) device. This
+# little device is mated with a central controller. The number displayed
+# on this card changes every so often and you need to enter the number
+# along with your user account name in order to gain access. Since chat
+# is based upon fixed strings this procedure will not work with chat.
+#
+# It is included by permission. An excellent reference for the expect
+# program used by this script is in the book:
+#
+# "Exploring Expect"
+# by Don Libes
+# Published by O'Rielly and Associates
+#
+
+send_user "hello, starting ppp\n"
+
+system "stty 19200 -echoe -echo raw < /dev/cua3 > /dev/cua3"
+
+#
+# These are the parameters for the program.
+#
+set user Pxxxxxx
+set password xxxxxxx
+set modem /dev/cua3
+set dialup <put phone number here>
+set timeout 60
+
+spawn -noecho -open [open $modem "r+"]
+
+send "AT&F\r"
+expect "OK"
+
+send "ATe0v1x4&c1q0&d2&c1s2=128s0=0DT $dialup\r"
+set timeout 15
+set counter 0
+
+set still_connecting 1
+
+expect {
+ -re ".*CONNECT.*\n" {
+ set timeout 5
+ set still_connecting 0
+ continue -expect
+ }
+ -re ".*CONNECT.*\r" {
+ set timeout 5
+ set still_connecting 0
+ continue -expect
+ }
+ -re ".*NO.*CARRIER" {
+ send_user "Failed to Connect, exiting...\n"
+ exit
+ }
+ -re ".*NO.*DIAL.*TONE" {
+ send_user "Failed to Connect, exiting...\n"
+ exit
+ }
+ -re ".*VOICE" {
+ send_user "Failed to Connect, exiting...\n"
+ exit
+ }
+ -re ".*sscode:.*\n" {
+ continue -expect
+ }
+ -re ".*sscode:" {
+ set timeout -1
+ expect_user -re "(.*)\n"
+ send "$expect_out(1,string)\r"
+ set timeout 30
+ continue -expect
+ }
+ -re ".*Next.*:" {
+ set timeout -1
+ expect_user -re "(.*)\n"
+ send "$expect_out(1,string)\r"
+ set timeout 30
+ continue -expect
+ }
+ -re "Your.*" {
+ send "\r"
+ continue -expect
+ }
+ -re ".*in:" {
+ send "$user\r"
+ continue -expect
+ }
+ -re ".*word:" {
+ send "$password\r"
+ }
+
+ timeout {
+ if { $still_connecting > 0 } {
+ continue -expect
+ }
+ set timeout 15
+ send "\r"
+ incr counter
+ if { $counter > 8 } {
+ send_user "Cannot Connect\n"
+ exit
+ } else {
+ continue -expect
+ }
+ }
+}
+
+overlay -0 $spawn_id -1 $spawn_id pppd /dev/cua3 19200 192.111.187.215: \
+ crtscts modem defaultroute debug
diff --git a/mdk-stage1/ppp/solaris/Makedefs b/mdk-stage1/ppp/solaris/Makedefs
new file mode 100644
index 000000000..81db8ab2e
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/Makedefs
@@ -0,0 +1,16 @@
+#
+# defines common to several Makefiles
+#
+
+INSTALL= /usr/sbin/install
+
+BINDIR = /usr/local/bin
+MANDIR = /usr/local/man
+ETCDIR = /etc/ppp
+
+COPTS = -O -Xa
+
+# For compiling with gcc, comment out the COPTS definition above and
+# uncomment the next 2 definitions.
+#CC = gcc
+#COPTS = -O2
diff --git a/mdk-stage1/ppp/solaris/Makedefs.sol2 b/mdk-stage1/ppp/solaris/Makedefs.sol2
new file mode 100644
index 000000000..1282c6e74
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/Makedefs.sol2
@@ -0,0 +1,59 @@
+#
+# Generic make definitions for Solaris 2
+#
+# $Id$
+#
+
+include ../solaris/Makedefs
+
+CPPFLAGS = -D_KERNEL -DSVR4 -DSOL2 -DPRIOQ -DDEBUG -I../include
+CFLAGS = $(CPPFLAGS) $(COPTS)
+
+# lint-specific variables
+LINT = lint
+LINT_OPT_32 =
+LINT_OPT_64 = -Xarch=v9 -errchk=longptr64
+
+LINT_32 =
+LINT_32 += -erroff=E_BAD_PTR_CAST_ALIGN
+LINT_32 += -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED
+LINT_32 += -erroff=E_SUSPICIOUS_COMPARISON
+LINT_32 += -erroff=E_CAST_UINT_TO_SIGNED_INT
+LINT_32 += -erroff=E_PASS_UINT_TO_SIGNED_INT
+LINT_32 += -erroff=E_INVALID_ANNOTATION_NAME
+LINT_32 += -erroff=E_FUNC_ARG_UNUSED
+# This might be needed, but zlib.c and vjcompress.c will squawk
+# when not ignored
+LINT_32 += -erroff=E_CASE_FALLTHRU
+LINT_32 += -erroff=E_RET_INT_IMPLICITLY
+LINT_32 += -erroff=E_FUNC_NO_RET_VAL
+# Some STREAMS macros will be noisy too when this isn't ignored
+LINT_32 += -erroff=E_CONSTANT_CONDITION
+LINT_32 += -erroff=E_CONST_EXPR
+
+# Extra noise suppressant for 64-bit
+EXTRA_OFF =
+EXTRA_OFF += -erroff=E_CAST_INT_TO_SMALL_INT
+EXTRA_OFF += -erroff=E_CAST_INT_CONST_TO_SMALL_INT
+EXTRA_OFF += -erroff=E_CAST_TO_PTR_FROM_INT
+EXTRA_OFF += -erroff=E_ASSIGN_INT_TO_SMALL_INT
+EXTRA_OFF += -erroff=E_ASSIGN_INT_FROM_BIG_CONST
+EXTRA_OFF += -erroff=E_CONST_PROMOTED_UNSIGNED_LL
+EXTRA_OFF += -erroff=E_CONST_PROMOTED_LONG_LONG
+EXTRA_OFF += -erroff=E_CONST_TRUNCATED_BY_ASSIGN
+EXTRA_OFF += -erroff=E_PASS_INT_FROM_BIG_CONST
+EXTRA_OFF += -erroff=E_COMP_INT_WITH_LARGE_INT
+EXTRA_OFF += -erroff=E_ASSIGN_UINT_TO_SIGNED_INT
+EXTRA_OFF += -erroff=E_ASSIGN_NARROW_CONV
+EXTRA_OFF += -erroff=E_PASS_INT_TO_SMALL_INT
+EXTRA_OFF += -erroff=E_PTR_CONV_LOSES_BITS
+
+LINT_64 = $(LINT_32)
+LINT_64 += $(EXTRA_OFF)
+
+LINTFLAGS64 = -Xa -nsxmuF -errtags=yes $(LINT_OPT_64) $(LINT_64)
+LINT64 = $(LINT) -c $(LINTFLAGS64) $(CPPFLAGS)
+
+LINTFLAGS32 = -Xa -nsxmuF -errtags=yes $(LINT_OPT_32) $(LINT_32)
+LINT32 = $(LINT) -c $(LINTFLAGS32) $(CPPFLAGS)
+
diff --git a/mdk-stage1/ppp/solaris/Makefile.sol2 b/mdk-stage1/ppp/solaris/Makefile.sol2
new file mode 100644
index 000000000..78628847b
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/Makefile.sol2
@@ -0,0 +1,66 @@
+#
+# Makefile for STREAMS modules for Solaris 2.
+#
+# $Id$
+#
+
+include Makedefs.sol2
+
+COPTS += -xO2 -xspace -W0,-Lt
+
+COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \
+ ppp_comp_mod.o
+
+all: ppp ppp_ahdl ppp_comp
+
+ppp: ppp.o ppp_mod.o
+ ld -r -o $@ ppp.o ppp_mod.o
+ chmod +x $@
+
+ppp_ahdl: ppp_ahdlc.o ppp_ahdlc_mod.o
+ ld -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o
+ chmod +x $@
+
+ppp_comp: $(COMP_OBJS)
+ ld -r -o $@ $(COMP_OBJS)
+ chmod +x $@
+
+bsd-comp.o: ../modules/bsd-comp.c
+ $(CC) $(CFLAGS) -c $?
+deflate.o: ../modules/deflate.c
+ $(CC) $(CFLAGS) -c $?
+ppp.o: ppp.c
+ $(CC) $(CFLAGS) -c $?
+ppp_mod.o: ppp_mod.c
+ $(CC) $(CFLAGS) -c $?
+ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+ $(CC) $(CFLAGS) -c $?
+ppp_ahdlc.o: ppp_ahdlc.c
+ $(CC) $(CFLAGS) -c $?
+ppp_comp.o: ppp_comp.c
+ $(CC) $(CFLAGS) -c $?
+ppp_comp_mod.o: ppp_comp_mod.c
+ $(CC) $(CFLAGS) -c $?
+vjcompress.o: ../modules/vjcompress.c
+ $(CC) $(CFLAGS) -c $?
+zlib.o: ../common/zlib.c
+ $(CC) $(CFLAGS) -c $?
+
+install:
+ cp ppp ppp.conf /kernel/drv
+ cp ppp_comp ppp_ahdl /kernel/strmod
+ if grep clone:ppp /etc/minor_perm; then :; else \
+ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+ /usr/sbin/rem_drv ppp 2>/dev/null || true
+ /usr/sbin/add_drv ppp
+
+SRCS = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+ ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+ $(LINT32) $(SRCS)
+
+clean:
+ rm -f ppp ppp_comp ppp_ahdl *.o *~ core
+ rm -f *.ln
diff --git a/mdk-stage1/ppp/solaris/Makefile.sol2-64 b/mdk-stage1/ppp/solaris/Makefile.sol2-64
new file mode 100644
index 000000000..63e75fc54
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/Makefile.sol2-64
@@ -0,0 +1,85 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2.
+#
+# $Id$
+#
+
+include Makedefs.sol2
+
+# Sun's cc flag for LP64 compilation / linkage
+COPTS += -xchip=ultra -xarch=v9 -Wc,-xcode=abs32 -Wc,-Qiselect-regsym=0 -xO3 -xspace -W0,-Lt
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR = sparcv9
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE = Makefile.sol2
+
+COMP_OBJS = $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+ $(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+ $(LP64DIR)/ppp_comp_mod.o
+
+all: std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+ $(MAKE) -f $(STD_MAKE) all
+
+ppp: $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+ ld -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+ chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+ ld -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+ chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+ ld -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+ chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o: ppp.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o: ppp_mod.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ppp_comp.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o: ../common/zlib.c
+ $(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+ mkdir -m 755 -p $@
+
+install:
+ cp ppp ppp.conf /kernel/drv
+ cp ppp_comp ppp_ahdl /kernel/strmod
+ cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+ cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+ if grep clone:ppp /etc/minor_perm; then :; else \
+ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+ /usr/sbin/rem_drv ppp 2>/dev/null || true
+ /usr/sbin/add_drv ppp
+
+SRCS = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+ ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+ $(LINT64) $(SRCS)
+
+lint-32:
+ $(LINT32) $(SRCS)
+
+clean:
+ $(MAKE) -f $(STD_MAKE) clean
+ rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff --git a/mdk-stage1/ppp/solaris/Makefile.top b/mdk-stage1/ppp/solaris/Makefile.top
new file mode 100644
index 000000000..f1200b852
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/Makefile.top
@@ -0,0 +1,50 @@
+#
+# ppp top level makefile for SVR4 and Solaris 2
+#
+# $Id$
+#
+
+include solaris/Makedefs
+
+all:
+ cd chat; $(MAKE) all
+ cd pppd; $(MAKE) all
+ cd pppstats; $(MAKE) all
+ cd pppdump; $(MAKE) all
+ cd solaris; $(MAKE) all
+
+install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp
+
+install-progs:
+ cd chat; $(MAKE) install
+ cd pppd; $(MAKE) install
+ cd pppstats; $(MAKE) install
+ cd pppdump; $(MAKE) install
+ cd solaris; $(MAKE) install
+
+install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
+ $(ETCDIR)/chap-secrets
+
+$(ETCDIR)/options:
+ cp etc.ppp/options $@
+ chmod go-w $@
+$(ETCDIR)/pap-secrets:
+ $(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/pap-secrets
+$(ETCDIR)/chap-secrets:
+ $(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/chap-secrets
+
+$(BINDIR):
+ mkdir -m 755 -p $@
+$(MANDIR)/man8:
+ mkdir -m 755 -p $@
+$(ETCDIR):
+ mkdir -m 755 -p $@
+
+clean:
+ rm -f *~
+ cd chat; $(MAKE) clean
+ cd pppd; $(MAKE) clean
+ cd pppstats; $(MAKE) clean
+ cd pppdump; $(MAKE) clean
+ cd solaris; $(MAKE) clean
+
diff --git a/mdk-stage1/ppp/solaris/ppp.c b/mdk-stage1/ppp/solaris/ppp.c
new file mode 100644
index 000000000..44bf08dff
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/ppp.c
@@ -0,0 +1,2486 @@
+/*
+ * ppp.c - STREAMS multiplexing pseudo-device driver for PPP.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/errno.h>
+#ifdef __osf__
+#include <sys/ioctl.h>
+#include <sys/cmn_err.h>
+#define queclass(mp) ((mp)->b_band & QPCTL)
+#else
+#include <sys/ioccom.h>
+#endif
+#include <sys/time.h>
+#ifdef SVR4
+#include <sys/cmn_err.h>
+#include <sys/conf.h>
+#include <sys/dlpi.h>
+#include <sys/ddi.h>
+#ifdef SOL2
+#include <sys/ksynch.h>
+#include <sys/kstat.h>
+#include <sys/sunddi.h>
+#include <sys/ethernet.h>
+#else
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#endif /* SOL2 */
+#else /* not SVR4 */
+#include <sys/user.h>
+#endif /* SVR4 */
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include "ppp_mod.h"
+
+/*
+ * Modifications marked with #ifdef PRIOQ are for priority queueing of
+ * interactive traffic, and are due to Marko Zec <zec@japa.tel.fer.hr>.
+ */
+#ifdef PRIOQ
+#endif /* PRIOQ */
+
+#include <netinet/in.h> /* leave this outside of PRIOQ for htons */
+
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+
+/*
+ * The IP module may use this SAP value for IP packets.
+ */
+#ifndef ETHERTYPE_IP
+#define ETHERTYPE_IP 0x800
+#endif
+
+#if !defined(ETHERTYPE_IPV6)
+#define ETHERTYPE_IPV6 0x86dd
+#endif /* !defined(ETHERTYPE_IPV6) */
+
+#if !defined(ETHERTYPE_ALLSAP) && defined(SOL2)
+#define ETHERTYPE_ALLSAP 0
+#endif /* !defined(ETHERTYPE_ALLSAP) && defined(SOL2) */
+
+#if !defined(PPP_ALLSAP) && defined(SOL2)
+#define PPP_ALLSAP PPP_ALLSTATIONS
+#endif /* !defined(PPP_ALLSAP) && defined(SOL2) */
+
+extern time_t time;
+
+#ifdef SOL2
+/*
+ * We use this reader-writer lock to ensure that the lower streams
+ * stay connected to the upper streams while the lower-side put and
+ * service procedures are running. Essentially it is an existence
+ * lock for the upper stream associated with each lower stream.
+ */
+krwlock_t ppp_lower_lock;
+#define LOCK_LOWER_W rw_enter(&ppp_lower_lock, RW_WRITER)
+#define LOCK_LOWER_R rw_enter(&ppp_lower_lock, RW_READER)
+#define TRYLOCK_LOWER_R rw_tryenter(&ppp_lower_lock, RW_READER)
+#define UNLOCK_LOWER rw_exit(&ppp_lower_lock)
+
+#define MT_ENTER(x) mutex_enter(x)
+#define MT_EXIT(x) mutex_exit(x)
+
+/*
+ * Notes on multithreaded implementation for Solaris 2:
+ *
+ * We use an inner perimeter around each queue pair and an outer
+ * perimeter around the whole driver. The inner perimeter is
+ * entered exclusively for all entry points (open, close, put,
+ * service). The outer perimeter is entered exclusively for open
+ * and close and shared for put and service. This is all done for
+ * us by the streams framework.
+ *
+ * I used to think that the perimeters were entered for the lower
+ * streams' put and service routines as well as for the upper streams'.
+ * Because of problems experienced by people, and after reading the
+ * documentation more closely, I now don't think that is true. So we
+ * now use ppp_lower_lock to give us an existence guarantee on the
+ * upper stream controlling each lower stream.
+ *
+ * Shared entry to the outer perimeter protects the existence of all
+ * the upper streams and their upperstr_t structures, and guarantees
+ * that the following fields of any upperstr_t won't change:
+ * nextmn, next, nextppa. It guarantees that the lowerq field of an
+ * upperstr_t won't go from non-zero to zero, that the global `ppas'
+ * won't change and that the no lower stream will get unlinked.
+ *
+ * Shared (reader) access to ppa_lower_lock guarantees that no lower
+ * stream will be unlinked and that the lowerq field of all upperstr_t
+ * structures won't change.
+ */
+
+#else /* SOL2 */
+#define LOCK_LOWER_W 0
+#define LOCK_LOWER_R 0
+#define TRYLOCK_LOWER_R 1
+#define UNLOCK_LOWER 0
+#define MT_ENTER(x) 0
+#define MT_EXIT(x) 0
+
+#endif /* SOL2 */
+
+/*
+ * Private information; one per upper stream.
+ */
+typedef struct upperstr {
+ minor_t mn; /* minor device number */
+ struct upperstr *nextmn; /* next minor device */
+ queue_t *q; /* read q associated with this upper stream */
+ int flags; /* flag bits, see below */
+ int state; /* current DLPI state */
+ int sap; /* service access point */
+ int req_sap; /* which SAP the DLPI client requested */
+ struct upperstr *ppa; /* control stream for our ppa */
+ struct upperstr *next; /* next stream for this ppa */
+ uint ioc_id; /* last ioctl ID for this stream */
+ enum NPmode npmode; /* what to do with packets on this SAP */
+ unsigned char rblocked; /* flow control has blocked upper read strm */
+ /* N.B. rblocked is only changed by control stream's put/srv procs */
+ /*
+ * There is exactly one control stream for each PPA.
+ * The following fields are only used for control streams.
+ */
+ int ppa_id;
+ queue_t *lowerq; /* write queue attached below this PPA */
+ struct upperstr *nextppa; /* next control stream */
+ int mru;
+ int mtu;
+ struct pppstat stats; /* statistics */
+ time_t last_sent; /* time last NP packet sent */
+ time_t last_recv; /* time last NP packet rcvd */
+#ifdef SOL2
+ kmutex_t stats_lock; /* lock for stats updates */
+ kstat_t *kstats; /* stats for netstat */
+#endif /* SOL2 */
+#ifdef LACHTCP
+ int ifflags;
+ char ifname[IFNAMSIZ];
+ struct ifstats ifstats;
+#endif /* LACHTCP */
+} upperstr_t;
+
+/* Values for flags */
+#define US_PRIV 1 /* stream was opened by superuser */
+#define US_CONTROL 2 /* stream is a control stream */
+#define US_BLOCKED 4 /* flow ctrl has blocked lower write stream */
+#define US_LASTMOD 8 /* no PPP modules below us */
+#define US_DBGLOG 0x10 /* log various occurrences */
+#define US_RBLOCKED 0x20 /* flow ctrl has blocked upper read stream */
+
+#if defined(SOL2)
+#if DL_CURRENT_VERSION >= 2
+#define US_PROMISC 0x40 /* stream is promiscuous */
+#endif /* DL_CURRENT_VERSION >= 2 */
+#define US_RAWDATA 0x80 /* raw M_DATA, no DLPI header */
+#endif /* defined(SOL2) */
+
+#ifdef PRIOQ
+static u_char max_band=0;
+static u_char def_band=0;
+
+#define IPPORT_DEFAULT 65535
+
+/*
+ * Port priority table
+ * Highest priority ports are listed first, lowest are listed last.
+ * ICMP & packets using unlisted ports will be treated as "default".
+ * If IPPORT_DEFAULT is not listed here, "default" packets will be
+ * assigned lowest priority.
+ * Each line should be terminated with "0".
+ * Line containing only "0" marks the end of the list.
+ */
+
+static u_short prioq_table[]= {
+ 113, 53, 0,
+ 22, 23, 513, 517, 518, 0,
+ 514, 21, 79, 111, 0,
+ 25, 109, 110, 0,
+ IPPORT_DEFAULT, 0,
+ 20, 70, 80, 8001, 8008, 8080, 0, /* 8001,8008,8080 - common proxy ports */
+0 };
+
+#endif /* PRIOQ */
+
+
+static upperstr_t *minor_devs = NULL;
+static upperstr_t *ppas = NULL;
+
+#ifdef SVR4
+static int pppopen __P((queue_t *, dev_t *, int, int, cred_t *));
+static int pppclose __P((queue_t *, int, cred_t *));
+#else
+static int pppopen __P((queue_t *, int, int, int));
+static int pppclose __P((queue_t *, int));
+#endif /* SVR4 */
+static int pppurput __P((queue_t *, mblk_t *));
+static int pppuwput __P((queue_t *, mblk_t *));
+static int pppursrv __P((queue_t *));
+static int pppuwsrv __P((queue_t *));
+static int ppplrput __P((queue_t *, mblk_t *));
+static int ppplwput __P((queue_t *, mblk_t *));
+static int ppplrsrv __P((queue_t *));
+static int ppplwsrv __P((queue_t *));
+#ifndef NO_DLPI
+static void dlpi_request __P((queue_t *, mblk_t *, upperstr_t *));
+static void dlpi_error __P((queue_t *, upperstr_t *, int, int, int));
+static void dlpi_ok __P((queue_t *, int));
+#endif
+static int send_data __P((mblk_t *, upperstr_t *));
+static void new_ppa __P((queue_t *, mblk_t *));
+static void attach_ppa __P((queue_t *, mblk_t *));
+static void detach_ppa __P((queue_t *, mblk_t *));
+static void detach_lower __P((queue_t *, mblk_t *));
+static void debug_dump __P((queue_t *, mblk_t *));
+static upperstr_t *find_dest __P((upperstr_t *, int));
+#if defined(SOL2)
+static upperstr_t *find_promisc __P((upperstr_t *, int));
+static mblk_t *prepend_ether __P((upperstr_t *, mblk_t *, int));
+static mblk_t *prepend_udind __P((upperstr_t *, mblk_t *, int));
+static void promisc_sendup __P((upperstr_t *, mblk_t *, int, int));
+#endif /* defined(SOL2) */
+static int putctl2 __P((queue_t *, int, int, int));
+static int putctl4 __P((queue_t *, int, int, int));
+static int pass_packet __P((upperstr_t *ppa, mblk_t *mp, int outbound));
+#ifdef FILTER_PACKETS
+static int ip_hard_filter __P((upperstr_t *ppa, mblk_t *mp, int outbound));
+#endif /* FILTER_PACKETS */
+
+#define PPP_ID 0xb1a6
+static struct module_info ppp_info = {
+#ifdef PRIOQ
+ PPP_ID, "ppp", 0, 512, 512, 384
+#else
+ PPP_ID, "ppp", 0, 512, 512, 128
+#endif /* PRIOQ */
+};
+
+static struct qinit pppurint = {
+ pppurput, pppursrv, pppopen, pppclose, NULL, &ppp_info, NULL
+};
+
+static struct qinit pppuwint = {
+ pppuwput, pppuwsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+static struct qinit ppplrint = {
+ ppplrput, ppplrsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+static struct qinit ppplwint = {
+ ppplwput, ppplwsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+#ifdef LACHTCP
+extern struct ifstats *ifstats;
+int pppdevflag = 0;
+#endif
+
+struct streamtab pppinfo = {
+ &pppurint, &pppuwint,
+ &ppplrint, &ppplwint
+};
+
+int ppp_count;
+
+/*
+ * How we maintain statistics.
+ */
+#ifdef SOL2
+#define INCR_IPACKETS(ppa) \
+ if (ppa->kstats != 0) { \
+ KSTAT_NAMED_PTR(ppa->kstats)[0].value.ul++; \
+ }
+#define INCR_IERRORS(ppa) \
+ if (ppa->kstats != 0) { \
+ KSTAT_NAMED_PTR(ppa->kstats)[1].value.ul++; \
+ }
+#define INCR_OPACKETS(ppa) \
+ if (ppa->kstats != 0) { \
+ KSTAT_NAMED_PTR(ppa->kstats)[2].value.ul++; \
+ }
+#define INCR_OERRORS(ppa) \
+ if (ppa->kstats != 0) { \
+ KSTAT_NAMED_PTR(ppa->kstats)[3].value.ul++; \
+ }
+#endif
+
+#ifdef LACHTCP
+#define INCR_IPACKETS(ppa) ppa->ifstats.ifs_ipackets++;
+#define INCR_IERRORS(ppa) ppa->ifstats.ifs_ierrors++;
+#define INCR_OPACKETS(ppa) ppa->ifstats.ifs_opackets++;
+#define INCR_OERRORS(ppa) ppa->ifstats.ifs_oerrors++;
+#endif
+
+/*
+ * STREAMS driver entry points.
+ */
+static int
+#ifdef SVR4
+pppopen(q, devp, oflag, sflag, credp)
+ queue_t *q;
+ dev_t *devp;
+ int oflag, sflag;
+ cred_t *credp;
+#else
+pppopen(q, dev, oflag, sflag)
+ queue_t *q;
+ int dev; /* really dev_t */
+ int oflag, sflag;
+#endif
+{
+ upperstr_t *up;
+ upperstr_t **prevp;
+ minor_t mn;
+#ifdef PRIOQ
+ u_short *ptr;
+ u_char new_band;
+#endif /* PRIOQ */
+
+ if (q->q_ptr)
+ DRV_OPEN_OK(dev); /* device is already open */
+
+#ifdef PRIOQ
+ /* Calculate max_bband & def_band from definitions in prioq.h
+ This colud be done at some more approtiate time (less often)
+ but this way it works well so I'll just leave it here */
+
+ max_band = 1;
+ def_band = 0;
+ ptr = prioq_table;
+ while (*ptr) {
+ new_band = 1;
+ while (*ptr)
+ if (*ptr++ == IPPORT_DEFAULT) {
+ new_band = 0;
+ def_band = max_band;
+ }
+ max_band += new_band;
+ ptr++;
+ }
+ if (def_band)
+ def_band = max_band - def_band;
+ --max_band;
+#endif /* PRIOQ */
+
+ if (sflag == CLONEOPEN) {
+ mn = 0;
+ for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) {
+ if (up->mn != mn)
+ break;
+ ++mn;
+ }
+ } else {
+#ifdef SVR4
+ mn = getminor(*devp);
+#else
+ mn = minor(dev);
+#endif
+ for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) {
+ if (up->mn >= mn)
+ break;
+ }
+ if (up->mn == mn) {
+ /* this can't happen */
+ q->q_ptr = WR(q)->q_ptr = (caddr_t) up;
+ DRV_OPEN_OK(dev);
+ }
+ }
+
+ /*
+ * Construct a new minor node.
+ */
+ up = (upperstr_t *) ALLOC_SLEEP(sizeof(upperstr_t));
+ bzero((caddr_t) up, sizeof(upperstr_t));
+ if (up == 0) {
+ DPRINT("pppopen: out of kernel memory\n");
+ OPEN_ERROR(ENXIO);
+ }
+ up->nextmn = *prevp;
+ *prevp = up;
+ up->mn = mn;
+#ifdef SVR4
+ *devp = makedevice(getmajor(*devp), mn);
+#endif
+ up->q = q;
+ if (NOTSUSER() == 0)
+ up->flags |= US_PRIV;
+#ifndef NO_DLPI
+ up->state = DL_UNATTACHED;
+#endif
+#ifdef LACHTCP
+ up->ifflags = IFF_UP | IFF_POINTOPOINT;
+#endif
+ up->sap = -1;
+ up->last_sent = up->last_recv = time;
+ up->npmode = NPMODE_DROP;
+ q->q_ptr = (caddr_t) up;
+ WR(q)->q_ptr = (caddr_t) up;
+ noenable(WR(q));
+#ifdef SOL2
+ mutex_init(&up->stats_lock, NULL, MUTEX_DRIVER, NULL);
+#endif
+ ++ppp_count;
+
+ qprocson(q);
+ DRV_OPEN_OK(makedev(major(dev), mn));
+}
+
+static int
+#ifdef SVR4
+pppclose(q, flag, credp)
+ queue_t *q;
+ int flag;
+ cred_t *credp;
+#else
+pppclose(q, flag)
+ queue_t *q;
+ int flag;
+#endif
+{
+ upperstr_t *up, **upp;
+ upperstr_t *as, *asnext;
+ upperstr_t **prevp;
+
+ qprocsoff(q);
+
+ up = (upperstr_t *) q->q_ptr;
+ if (up == 0) {
+ DPRINT("pppclose: q_ptr = 0\n");
+ return 0;
+ }
+ if (up->flags & US_DBGLOG)
+ DPRINT2("ppp/%d: close, flags=%x\n", up->mn, up->flags);
+ if (up->flags & US_CONTROL) {
+#ifdef LACHTCP
+ struct ifstats *ifp, *pifp;
+#endif
+ if (up->lowerq != 0) {
+ /* Gack! the lower stream should have be unlinked earlier! */
+ DPRINT1("ppp%d: lower stream still connected on close?\n",
+ up->mn);
+ LOCK_LOWER_W;
+ up->lowerq->q_ptr = 0;
+ RD(up->lowerq)->q_ptr = 0;
+ up->lowerq = 0;
+ UNLOCK_LOWER;
+ }
+
+ /*
+ * This stream represents a PPA:
+ * For all streams attached to the PPA, clear their
+ * references to this PPA.
+ * Then remove this PPA from the list of PPAs.
+ */
+ for (as = up->next; as != 0; as = asnext) {
+ asnext = as->next;
+ as->next = 0;
+ as->ppa = 0;
+ if (as->flags & US_BLOCKED) {
+ as->flags &= ~US_BLOCKED;
+ flushq(WR(as->q), FLUSHDATA);
+ }
+ }
+ for (upp = &ppas; *upp != 0; upp = &(*upp)->nextppa)
+ if (*upp == up) {
+ *upp = up->nextppa;
+ break;
+ }
+#ifdef LACHTCP
+ /* Remove the statistics from the active list. */
+ for (ifp = ifstats, pifp = 0; ifp; ifp = ifp->ifs_next) {
+ if (ifp == &up->ifstats) {
+ if (pifp)
+ pifp->ifs_next = ifp->ifs_next;
+ else
+ ifstats = ifp->ifs_next;
+ break;
+ }
+ pifp = ifp;
+ }
+#endif
+ } else {
+ /*
+ * If this stream is attached to a PPA,
+ * remove it from the PPA's list.
+ */
+ if ((as = up->ppa) != 0) {
+ for (; as->next != 0; as = as->next)
+ if (as->next == up) {
+ as->next = up->next;
+ break;
+ }
+ }
+ }
+
+#ifdef SOL2
+ if (up->kstats)
+ kstat_delete(up->kstats);
+ mutex_destroy(&up->stats_lock);
+#endif
+
+ q->q_ptr = NULL;
+ WR(q)->q_ptr = NULL;
+
+ for (prevp = &minor_devs; *prevp != 0; prevp = &(*prevp)->nextmn) {
+ if (*prevp == up) {
+ *prevp = up->nextmn;
+ break;
+ }
+ }
+ FREE(up, sizeof(upperstr_t));
+ --ppp_count;
+
+ return 0;
+}
+
+/*
+ * A message from on high. We do one of three things:
+ * - qreply()
+ * - put the message on the lower write stream
+ * - queue it for our service routine
+ */
+static int
+pppuwput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us, *ppa, *nps;
+ struct iocblk *iop;
+ struct linkblk *lb;
+#ifdef LACHTCP
+ struct ifreq *ifr;
+ int i;
+#endif
+ queue_t *lq;
+ int error, n, sap;
+ mblk_t *mq;
+ struct ppp_idle *pip;
+#ifdef PRIOQ
+ queue_t *tlq;
+#endif /* PRIOQ */
+#ifdef NO_DLPI
+ upperstr_t *os;
+#endif
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("pppuwput: q_ptr = 0!\n");
+ return 0;
+ }
+ if (mp == 0) {
+ DPRINT1("pppuwput/%d: mp = 0!\n", us->mn);
+ return 0;
+ }
+ if (mp->b_datap == 0) {
+ DPRINT1("pppuwput/%d: mp->b_datap = 0!\n", us->mn);
+ return 0;
+ }
+ switch (mp->b_datap->db_type) {
+#ifndef NO_DLPI
+ case M_PCPROTO:
+ case M_PROTO:
+ dlpi_request(q, mp, us);
+ break;
+#endif /* NO_DLPI */
+
+ case M_DATA:
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: uwput M_DATA len=%d flags=%x\n",
+ us->mn, msgdsize(mp), us->flags);
+ if (us->ppa == 0 || msgdsize(mp) > us->ppa->mtu + PPP_HDRLEN
+#ifndef NO_DLPI
+ || (us->flags & US_CONTROL) == 0
+#endif /* NO_DLPI */
+ ) {
+ DPRINT1("pppuwput: junk data len=%d\n", msgdsize(mp));
+ freemsg(mp);
+ break;
+ }
+#ifdef NO_DLPI
+ if ((us->flags & US_CONTROL) == 0 && !pass_packet(us, mp, 1))
+ break;
+#endif
+ if (!send_data(mp, us))
+ putq(q, mp);
+ break;
+
+ case M_IOCTL:
+ iop = (struct iocblk *) mp->b_rptr;
+ error = EINVAL;
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: ioctl %x count=%d\n",
+ us->mn, iop->ioc_cmd, iop->ioc_count);
+ switch (iop->ioc_cmd) {
+#if defined(SOL2)
+ case DLIOCRAW: /* raw M_DATA mode */
+ us->flags |= US_RAWDATA;
+ error = 0;
+ break;
+#endif /* defined(SOL2) */
+ case I_LINK:
+ if ((us->flags & US_CONTROL) == 0 || us->lowerq != 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl I_LINK b_cont = 0!\n", us->mn);
+ break;
+ }
+ lb = (struct linkblk *) mp->b_cont->b_rptr;
+ lq = lb->l_qbot;
+ if (lq == 0) {
+ DPRINT1("pppuwput/%d: ioctl I_LINK l_qbot = 0!\n", us->mn);
+ break;
+ }
+ LOCK_LOWER_W;
+ us->lowerq = lq;
+ lq->q_ptr = (caddr_t) q;
+ RD(lq)->q_ptr = (caddr_t) us->q;
+ UNLOCK_LOWER;
+ iop->ioc_count = 0;
+ error = 0;
+ us->flags &= ~US_LASTMOD;
+ /* Unblock upper streams which now feed this lower stream. */
+ qenable(q);
+ /* Send useful information down to the modules which
+ are now linked below us. */
+ putctl2(lq, M_CTL, PPPCTL_UNIT, us->ppa_id);
+ putctl4(lq, M_CTL, PPPCTL_MRU, us->mru);
+ putctl4(lq, M_CTL, PPPCTL_MTU, us->mtu);
+#ifdef PRIOQ
+ /* Lower tty driver's queue hiwat/lowat from default 4096/128
+ to 256/128 since we don't want queueing of data on
+ output to physical device */
+
+ freezestr(lq);
+ for (tlq = lq; tlq->q_next != NULL; tlq = tlq->q_next)
+ ;
+ strqset(tlq, QHIWAT, 0, 256);
+ strqset(tlq, QLOWAT, 0, 128);
+ unfreezestr(lq);
+#endif /* PRIOQ */
+ break;
+
+ case I_UNLINK:
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl I_UNLINK b_cont = 0!\n", us->mn);
+ break;
+ }
+ lb = (struct linkblk *) mp->b_cont->b_rptr;
+#if DEBUG
+ if (us->lowerq != lb->l_qbot) {
+ DPRINT2("ppp unlink: lowerq=%x qbot=%x\n",
+ us->lowerq, lb->l_qbot);
+ break;
+ }
+#endif
+ iop->ioc_count = 0;
+ qwriter(q, mp, detach_lower, PERIM_OUTER);
+ error = -1;
+ break;
+
+ case PPPIO_NEWPPA:
+ if (us->flags & US_CONTROL)
+ break;
+ if ((us->flags & US_PRIV) == 0) {
+ error = EPERM;
+ break;
+ }
+ /* Arrange to return an int */
+ if ((mq = mp->b_cont) == 0
+ || mq->b_datap->db_lim - mq->b_rptr < sizeof(int)) {
+ mq = allocb(sizeof(int), BPRI_HI);
+ if (mq == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = mq;
+ mq->b_cont = 0;
+ }
+ iop->ioc_count = sizeof(int);
+ mq->b_wptr = mq->b_rptr + sizeof(int);
+ qwriter(q, mp, new_ppa, PERIM_OUTER);
+ error = -1;
+ break;
+
+ case PPPIO_ATTACH:
+ /* like dlpi_attach, for programs which can't write to
+ the stream (like pppstats) */
+ if (iop->ioc_count != sizeof(int) || us->ppa != 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_ATTACH b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
+ if (ppa->ppa_id == n)
+ break;
+ if (ppa == 0)
+ break;
+ us->ppa = ppa;
+ iop->ioc_count = 0;
+ qwriter(q, mp, attach_ppa, PERIM_OUTER);
+ error = -1;
+ break;
+
+#ifdef NO_DLPI
+ case PPPIO_BIND:
+ /* Attach to a given SAP. */
+ if (iop->ioc_count != sizeof(int) || us->ppa == 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_BIND b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ /* n must be a valid PPP network protocol number. */
+ if (n < 0x21 || n > 0x3fff || (n & 0x101) != 1)
+ break;
+ /* check that no other stream is bound to this sap already. */
+ for (os = us->ppa; os != 0; os = os->next)
+ if (os->sap == n)
+ break;
+ if (os != 0)
+ break;
+ us->sap = n;
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+#endif /* NO_DLPI */
+
+ case PPPIO_MRU:
+ if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_MRU b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ if (n <= 0 || n > PPP_MAXMRU)
+ break;
+ if (n < PPP_MRU)
+ n = PPP_MRU;
+ us->mru = n;
+ if (us->lowerq)
+ putctl4(us->lowerq, M_CTL, PPPCTL_MRU, n);
+ error = 0;
+ iop->ioc_count = 0;
+ break;
+
+ case PPPIO_MTU:
+ if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_MTU b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ if (n <= 0 || n > PPP_MAXMTU)
+ break;
+ us->mtu = n;
+#ifdef LACHTCP
+ /* The MTU reported in netstat, not used as IP max packet size! */
+ us->ifstats.ifs_mtu = n;
+#endif
+ if (us->lowerq)
+ putctl4(us->lowerq, M_CTL, PPPCTL_MTU, n);
+ error = 0;
+ iop->ioc_count = 0;
+ break;
+
+ case PPPIO_LASTMOD:
+ us->flags |= US_LASTMOD;
+ error = 0;
+ break;
+
+ case PPPIO_DEBUG:
+ if (iop->ioc_count != sizeof(int))
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_DEBUG b_cont = 0!\n", us->mn);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ if (n == PPPDBG_DUMP + PPPDBG_DRIVER) {
+ qwriter(q, NULL, debug_dump, PERIM_OUTER);
+ iop->ioc_count = 0;
+ error = -1;
+ } else if (n == PPPDBG_LOG + PPPDBG_DRIVER) {
+ DPRINT1("ppp/%d: debug log enabled\n", us->mn);
+ us->flags |= US_DBGLOG;
+ iop->ioc_count = 0;
+ error = 0;
+ } else {
+ if (us->ppa == 0 || us->ppa->lowerq == 0)
+ break;
+ putnext(us->ppa->lowerq, mp);
+ error = -1;
+ }
+ break;
+
+ case PPPIO_NPMODE:
+ if (iop->ioc_count != 2 * sizeof(int))
+ break;
+ if ((us->flags & US_CONTROL) == 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("pppuwput/%d: ioctl PPPIO_NPMODE b_cont = 0!\n", us->mn);
+ break;
+ }
+ sap = ((int *)mp->b_cont->b_rptr)[0];
+ for (nps = us->next; nps != 0; nps = nps->next) {
+ if (us->flags & US_DBGLOG)
+ DPRINT2("us = 0x%x, us->next->sap = 0x%x\n", nps, nps->sap);
+ if (nps->sap == sap)
+ break;
+ }
+ if (nps == 0) {
+ if (us->flags & US_DBGLOG)
+ DPRINT2("ppp/%d: no stream for sap %x\n", us->mn, sap);
+ break;
+ }
+ /* XXX possibly should use qwriter here */
+ nps->npmode = (enum NPmode) ((int *)mp->b_cont->b_rptr)[1];
+ if (nps->npmode != NPMODE_QUEUE && (nps->flags & US_BLOCKED) != 0)
+ qenable(WR(nps->q));
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+
+ case PPPIO_GIDLE:
+ if ((ppa = us->ppa) == 0)
+ break;
+ mq = allocb(sizeof(struct ppp_idle), BPRI_HI);
+ if (mq == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = mq;
+ mq->b_cont = 0;
+ pip = (struct ppp_idle *) mq->b_wptr;
+ pip->xmit_idle = time - ppa->last_sent;
+ pip->recv_idle = time - ppa->last_recv;
+ mq->b_wptr += sizeof(struct ppp_idle);
+ iop->ioc_count = sizeof(struct ppp_idle);
+ error = 0;
+ break;
+
+#ifdef LACHTCP
+ case SIOCSIFNAME:
+ /* Sent from IP down to us. Attach the ifstats structure. */
+ if (iop->ioc_count != sizeof(struct ifreq) || us->ppa == 0)
+ break;
+ ifr = (struct ifreq *)mp->b_cont->b_rptr;
+ /* Find the unit number in the interface name. */
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (ifr->ifr_name[i] == 0 ||
+ (ifr->ifr_name[i] >= '0' &&
+ ifr->ifr_name[i] <= '9'))
+ break;
+ else
+ us->ifname[i] = ifr->ifr_name[i];
+ }
+ us->ifname[i] = 0;
+
+ /* Convert the unit number to binary. */
+ for (n = 0; i < IFNAMSIZ; i++) {
+ if (ifr->ifr_name[i] == 0) {
+ break;
+ }
+ else {
+ n = n * 10 + ifr->ifr_name[i] - '0';
+ }
+ }
+
+ /* Verify the ppa. */
+ if (us->ppa->ppa_id != n)
+ break;
+ ppa = us->ppa;
+
+ /* Set up the netstat block. */
+ strncpy (ppa->ifname, us->ifname, IFNAMSIZ);
+
+ ppa->ifstats.ifs_name = ppa->ifname;
+ ppa->ifstats.ifs_unit = n;
+ ppa->ifstats.ifs_active = us->state != DL_UNBOUND;
+ ppa->ifstats.ifs_mtu = ppa->mtu;
+
+ /* Link in statistics used by netstat. */
+ ppa->ifstats.ifs_next = ifstats;
+ ifstats = &ppa->ifstats;
+
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+
+ case SIOCGIFFLAGS:
+ if (!(us->flags & US_CONTROL)) {
+ if (us->ppa)
+ us = us->ppa;
+ else
+ break;
+ }
+ ((struct iocblk_in *)iop)->ioc_ifflags = us->ifflags;
+ error = 0;
+ break;
+
+ case SIOCSIFFLAGS:
+ if (!(us->flags & US_CONTROL)) {
+ if (us->ppa)
+ us = us->ppa;
+ else
+ break;
+ }
+ us->ifflags = ((struct iocblk_in *)iop)->ioc_ifflags;
+ error = 0;
+ break;
+
+ case SIOCSIFADDR:
+ if (!(us->flags & US_CONTROL)) {
+ if (us->ppa)
+ us = us->ppa;
+ else
+ break;
+ }
+ us->ifflags |= IFF_RUNNING;
+ ((struct iocblk_in *)iop)->ioc_ifflags |= IFF_RUNNING;
+ error = 0;
+ break;
+
+ case SIOCSIFMTU:
+ /*
+ * Vanilla SVR4 systems don't handle SIOCSIFMTU, rather
+ * they take the MTU from the DL_INFO_ACK we sent in response
+ * to their DL_INFO_REQ. Fortunately, they will update the
+ * MTU if we send an unsolicited DL_INFO_ACK up.
+ */
+ if ((mq = allocb(sizeof(dl_info_req_t), BPRI_HI)) == 0)
+ break; /* should do bufcall */
+ ((union DL_primitives *)mq->b_rptr)->dl_primitive = DL_INFO_REQ;
+ mq->b_wptr = mq->b_rptr + sizeof(dl_info_req_t);
+ dlpi_request(q, mq, us);
+ error = 0;
+ break;
+
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFMETRIC:
+ error = 0;
+ break;
+#endif /* LACHTCP */
+
+ default:
+ if (us->ppa == 0 || us->ppa->lowerq == 0)
+ break;
+ us->ioc_id = iop->ioc_id;
+ error = -1;
+ switch (iop->ioc_cmd) {
+ case PPPIO_GETSTAT:
+ case PPPIO_GETCSTAT:
+ if (us->flags & US_LASTMOD) {
+ error = EINVAL;
+ break;
+ }
+ putnext(us->ppa->lowerq, mp);
+ break;
+ default:
+ if (us->flags & US_PRIV)
+ putnext(us->ppa->lowerq, mp);
+ else {
+ DPRINT1("ppp ioctl %x rejected\n", iop->ioc_cmd);
+ error = EPERM;
+ }
+ break;
+ }
+ break;
+ }
+
+ if (error > 0) {
+ iop->ioc_error = error;
+ mp->b_datap->db_type = M_IOCNAK;
+ qreply(q, mp);
+ } else if (error == 0) {
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+ }
+ break;
+
+ case M_FLUSH:
+ if (us->flags & US_DBGLOG)
+ DPRINT2("ppp/%d: flush %x\n", us->mn, *mp->b_rptr);
+ if (*mp->b_rptr & FLUSHW)
+ flushq(q, FLUSHDATA);
+ if (*mp->b_rptr & FLUSHR) {
+ *mp->b_rptr &= ~FLUSHW;
+ qreply(q, mp);
+ } else
+ freemsg(mp);
+ break;
+
+ default:
+ freemsg(mp);
+ break;
+ }
+ return 0;
+}
+
+#ifndef NO_DLPI
+static void
+dlpi_request(q, mp, us)
+ queue_t *q;
+ mblk_t *mp;
+ upperstr_t *us;
+{
+ union DL_primitives *d = (union DL_primitives *) mp->b_rptr;
+ int size = mp->b_wptr - mp->b_rptr;
+ mblk_t *reply, *np;
+ upperstr_t *ppa, *os;
+ int sap, len;
+ dl_info_ack_t *info;
+ dl_bind_ack_t *ackp;
+#if DL_CURRENT_VERSION >= 2
+ dl_phys_addr_ack_t *paddrack;
+ static struct ether_addr eaddr = {0};
+#endif
+
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: dlpi prim %x len=%d\n", us->mn,
+ d->dl_primitive, size);
+ switch (d->dl_primitive) {
+ case DL_INFO_REQ:
+ if (size < sizeof(dl_info_req_t))
+ goto badprim;
+ if ((reply = allocb(sizeof(dl_info_ack_t), BPRI_HI)) == 0)
+ break; /* should do bufcall */
+ reply->b_datap->db_type = M_PCPROTO;
+ info = (dl_info_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_info_ack_t);
+ bzero((caddr_t) info, sizeof(dl_info_ack_t));
+ info->dl_primitive = DL_INFO_ACK;
+ info->dl_max_sdu = us->ppa? us->ppa->mtu: PPP_MAXMTU;
+ info->dl_min_sdu = 1;
+ info->dl_addr_length = sizeof(uint);
+ info->dl_mac_type = DL_ETHER; /* a bigger lie */
+ info->dl_current_state = us->state;
+ info->dl_service_mode = DL_CLDLS;
+ info->dl_provider_style = DL_STYLE2;
+#if DL_CURRENT_VERSION >= 2
+ info->dl_sap_length = sizeof(uint);
+ info->dl_version = DL_CURRENT_VERSION;
+#endif
+ qreply(q, reply);
+ break;
+
+ case DL_ATTACH_REQ:
+ if (size < sizeof(dl_attach_req_t))
+ goto badprim;
+ if (us->state != DL_UNATTACHED || us->ppa != 0) {
+ dlpi_error(q, us, DL_ATTACH_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+ for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
+ if (ppa->ppa_id == d->attach_req.dl_ppa)
+ break;
+ if (ppa == 0) {
+ dlpi_error(q, us, DL_ATTACH_REQ, DL_BADPPA, 0);
+ break;
+ }
+ us->ppa = ppa;
+ qwriter(q, mp, attach_ppa, PERIM_OUTER);
+ return;
+
+ case DL_DETACH_REQ:
+ if (size < sizeof(dl_detach_req_t))
+ goto badprim;
+ if (us->state != DL_UNBOUND || us->ppa == 0) {
+ dlpi_error(q, us, DL_DETACH_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+ qwriter(q, mp, detach_ppa, PERIM_OUTER);
+ return;
+
+ case DL_BIND_REQ:
+ if (size < sizeof(dl_bind_req_t))
+ goto badprim;
+ if (us->state != DL_UNBOUND || us->ppa == 0) {
+ dlpi_error(q, us, DL_BIND_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+#if 0
+ /* apparently this test fails (unnecessarily?) on some systems */
+ if (d->bind_req.dl_service_mode != DL_CLDLS) {
+ dlpi_error(q, us, DL_BIND_REQ, DL_UNSUPPORTED, 0);
+ break;
+ }
+#endif
+
+ /* saps must be valid PPP network protocol numbers,
+ except that we accept ETHERTYPE_IP in place of PPP_IP. */
+ sap = d->bind_req.dl_sap;
+ us->req_sap = sap;
+
+#if defined(SOL2)
+ if (us->flags & US_DBGLOG)
+ DPRINT2("DL_BIND_REQ: ip gives sap = 0x%x, us = 0x%x", sap, us);
+
+ if (sap == ETHERTYPE_IP) /* normal IFF_IPV4 */
+ sap = PPP_IP;
+ else if (sap == ETHERTYPE_IPV6) /* when IFF_IPV6 is set */
+ sap = PPP_IPV6;
+ else if (sap == ETHERTYPE_ALLSAP) /* snoop gives sap of 0 */
+ sap = PPP_ALLSAP;
+ else {
+ DPRINT2("DL_BIND_REQ: unrecognized sap = 0x%x, us = 0x%x", sap, us);
+ dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0);
+ break;
+ }
+#else
+ if (sap == ETHERTYPE_IP)
+ sap = PPP_IP;
+ if (sap < 0x21 || sap > 0x3fff || (sap & 0x101) != 1) {
+ dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0);
+ break;
+ }
+#endif /* defined(SOL2) */
+
+ /* check that no other stream is bound to this sap already. */
+ for (os = us->ppa; os != 0; os = os->next)
+ if (os->sap == sap)
+ break;
+ if (os != 0) {
+ dlpi_error(q, us, DL_BIND_REQ, DL_NOADDR, 0);
+ break;
+ }
+
+ us->sap = sap;
+ us->state = DL_IDLE;
+
+ if ((reply = allocb(sizeof(dl_bind_ack_t) + sizeof(uint),
+ BPRI_HI)) == 0)
+ break; /* should do bufcall */
+ ackp = (dl_bind_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_bind_ack_t) + sizeof(uint);
+ reply->b_datap->db_type = M_PCPROTO;
+ bzero((caddr_t) ackp, sizeof(dl_bind_ack_t));
+ ackp->dl_primitive = DL_BIND_ACK;
+ ackp->dl_sap = sap;
+ ackp->dl_addr_length = sizeof(uint);
+ ackp->dl_addr_offset = sizeof(dl_bind_ack_t);
+ *(uint *)(ackp+1) = sap;
+ qreply(q, reply);
+ break;
+
+ case DL_UNBIND_REQ:
+ if (size < sizeof(dl_unbind_req_t))
+ goto badprim;
+ if (us->state != DL_IDLE) {
+ dlpi_error(q, us, DL_UNBIND_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+ us->sap = -1;
+ us->state = DL_UNBOUND;
+#ifdef LACHTCP
+ us->ppa->ifstats.ifs_active = 0;
+#endif
+ dlpi_ok(q, DL_UNBIND_REQ);
+ break;
+
+ case DL_UNITDATA_REQ:
+ if (size < sizeof(dl_unitdata_req_t))
+ goto badprim;
+ if (us->state != DL_IDLE) {
+ dlpi_error(q, us, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
+ break;
+ }
+ if ((ppa = us->ppa) == 0) {
+ cmn_err(CE_CONT, "ppp: in state dl_idle but ppa == 0?\n");
+ break;
+ }
+ len = mp->b_cont == 0? 0: msgdsize(mp->b_cont);
+ if (len > ppa->mtu) {
+ DPRINT2("dlpi data too large (%d > %d)\n", len, ppa->mtu);
+ break;
+ }
+
+#if defined(SOL2)
+ /*
+ * Should there be any promiscuous stream(s), send the data
+ * up for each promiscuous stream that we recognize.
+ */
+ if (mp->b_cont)
+ promisc_sendup(ppa, mp->b_cont, us->sap, 0);
+#endif /* defined(SOL2) */
+
+ mp->b_band = 0;
+#ifdef PRIOQ
+ /* Extract s_port & d_port from IP-packet, the code is a bit
+ dirty here, but so am I, too... */
+ if (mp->b_datap->db_type == M_PROTO && us->sap == PPP_IP
+ && mp->b_cont != 0) {
+ u_char *bb, *tlh;
+ int iphlen, len;
+ u_short *ptr;
+ u_char band_unset, cur_band, syn;
+ u_short s_port, d_port;
+
+ bb = mp->b_cont->b_rptr; /* bb points to IP-header*/
+ len = mp->b_cont->b_wptr - mp->b_cont->b_rptr;
+ syn = 0;
+ s_port = IPPORT_DEFAULT;
+ d_port = IPPORT_DEFAULT;
+ if (len >= 20) { /* 20 = minimum length of IP header */
+ iphlen = (bb[0] & 0x0f) * 4;
+ tlh = bb + iphlen;
+ len -= iphlen;
+ switch (bb[9]) {
+ case IPPROTO_TCP:
+ if (len >= 20) { /* min length of TCP header */
+ s_port = (tlh[0] << 8) + tlh[1];
+ d_port = (tlh[2] << 8) + tlh[3];
+ syn = tlh[13] & 0x02;
+ }
+ break;
+ case IPPROTO_UDP:
+ if (len >= 8) { /* min length of UDP header */
+ s_port = (tlh[0] << 8) + tlh[1];
+ d_port = (tlh[2] << 8) + tlh[3];
+ }
+ break;
+ }
+ }
+
+ /*
+ * Now calculate b_band for this packet from the
+ * port-priority table.
+ */
+ ptr = prioq_table;
+ cur_band = max_band;
+ band_unset = 1;
+ while (*ptr) {
+ while (*ptr && band_unset)
+ if (s_port == *ptr || d_port == *ptr++) {
+ mp->b_band = cur_band;
+ band_unset = 0;
+ break;
+ }
+ ptr++;
+ cur_band--;
+ }
+ if (band_unset)
+ mp->b_band = def_band;
+ /* It may be usable to urge SYN packets a bit */
+ if (syn)
+ mp->b_band++;
+ }
+#endif /* PRIOQ */
+ /* this assumes PPP_HDRLEN <= sizeof(dl_unitdata_req_t) */
+ if (mp->b_datap->db_ref > 1) {
+ np = allocb(PPP_HDRLEN, BPRI_HI);
+ if (np == 0)
+ break; /* gak! */
+ np->b_cont = mp->b_cont;
+ mp->b_cont = 0;
+ freeb(mp);
+ mp = np;
+ } else
+ mp->b_datap->db_type = M_DATA;
+ /* XXX should use dl_dest_addr_offset/length here,
+ but we would have to translate ETHERTYPE_IP -> PPP_IP */
+ mp->b_wptr = mp->b_rptr + PPP_HDRLEN;
+ mp->b_rptr[0] = PPP_ALLSTATIONS;
+ mp->b_rptr[1] = PPP_UI;
+ mp->b_rptr[2] = us->sap >> 8;
+ mp->b_rptr[3] = us->sap;
+ if (pass_packet(us, mp, 1)) {
+ if (!send_data(mp, us))
+ putq(q, mp);
+ }
+ return;
+
+#if DL_CURRENT_VERSION >= 2
+ case DL_PHYS_ADDR_REQ:
+ if (size < sizeof(dl_phys_addr_req_t))
+ goto badprim;
+
+ /*
+ * Don't check state because ifconfig sends this one down too
+ */
+
+ if ((reply = allocb(sizeof(dl_phys_addr_ack_t)+ETHERADDRL,
+ BPRI_HI)) == 0)
+ break; /* should do bufcall */
+ reply->b_datap->db_type = M_PCPROTO;
+ paddrack = (dl_phys_addr_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_phys_addr_ack_t);
+ bzero((caddr_t) paddrack, sizeof(dl_phys_addr_ack_t)+ETHERADDRL);
+ paddrack->dl_primitive = DL_PHYS_ADDR_ACK;
+ paddrack->dl_addr_length = ETHERADDRL;
+ paddrack->dl_addr_offset = sizeof(dl_phys_addr_ack_t);
+ bcopy(&eaddr, reply->b_wptr, ETHERADDRL);
+ reply->b_wptr += ETHERADDRL;
+ qreply(q, reply);
+ break;
+
+#if defined(SOL2)
+ case DL_PROMISCON_REQ:
+ if (size < sizeof(dl_promiscon_req_t))
+ goto badprim;
+ us->flags |= US_PROMISC;
+ dlpi_ok(q, DL_PROMISCON_REQ);
+ break;
+
+ case DL_PROMISCOFF_REQ:
+ if (size < sizeof(dl_promiscoff_req_t))
+ goto badprim;
+ us->flags &= ~US_PROMISC;
+ dlpi_ok(q, DL_PROMISCOFF_REQ);
+ break;
+#else
+ case DL_PROMISCON_REQ: /* fall thru */
+ case DL_PROMISCOFF_REQ: /* fall thru */
+#endif /* defined(SOL2) */
+#endif /* DL_CURRENT_VERSION >= 2 */
+
+#if DL_CURRENT_VERSION >= 2
+ case DL_SET_PHYS_ADDR_REQ:
+ case DL_SUBS_BIND_REQ:
+ case DL_SUBS_UNBIND_REQ:
+ case DL_ENABMULTI_REQ:
+ case DL_DISABMULTI_REQ:
+ case DL_XID_REQ:
+ case DL_TEST_REQ:
+ case DL_REPLY_UPDATE_REQ:
+ case DL_REPLY_REQ:
+ case DL_DATA_ACK_REQ:
+#endif
+ case DL_CONNECT_REQ:
+ case DL_TOKEN_REQ:
+ dlpi_error(q, us, d->dl_primitive, DL_NOTSUPPORTED, 0);
+ break;
+
+ case DL_CONNECT_RES:
+ case DL_DISCONNECT_REQ:
+ case DL_RESET_REQ:
+ case DL_RESET_RES:
+ dlpi_error(q, us, d->dl_primitive, DL_OUTSTATE, 0);
+ break;
+
+ case DL_UDQOS_REQ:
+ dlpi_error(q, us, d->dl_primitive, DL_BADQOSTYPE, 0);
+ break;
+
+#if DL_CURRENT_VERSION >= 2
+ case DL_TEST_RES:
+ case DL_XID_RES:
+ break;
+#endif
+
+ default:
+ cmn_err(CE_CONT, "ppp: unknown dlpi prim 0x%x\n", d->dl_primitive);
+ /* fall through */
+ badprim:
+ dlpi_error(q, us, d->dl_primitive, DL_BADPRIM, 0);
+ break;
+ }
+ freemsg(mp);
+}
+
+static void
+dlpi_error(q, us, prim, err, uerr)
+ queue_t *q;
+ upperstr_t *us;
+ int prim, err, uerr;
+{
+ mblk_t *reply;
+ dl_error_ack_t *errp;
+
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: dlpi error, prim=%x, err=%x\n", us->mn, prim, err);
+ reply = allocb(sizeof(dl_error_ack_t), BPRI_HI);
+ if (reply == 0)
+ return; /* XXX should do bufcall */
+ reply->b_datap->db_type = M_PCPROTO;
+ errp = (dl_error_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_error_ack_t);
+ errp->dl_primitive = DL_ERROR_ACK;
+ errp->dl_error_primitive = prim;
+ errp->dl_errno = err;
+ errp->dl_unix_errno = uerr;
+ qreply(q, reply);
+}
+
+static void
+dlpi_ok(q, prim)
+ queue_t *q;
+ int prim;
+{
+ mblk_t *reply;
+ dl_ok_ack_t *okp;
+
+ reply = allocb(sizeof(dl_ok_ack_t), BPRI_HI);
+ if (reply == 0)
+ return; /* XXX should do bufcall */
+ reply->b_datap->db_type = M_PCPROTO;
+ okp = (dl_ok_ack_t *) reply->b_wptr;
+ reply->b_wptr += sizeof(dl_ok_ack_t);
+ okp->dl_primitive = DL_OK_ACK;
+ okp->dl_correct_primitive = prim;
+ qreply(q, reply);
+}
+#endif /* NO_DLPI */
+
+static int
+pass_packet(us, mp, outbound)
+ upperstr_t *us;
+ mblk_t *mp;
+ int outbound;
+{
+ int pass;
+ upperstr_t *ppa;
+
+ if ((ppa = us->ppa) == 0) {
+ freemsg(mp);
+ return 0;
+ }
+
+#ifdef FILTER_PACKETS
+ pass = ip_hard_filter(us, mp, outbound);
+#else
+ /*
+ * Here is where we might, in future, decide whether to pass
+ * or drop the packet, and whether it counts as link activity.
+ */
+ pass = 1;
+#endif /* FILTER_PACKETS */
+
+ if (pass < 0) {
+ /* pass only if link already up, and don't update time */
+ if (ppa->lowerq == 0) {
+ freemsg(mp);
+ return 0;
+ }
+ pass = 1;
+ } else if (pass) {
+ if (outbound)
+ ppa->last_sent = time;
+ else
+ ppa->last_recv = time;
+ }
+
+ return pass;
+}
+
+/*
+ * We have some data to send down to the lower stream (or up the
+ * control stream, if we don't have a lower stream attached).
+ * Returns 1 if the message was dealt with, 0 if it wasn't able
+ * to be sent on and should therefore be queued up.
+ */
+static int
+send_data(mp, us)
+ mblk_t *mp;
+ upperstr_t *us;
+{
+ upperstr_t *ppa;
+
+ if ((us->flags & US_BLOCKED) || us->npmode == NPMODE_QUEUE)
+ return 0;
+ ppa = us->ppa;
+ if (ppa == 0 || us->npmode == NPMODE_DROP || us->npmode == NPMODE_ERROR) {
+ if (us->flags & US_DBGLOG)
+ DPRINT2("ppp/%d: dropping pkt (npmode=%d)\n", us->mn, us->npmode);
+ freemsg(mp);
+ return 1;
+ }
+ if (ppa->lowerq == 0) {
+ /* try to send it up the control stream */
+ if (bcanputnext(ppa->q, mp->b_band)) {
+ /*
+ * The message seems to get corrupted for some reason if
+ * we just send the message up as it is, so we send a copy.
+ */
+ mblk_t *np = copymsg(mp);
+ freemsg(mp);
+ if (np != 0)
+ putnext(ppa->q, np);
+ return 1;
+ }
+ } else {
+ if (bcanputnext(ppa->lowerq, mp->b_band)) {
+ MT_ENTER(&ppa->stats_lock);
+ ppa->stats.ppp_opackets++;
+ ppa->stats.ppp_obytes += msgdsize(mp);
+#ifdef INCR_OPACKETS
+ INCR_OPACKETS(ppa);
+#endif
+ MT_EXIT(&ppa->stats_lock);
+ /*
+ * The lower queue is only ever detached while holding an
+ * exclusive lock on the whole driver. So we can be confident
+ * that the lower queue is still there.
+ */
+ putnext(ppa->lowerq, mp);
+ return 1;
+ }
+ }
+ us->flags |= US_BLOCKED;
+ return 0;
+}
+
+/*
+ * Allocate a new PPA id and link this stream into the list of PPAs.
+ * This procedure is called with an exclusive lock on all queues in
+ * this driver.
+ */
+static void
+new_ppa(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us, *up, **usp;
+ int ppa_id;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("new_ppa: q_ptr = 0!\n");
+ return;
+ }
+
+ usp = &ppas;
+ ppa_id = 0;
+ while ((up = *usp) != 0 && ppa_id == up->ppa_id) {
+ ++ppa_id;
+ usp = &up->nextppa;
+ }
+ us->ppa_id = ppa_id;
+ us->ppa = us;
+ us->next = 0;
+ us->nextppa = *usp;
+ *usp = us;
+ us->flags |= US_CONTROL;
+ us->npmode = NPMODE_PASS;
+
+ us->mtu = PPP_MTU;
+ us->mru = PPP_MRU;
+
+#ifdef SOL2
+ /*
+ * Create a kstats record for our statistics, so netstat -i works.
+ */
+ if (us->kstats == 0) {
+ char unit[32];
+
+ sprintf(unit, "ppp%d", us->ppa->ppa_id);
+ us->kstats = kstat_create("ppp", us->ppa->ppa_id, unit,
+ "net", KSTAT_TYPE_NAMED, 4, 0);
+ if (us->kstats != 0) {
+ kstat_named_t *kn = KSTAT_NAMED_PTR(us->kstats);
+
+ strcpy(kn[0].name, "ipackets");
+ kn[0].data_type = KSTAT_DATA_ULONG;
+ strcpy(kn[1].name, "ierrors");
+ kn[1].data_type = KSTAT_DATA_ULONG;
+ strcpy(kn[2].name, "opackets");
+ kn[2].data_type = KSTAT_DATA_ULONG;
+ strcpy(kn[3].name, "oerrors");
+ kn[3].data_type = KSTAT_DATA_ULONG;
+ kstat_install(us->kstats);
+ }
+ }
+#endif /* SOL2 */
+
+ *(int *)mp->b_cont->b_rptr = ppa_id;
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+}
+
+static void
+attach_ppa(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us, *t;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("attach_ppa: q_ptr = 0!\n");
+ return;
+ }
+
+#ifndef NO_DLPI
+ us->state = DL_UNBOUND;
+#endif
+ for (t = us->ppa; t->next != 0; t = t->next)
+ ;
+ t->next = us;
+ us->next = 0;
+ if (mp->b_datap->db_type == M_IOCTL) {
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+ } else {
+#ifndef NO_DLPI
+ dlpi_ok(q, DL_ATTACH_REQ);
+#endif
+ }
+}
+
+static void
+detach_ppa(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us, *t;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("detach_ppa: q_ptr = 0!\n");
+ return;
+ }
+
+ for (t = us->ppa; t->next != 0; t = t->next)
+ if (t->next == us) {
+ t->next = us->next;
+ break;
+ }
+ us->next = 0;
+ us->ppa = 0;
+#ifndef NO_DLPI
+ us->state = DL_UNATTACHED;
+ dlpi_ok(q, DL_DETACH_REQ);
+#endif
+}
+
+/*
+ * We call this with qwriter in order to give the upper queue procedures
+ * the guarantee that the lower queue is not going to go away while
+ * they are executing.
+ */
+static void
+detach_lower(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("detach_lower: q_ptr = 0!\n");
+ return;
+ }
+
+ LOCK_LOWER_W;
+ us->lowerq->q_ptr = 0;
+ RD(us->lowerq)->q_ptr = 0;
+ us->lowerq = 0;
+ UNLOCK_LOWER;
+
+ /* Unblock streams which now feed back up the control stream. */
+ qenable(us->q);
+
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+}
+
+static int
+pppuwsrv(q)
+ queue_t *q;
+{
+ upperstr_t *us, *as;
+ mblk_t *mp;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("pppuwsrv: q_ptr = 0!\n");
+ return 0;
+ }
+
+ /*
+ * If this is a control stream, then this service procedure
+ * probably got enabled because of flow control in the lower
+ * stream being enabled (or because of the lower stream going
+ * away). Therefore we enable the service procedure of all
+ * attached upper streams.
+ */
+ if (us->flags & US_CONTROL) {
+ for (as = us->next; as != 0; as = as->next)
+ qenable(WR(as->q));
+ }
+
+ /* Try to send on any data queued here. */
+ us->flags &= ~US_BLOCKED;
+ while ((mp = getq(q)) != 0) {
+ if (!send_data(mp, us)) {
+ putbq(q, mp);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* should never get called... */
+static int
+ppplwput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ putnext(q, mp);
+ return 0;
+}
+
+static int
+ppplwsrv(q)
+ queue_t *q;
+{
+ queue_t *uq;
+
+ /*
+ * Flow control has back-enabled this stream:
+ * enable the upper write service procedure for
+ * the upper control stream for this lower stream.
+ */
+ LOCK_LOWER_R;
+ uq = (queue_t *) q->q_ptr;
+ if (uq != 0)
+ qenable(uq);
+ UNLOCK_LOWER;
+ return 0;
+}
+
+/*
+ * This should only get called for control streams.
+ */
+static int
+pppurput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *ppa, *us;
+ int proto, len;
+ struct iocblk *iop;
+
+ ppa = (upperstr_t *) q->q_ptr;
+ if (ppa == 0) {
+ DPRINT("pppurput: q_ptr = 0!\n");
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+ case M_CTL:
+ MT_ENTER(&ppa->stats_lock);
+ switch (*mp->b_rptr) {
+ case PPPCTL_IERROR:
+#ifdef INCR_IERRORS
+ INCR_IERRORS(ppa);
+#endif
+ ppa->stats.ppp_ierrors++;
+ break;
+ case PPPCTL_OERROR:
+#ifdef INCR_OERRORS
+ INCR_OERRORS(ppa);
+#endif
+ ppa->stats.ppp_oerrors++;
+ break;
+ }
+ MT_EXIT(&ppa->stats_lock);
+ freemsg(mp);
+ break;
+
+ case M_IOCACK:
+ case M_IOCNAK:
+ /*
+ * Attempt to match up the response with the stream
+ * that the request came from.
+ */
+ iop = (struct iocblk *) mp->b_rptr;
+ for (us = ppa; us != 0; us = us->next)
+ if (us->ioc_id == iop->ioc_id)
+ break;
+ if (us == 0)
+ freemsg(mp);
+ else
+ putnext(us->q, mp);
+ break;
+
+ case M_HANGUP:
+ /*
+ * The serial device has hung up. We don't want to send
+ * the M_HANGUP message up to pppd because that will stop
+ * us from using the control stream any more. Instead we
+ * send a zero-length message as an end-of-file indication.
+ */
+ freemsg(mp);
+ mp = allocb(1, BPRI_HI);
+ if (mp == 0) {
+ DPRINT1("ppp/%d: couldn't allocate eof message!\n", ppa->mn);
+ break;
+ }
+ putnext(ppa->q, mp);
+ break;
+
+ default:
+ if (mp->b_datap->db_type == M_DATA) {
+ len = msgdsize(mp);
+ if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN) {
+ PULLUP(mp, PPP_HDRLEN);
+ if (mp == 0) {
+ DPRINT1("ppp_urput: msgpullup failed (len=%d)\n", len);
+ break;
+ }
+ }
+ MT_ENTER(&ppa->stats_lock);
+ ppa->stats.ppp_ipackets++;
+ ppa->stats.ppp_ibytes += len;
+#ifdef INCR_IPACKETS
+ INCR_IPACKETS(ppa);
+#endif
+ MT_EXIT(&ppa->stats_lock);
+
+ proto = PPP_PROTOCOL(mp->b_rptr);
+
+#if defined(SOL2)
+ /*
+ * Should there be any promiscuous stream(s), send the data
+ * up for each promiscuous stream that we recognize.
+ */
+ promisc_sendup(ppa, mp, proto, 1);
+#endif /* defined(SOL2) */
+
+ if (proto < 0x8000 && (us = find_dest(ppa, proto)) != 0) {
+ /*
+ * A data packet for some network protocol.
+ * Queue it on the upper stream for that protocol.
+ * XXX could we just putnext it? (would require thought)
+ * The rblocked flag is there to ensure that we keep
+ * messages in order for each network protocol.
+ */
+ if (!pass_packet(us, mp, 0))
+ break;
+ if (!us->rblocked && !canput(us->q))
+ us->rblocked = 1;
+ if (!us->rblocked)
+ putq(us->q, mp);
+ else
+ putq(q, mp);
+ break;
+ }
+ }
+ /*
+ * A control frame, a frame for an unknown protocol,
+ * or some other message type.
+ * Send it up to pppd via the control stream.
+ */
+ if (queclass(mp) == QPCTL || canputnext(ppa->q))
+ putnext(ppa->q, mp);
+ else
+ putq(q, mp);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+pppursrv(q)
+ queue_t *q;
+{
+ upperstr_t *us, *as;
+ mblk_t *mp, *hdr;
+#ifndef NO_DLPI
+ dl_unitdata_ind_t *ud;
+#endif
+ int proto;
+
+ us = (upperstr_t *) q->q_ptr;
+ if (us == 0) {
+ DPRINT("pppursrv: q_ptr = 0!\n");
+ return 0;
+ }
+
+ if (us->flags & US_CONTROL) {
+ /*
+ * A control stream.
+ * If there is no lower queue attached, run the write service
+ * routines of other upper streams attached to this PPA.
+ */
+ if (us->lowerq == 0) {
+ as = us;
+ do {
+ if (as->flags & US_BLOCKED)
+ qenable(WR(as->q));
+ as = as->next;
+ } while (as != 0);
+ }
+
+ /*
+ * Messages get queued on this stream's read queue if they
+ * can't be queued on the read queue of the attached stream
+ * that they are destined for. This is for flow control -
+ * when this queue fills up, the lower read put procedure will
+ * queue messages there and the flow control will propagate
+ * down from there.
+ */
+ while ((mp = getq(q)) != 0) {
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ if (proto < 0x8000 && (as = find_dest(us, proto)) != 0) {
+ if (!canput(as->q))
+ break;
+ putq(as->q, mp);
+ } else {
+ if (!canputnext(q))
+ break;
+ putnext(q, mp);
+ }
+ }
+ if (mp) {
+ putbq(q, mp);
+ } else {
+ /* can now put stuff directly on network protocol streams again */
+ for (as = us->next; as != 0; as = as->next)
+ as->rblocked = 0;
+ }
+
+ /*
+ * If this stream has a lower stream attached,
+ * enable the read queue's service routine.
+ * XXX we should really only do this if the queue length
+ * has dropped below the low-water mark.
+ */
+ if (us->lowerq != 0)
+ qenable(RD(us->lowerq));
+
+ } else {
+ /*
+ * A network protocol stream. Put a DLPI header on each
+ * packet and send it on.
+ * (Actually, it seems that the IP module will happily
+ * accept M_DATA messages without the DL_UNITDATA_IND header.)
+ */
+ while ((mp = getq(q)) != 0) {
+ if (!canputnext(q)) {
+ putbq(q, mp);
+ break;
+ }
+#ifndef NO_DLPI
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ mp->b_rptr += PPP_HDRLEN;
+ hdr = allocb(sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint),
+ BPRI_MED);
+ if (hdr == 0) {
+ /* XXX should put it back and use bufcall */
+ freemsg(mp);
+ continue;
+ }
+ hdr->b_datap->db_type = M_PROTO;
+ ud = (dl_unitdata_ind_t *) hdr->b_wptr;
+ hdr->b_wptr += sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint);
+ hdr->b_cont = mp;
+ ud->dl_primitive = DL_UNITDATA_IND;
+ ud->dl_dest_addr_length = sizeof(uint);
+ ud->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+ ud->dl_src_addr_length = sizeof(uint);
+ ud->dl_src_addr_offset = ud->dl_dest_addr_offset + sizeof(uint);
+#if DL_CURRENT_VERSION >= 2
+ ud->dl_group_address = 0;
+#endif
+ /* Send the DLPI client the data with the SAP they requested,
+ (e.g. ETHERTYPE_IP) rather than the PPP protocol number
+ (e.g. PPP_IP) */
+ ((uint *)(ud + 1))[0] = us->req_sap; /* dest SAP */
+ ((uint *)(ud + 1))[1] = us->req_sap; /* src SAP */
+ putnext(q, hdr);
+#else /* NO_DLPI */
+ putnext(q, mp);
+#endif /* NO_DLPI */
+ }
+ /*
+ * Now that we have consumed some packets from this queue,
+ * enable the control stream's read service routine so that we
+ * can process any packets for us that might have got queued
+ * there for flow control reasons.
+ */
+ if (us->ppa)
+ qenable(us->ppa->q);
+ }
+
+ return 0;
+}
+
+static upperstr_t *
+find_dest(ppa, proto)
+ upperstr_t *ppa;
+ int proto;
+{
+ upperstr_t *us;
+
+ for (us = ppa->next; us != 0; us = us->next)
+ if (proto == us->sap)
+ break;
+ return us;
+}
+
+#if defined (SOL2)
+/*
+ * Test upstream promiscuous conditions. As of now, only pass IPv4 and
+ * Ipv6 packets upstream (let PPP packets be decoded elsewhere).
+ */
+static upperstr_t *
+find_promisc(us, proto)
+ upperstr_t *us;
+ int proto;
+{
+
+ if ((proto != PPP_IP) && (proto != PPP_IPV6))
+ return (upperstr_t *)0;
+
+ for ( ; us; us = us->next) {
+ if ((us->flags & US_PROMISC) && (us->state == DL_IDLE))
+ return us;
+ }
+
+ return (upperstr_t *)0;
+}
+
+/*
+ * Prepend an empty Ethernet header to msg for snoop, et al.
+ */
+static mblk_t *
+prepend_ether(us, mp, proto)
+ upperstr_t *us;
+ mblk_t *mp;
+ int proto;
+{
+ mblk_t *eh;
+ int type;
+
+ if ((eh = allocb(sizeof(struct ether_header), BPRI_HI)) == 0) {
+ freemsg(mp);
+ return (mblk_t *)0;
+ }
+
+ if (proto == PPP_IP)
+ type = ETHERTYPE_IP;
+ else if (proto == PPP_IPV6)
+ type = ETHERTYPE_IPV6;
+ else
+ type = proto; /* What else? Let decoder decide */
+
+ eh->b_wptr += sizeof(struct ether_header);
+ bzero((caddr_t)eh->b_rptr, sizeof(struct ether_header));
+ ((struct ether_header *)eh->b_rptr)->ether_type = htons((short)type);
+ eh->b_cont = mp;
+ return (eh);
+}
+
+/*
+ * Prepend DL_UNITDATA_IND mblk to msg
+ */
+static mblk_t *
+prepend_udind(us, mp, proto)
+ upperstr_t *us;
+ mblk_t *mp;
+ int proto;
+{
+ dl_unitdata_ind_t *dlu;
+ mblk_t *dh;
+ size_t size;
+
+ size = sizeof(dl_unitdata_ind_t);
+ if ((dh = allocb(size, BPRI_MED)) == 0) {
+ freemsg(mp);
+ return (mblk_t *)0;
+ }
+
+ dh->b_datap->db_type = M_PROTO;
+ dh->b_wptr = dh->b_datap->db_lim;
+ dh->b_rptr = dh->b_wptr - size;
+
+ dlu = (dl_unitdata_ind_t *)dh->b_rptr;
+ dlu->dl_primitive = DL_UNITDATA_IND;
+ dlu->dl_dest_addr_length = 0;
+ dlu->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+ dlu->dl_src_addr_length = 0;
+ dlu->dl_src_addr_offset = sizeof(dl_unitdata_ind_t);
+ dlu->dl_group_address = 0;
+
+ dh->b_cont = mp;
+ return (dh);
+}
+
+/*
+ * For any recognized promiscuous streams, send data upstream
+ */
+static void
+promisc_sendup(ppa, mp, proto, skip)
+ upperstr_t *ppa;
+ mblk_t *mp;
+ int proto, skip;
+{
+ mblk_t *dup_mp, *dup_dup_mp;
+ upperstr_t *prus, *nprus;
+
+ if ((prus = find_promisc(ppa, proto)) != 0) {
+ if (dup_mp = dupmsg(mp)) {
+
+ if (skip)
+ dup_mp->b_rptr += PPP_HDRLEN;
+
+ for ( ; nprus = find_promisc(prus->next, proto);
+ prus = nprus) {
+
+ if (dup_dup_mp = dupmsg(dup_mp)) {
+ if (canputnext(prus->q)) {
+ if (prus->flags & US_RAWDATA) {
+ dup_dup_mp = prepend_ether(prus, dup_dup_mp, proto);
+ putnext(prus->q, dup_dup_mp);
+ } else {
+ dup_dup_mp = prepend_udind(prus, dup_dup_mp, proto);
+ putnext(prus->q, dup_dup_mp);
+ }
+ } else {
+ DPRINT("ppp_urput: data to promisc q dropped\n");
+ freemsg(dup_dup_mp);
+ }
+ }
+ }
+
+ if (canputnext(prus->q)) {
+ if (prus->flags & US_RAWDATA) {
+ dup_mp = prepend_ether(prus, dup_mp, proto);
+ putnext(prus->q, dup_mp);
+ } else {
+ dup_mp = prepend_udind(prus, dup_mp, proto);
+ putnext(prus->q, dup_mp);
+ }
+ } else {
+ DPRINT("ppp_urput: data to promisc q dropped\n");
+ freemsg(dup_mp);
+ }
+ }
+ }
+}
+#endif /* defined(SOL2) */
+
+/*
+ * We simply put the message on to the associated upper control stream
+ * (either here or in ppplrsrv). That way we enter the perimeters
+ * before looking through the list of attached streams to decide which
+ * stream it should go up.
+ */
+static int
+ppplrput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ queue_t *uq;
+ struct iocblk *iop;
+
+ switch (mp->b_datap->db_type) {
+ case M_IOCTL:
+ iop = (struct iocblk *) mp->b_rptr;
+ iop->ioc_error = EINVAL;
+ mp->b_datap->db_type = M_IOCNAK;
+ qreply(q, mp);
+ return 0;
+ case M_FLUSH:
+ if (*mp->b_rptr & FLUSHR)
+ flushq(q, FLUSHDATA);
+ if (*mp->b_rptr & FLUSHW) {
+ *mp->b_rptr &= ~FLUSHR;
+ qreply(q, mp);
+ } else
+ freemsg(mp);
+ return 0;
+ }
+
+ /*
+ * If we can't get the lower lock straight away, queue this one
+ * rather than blocking, to avoid the possibility of deadlock.
+ */
+ if (!TRYLOCK_LOWER_R) {
+ putq(q, mp);
+ return 0;
+ }
+
+ /*
+ * Check that we're still connected to the driver.
+ */
+ uq = (queue_t *) q->q_ptr;
+ if (uq == 0) {
+ UNLOCK_LOWER;
+ DPRINT1("ppplrput: q = %x, uq = 0??\n", q);
+ freemsg(mp);
+ return 0;
+ }
+
+ /*
+ * Try to forward the message to the put routine for the upper
+ * control stream for this lower stream.
+ * If there are already messages queued here, queue this one so
+ * they don't get out of order.
+ */
+ if (queclass(mp) == QPCTL || (qsize(q) == 0 && canput(uq)))
+ put(uq, mp);
+ else
+ putq(q, mp);
+
+ UNLOCK_LOWER;
+ return 0;
+}
+
+static int
+ppplrsrv(q)
+ queue_t *q;
+{
+ mblk_t *mp;
+ queue_t *uq;
+
+ /*
+ * Packets get queued here for flow control reasons
+ * or if the lrput routine couldn't get the lower lock
+ * without blocking.
+ */
+ LOCK_LOWER_R;
+ uq = (queue_t *) q->q_ptr;
+ if (uq == 0) {
+ UNLOCK_LOWER;
+ flushq(q, FLUSHALL);
+ DPRINT1("ppplrsrv: q = %x, uq = 0??\n", q);
+ return 0;
+ }
+ while ((mp = getq(q)) != 0) {
+ if (queclass(mp) == QPCTL || canput(uq))
+ put(uq, mp);
+ else {
+ putbq(q, mp);
+ break;
+ }
+ }
+ UNLOCK_LOWER;
+ return 0;
+}
+
+static int
+putctl2(q, type, code, val)
+ queue_t *q;
+ int type, code, val;
+{
+ mblk_t *mp;
+
+ mp = allocb(2, BPRI_HI);
+ if (mp == 0)
+ return 0;
+ mp->b_datap->db_type = type;
+ mp->b_wptr[0] = code;
+ mp->b_wptr[1] = val;
+ mp->b_wptr += 2;
+ putnext(q, mp);
+ return 1;
+}
+
+static int
+putctl4(q, type, code, val)
+ queue_t *q;
+ int type, code, val;
+{
+ mblk_t *mp;
+
+ mp = allocb(4, BPRI_HI);
+ if (mp == 0)
+ return 0;
+ mp->b_datap->db_type = type;
+ mp->b_wptr[0] = code;
+ ((short *)mp->b_wptr)[1] = val;
+ mp->b_wptr += 4;
+ putnext(q, mp);
+ return 1;
+}
+
+static void
+debug_dump(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ upperstr_t *us;
+ queue_t *uq, *lq;
+
+ DPRINT("ppp upper streams:\n");
+ for (us = minor_devs; us != 0; us = us->nextmn) {
+ uq = us->q;
+ DPRINT3(" %d: q=%x rlev=%d",
+ us->mn, uq, (uq? qsize(uq): 0));
+ DPRINT3(" wlev=%d flags=0x%b", (uq? qsize(WR(uq)): 0),
+ us->flags, "\020\1priv\2control\3blocked\4last");
+ DPRINT3(" state=%x sap=%x req_sap=%x", us->state, us->sap,
+ us->req_sap);
+ if (us->ppa == 0)
+ DPRINT(" ppa=?\n");
+ else
+ DPRINT1(" ppa=%d\n", us->ppa->ppa_id);
+ if (us->flags & US_CONTROL) {
+ lq = us->lowerq;
+ DPRINT3(" control for %d lq=%x rlev=%d",
+ us->ppa_id, lq, (lq? qsize(RD(lq)): 0));
+ DPRINT3(" wlev=%d mru=%d mtu=%d\n",
+ (lq? qsize(lq): 0), us->mru, us->mtu);
+ }
+ }
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+}
+
+#ifdef FILTER_PACKETS
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+
+#define MAX_IPHDR 128 /* max TCP/IP header size */
+
+
+/* The following table contains a hard-coded list of protocol/port pairs.
+ * Any matching packets are either discarded unconditionally, or,
+ * if ok_if_link_up is non-zero when a connection does not currently exist
+ * (i.e., they go through if the connection is present, but never initiate
+ * a dial-out).
+ * This idea came from a post by dm@garage.uun.org (David Mazieres)
+ */
+static struct pktfilt_tab {
+ int proto;
+ u_short port;
+ u_short ok_if_link_up;
+} pktfilt_tab[] = {
+ { IPPROTO_UDP, 520, 1 }, /* RIP, ok to pass if link is up */
+ { IPPROTO_UDP, 123, 1 }, /* NTP, don't keep up the link for it */
+ { -1, 0, 0 } /* terminator entry has port == -1 */
+};
+
+
+static int
+ip_hard_filter(us, mp, outbound)
+ upperstr_t *us;
+ mblk_t *mp;
+ int outbound;
+{
+ struct ip *ip;
+ struct pktfilt_tab *pft;
+ mblk_t *temp_mp;
+ int proto;
+ int len, hlen;
+
+
+ /* Note, the PPP header has already been pulled up in all cases */
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: filter, proto=0x%x, out=%d\n", us->mn, proto, outbound);
+
+ switch (proto)
+ {
+ case PPP_IP:
+ if ((mp->b_wptr - mp->b_rptr) == PPP_HDRLEN && mp->b_cont != 0) {
+ temp_mp = mp->b_cont;
+ len = msgdsize(temp_mp);
+ hlen = (len < MAX_IPHDR) ? len : MAX_IPHDR;
+ PULLUP(temp_mp, hlen);
+ if (temp_mp == 0) {
+ DPRINT2("ppp/%d: filter, pullup next failed, len=%d\n",
+ us->mn, hlen);
+ mp->b_cont = 0; /* PULLUP() freed the rest */
+ freemsg(mp);
+ return 0;
+ }
+ ip = (struct ip *)mp->b_cont->b_rptr;
+ }
+ else {
+ len = msgdsize(mp);
+ hlen = (len < (PPP_HDRLEN+MAX_IPHDR)) ? len : (PPP_HDRLEN+MAX_IPHDR);
+ PULLUP(mp, hlen);
+ if (mp == 0) {
+ DPRINT2("ppp/%d: filter, pullup failed, len=%d\n",
+ us->mn, hlen);
+ return 0;
+ }
+ ip = (struct ip *)(mp->b_rptr + PPP_HDRLEN);
+ }
+
+ /* For IP traffic, certain packets (e.g., RIP) may be either
+ * 1. ignored - dropped completely
+ * 2. will not initiate a connection, but
+ * will be passed if a connection is currently up.
+ */
+ for (pft=pktfilt_tab; pft->proto != -1; pft++) {
+ if (ip->ip_p == pft->proto) {
+ switch(pft->proto) {
+ case IPPROTO_UDP:
+ if (((struct udphdr *) &((int *)ip)[ip->ip_hl])->uh_dport
+ == htons(pft->port)) goto endfor;
+ break;
+ case IPPROTO_TCP:
+ if (((struct tcphdr *) &((int *)ip)[ip->ip_hl])->th_dport
+ == htons(pft->port)) goto endfor;
+ break;
+ }
+ }
+ }
+ endfor:
+ if (pft->proto != -1) {
+ if (us->flags & US_DBGLOG)
+ DPRINT3("ppp/%d: found IP pkt, proto=0x%x (%d)\n",
+ us->mn, pft->proto, pft->port);
+ /* Discard if not connected, or if not pass_with_link_up */
+ /* else, if link is up let go by, but don't update time */
+ return pft->ok_if_link_up? -1: 0;
+ }
+ break;
+ } /* end switch (proto) */
+
+ return 1;
+}
+#endif /* FILTER_PACKETS */
+
diff --git a/mdk-stage1/ppp/solaris/ppp.conf b/mdk-stage1/ppp/solaris/ppp.conf
new file mode 100644
index 000000000..e443a7aac
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/ppp.conf
@@ -0,0 +1 @@
+name="ppp" parent="pseudo" instance=0;
diff --git a/mdk-stage1/ppp/solaris/ppp_ahdlc.c b/mdk-stage1/ppp/solaris/ppp_ahdlc.c
new file mode 100644
index 000000000..d0b961258
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/ppp_ahdlc.c
@@ -0,0 +1,878 @@
+/*
+ * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC.
+ *
+ * Re-written by Adi Masputra <adi.masputra@sun.com>, based on
+ * the original ppp_ahdlc.c
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stream.h>
+#include <sys/errno.h>
+
+#ifdef SVR4
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#else
+#include <sys/user.h>
+#ifdef __osf__
+#include <sys/cmn_err.h>
+#endif
+#endif /* SVR4 */
+
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include "ppp_mod.h"
+
+/*
+ * Right now, mutex is only enabled for Solaris 2.x
+ */
+#if defined(SOL2)
+#define USE_MUTEX
+#endif /* SOL2 */
+
+/*
+ * intpointer_t and uintpointer_t are signed and unsigned integer types
+ * large enough to hold any data pointer; that is, data pointers can be
+ * assigned into or from these integer types without losing precision.
+ * On recent Solaris releases, these types are defined in sys/int_types.h,
+ * but not on SunOS 4.x or the earlier Solaris versions.
+ */
+#if defined(_LP64) || defined(_I32LPx)
+typedef long intpointer_t;
+typedef unsigned long uintpointer_t;
+#else
+typedef int intpointer_t;
+typedef unsigned int uintpointer_t;
+#endif
+
+MOD_OPEN_DECL(ahdlc_open);
+MOD_CLOSE_DECL(ahdlc_close);
+static int ahdlc_wput __P((queue_t *, mblk_t *));
+static int ahdlc_rput __P((queue_t *, mblk_t *));
+static void ahdlc_encode __P((queue_t *, mblk_t *));
+static void ahdlc_decode __P((queue_t *, mblk_t *));
+static int msg_byte __P((mblk_t *, unsigned int));
+
+#if defined(SOL2)
+/*
+ * Don't send HDLC start flag is last transmit is within 1.5 seconds -
+ * FLAG_TIME is defined is microseconds
+ */
+#define FLAG_TIME 1500
+#define ABS(x) (x >= 0 ? x : (-x))
+#endif /* SOL2 */
+
+/*
+ * Extract byte i of message mp
+ */
+#define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
+ msg_byte((mp), (i)))
+
+/*
+ * Is this LCP packet one we have to transmit using LCP defaults?
+ */
+#define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
+
+/*
+ * Standard STREAMS declarations
+ */
+static struct module_info minfo = {
+ 0x7d23, "ppp_ahdl", 0, INFPSZ, 32768, 512
+};
+
+static struct qinit rinit = {
+ ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
+};
+
+static struct qinit winit = {
+ ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
+};
+
+#if defined(SVR4) && !defined(SOL2)
+int phdldevflag = 0;
+#define ppp_ahdlcinfo phdlinfo
+#endif /* defined(SVR4) && !defined(SOL2) */
+
+struct streamtab ppp_ahdlcinfo = {
+ &rinit, /* ptr to st_rdinit */
+ &winit, /* ptr to st_wrinit */
+ NULL, /* ptr to st_muxrinit */
+ NULL, /* ptr to st_muxwinit */
+#if defined(SUNOS4)
+ NULL /* ptr to ptr to st_modlist */
+#endif /* SUNOS4 */
+};
+
+#if defined(SUNOS4)
+int ppp_ahdlc_count = 0; /* open counter */
+#endif /* SUNOS4 */
+
+/*
+ * Per-stream state structure
+ */
+typedef struct ahdlc_state {
+#if defined(USE_MUTEX)
+ kmutex_t lock; /* lock for this structure */
+#endif /* USE_MUTEX */
+ int flags; /* link flags */
+ mblk_t *rx_buf; /* ptr to receive buffer */
+ int rx_buf_size; /* receive buffer size */
+ ushort_t infcs; /* calculated rx HDLC FCS */
+ u_int32_t xaccm[8]; /* 256-bit xmit ACCM */
+ u_int32_t raccm; /* 32-bit rcv ACCM */
+ int mtu; /* interface MTU */
+ int mru; /* link MRU */
+ int unit; /* current PPP unit number */
+ struct pppstat stats; /* statistic structure */
+#if defined(SOL2)
+ clock_t flag_time; /* time in usec between flags */
+ clock_t lbolt; /* last updated lbolt */
+#endif /* SOL2 */
+} ahdlc_state_t;
+
+/*
+ * Values for flags
+ */
+#define ESCAPED 0x100 /* last saw escape char on input */
+#define IFLUSH 0x200 /* flushing input due to error */
+
+/*
+ * RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also.
+ */
+#define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+static u_int32_t paritytab[8] =
+{
+ 0x96696996, 0x69969669, 0x69969669, 0x96696996,
+ 0x69969669, 0x96696996, 0x96696996, 0x69969669
+};
+
+/*
+ * STREAMS module open (entry) point
+ */
+MOD_OPEN(ahdlc_open)
+{
+ ahdlc_state_t *state;
+
+ /*
+ * Return if it's already opened
+ */
+ if (q->q_ptr) {
+ return 0;
+ }
+
+ /*
+ * This can only be opened as a module
+ */
+ if (sflag != MODOPEN) {
+ return 0;
+ }
+
+ state = (ahdlc_state_t *) ALLOC_NOSLEEP(sizeof(ahdlc_state_t));
+ if (state == 0)
+ OPEN_ERROR(ENOSR);
+ bzero((caddr_t) state, sizeof(ahdlc_state_t));
+
+ q->q_ptr = (caddr_t) state;
+ WR(q)->q_ptr = (caddr_t) state;
+
+#if defined(USE_MUTEX)
+ mutex_init(&state->lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+ state->xaccm[0] = ~0; /* escape 0x00 through 0x1f */
+ state->xaccm[3] = 0x60000000; /* escape 0x7d and 0x7e */
+ state->mru = PPP_MRU; /* default of 1500 bytes */
+#if defined(SOL2)
+ state->flag_time = drv_usectohz(FLAG_TIME);
+#endif /* SOL2 */
+
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+
+#if defined(SUNOS4)
+ ppp_ahdlc_count++;
+#endif /* SUNOS4 */
+
+ qprocson(q);
+
+ return 0;
+}
+
+/*
+ * STREAMS module close (exit) point
+ */
+MOD_CLOSE(ahdlc_close)
+{
+ ahdlc_state_t *state;
+
+ qprocsoff(q);
+
+ state = (ahdlc_state_t *) q->q_ptr;
+
+ if (state == 0) {
+ DPRINT("state == 0 in ahdlc_close\n");
+ return 0;
+ }
+
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+ if (state->rx_buf != 0) {
+ freemsg(state->rx_buf);
+ state->rx_buf = 0;
+ }
+
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+ mutex_destroy(&state->lock);
+#endif /* USE_MUTEX */
+
+ FREE(q->q_ptr, sizeof(ahdlc_state_t));
+ q->q_ptr = NULL;
+ OTHERQ(q)->q_ptr = NULL;
+
+#if defined(SUNOS4)
+ if (ppp_ahdlc_count)
+ ppp_ahdlc_count--;
+#endif /* SUNOS4 */
+
+ return 0;
+}
+
+/*
+ * Write side put routine
+ */
+static int
+ahdlc_wput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ ahdlc_state_t *state;
+ struct iocblk *iop;
+ int error;
+ mblk_t *np;
+ struct ppp_stats *psp;
+
+ state = (ahdlc_state_t *) q->q_ptr;
+ if (state == 0) {
+ DPRINT("state == 0 in ahdlc_wput\n");
+ freemsg(mp);
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+ case M_DATA:
+ /*
+ * A data packet - do character-stuffing and FCS, and
+ * send it onwards.
+ */
+ ahdlc_encode(q, mp);
+ freemsg(mp);
+ break;
+
+ case M_IOCTL:
+ iop = (struct iocblk *) mp->b_rptr;
+ error = EINVAL;
+ switch (iop->ioc_cmd) {
+ case PPPIO_XACCM:
+ if ((iop->ioc_count < sizeof(u_int32_t)) ||
+ (iop->ioc_count > sizeof(ext_accm))) {
+ break;
+ }
+ if (mp->b_cont == 0) {
+ DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit);
+ break;
+ }
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
+ iop->ioc_count);
+ state->xaccm[2] &= ~0x40000000; /* don't escape 0x5e */
+ state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+
+ case PPPIO_RACCM:
+ if (iop->ioc_count != sizeof(u_int32_t))
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit);
+ break;
+ }
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
+ sizeof(u_int32_t));
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ iop->ioc_count = 0;
+ error = 0;
+ break;
+
+ case PPPIO_GCLEAN:
+ np = allocb(sizeof(int), BPRI_HI);
+ if (np == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = np;
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ *(int *)np->b_wptr = state->flags & RCV_FLAGS;
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ np->b_wptr += sizeof(int);
+ iop->ioc_count = sizeof(int);
+ error = 0;
+ break;
+
+ case PPPIO_GETSTAT:
+ np = allocb(sizeof(struct ppp_stats), BPRI_HI);
+ if (np == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = np;
+ psp = (struct ppp_stats *) np->b_wptr;
+ np->b_wptr += sizeof(struct ppp_stats);
+ bzero((caddr_t)psp, sizeof(struct ppp_stats));
+ psp->p = state->stats;
+ iop->ioc_count = sizeof(struct ppp_stats);
+ error = 0;
+ break;
+
+ case PPPIO_LASTMOD:
+ /* we knew this anyway */
+ error = 0;
+ break;
+
+ default:
+ error = -1;
+ break;
+ }
+
+ if (error < 0)
+ putnext(q, mp);
+ else if (error == 0) {
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+ } else {
+ mp->b_datap->db_type = M_IOCNAK;
+ iop->ioc_count = 0;
+ iop->ioc_error = error;
+ qreply(q, mp);
+ }
+ break;
+
+ case M_CTL:
+ switch (*mp->b_rptr) {
+ case PPPCTL_MTU:
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ state->mtu = ((unsigned short *)mp->b_rptr)[1];
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ freemsg(mp);
+ break;
+ case PPPCTL_MRU:
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ state->mru = ((unsigned short *)mp->b_rptr)[1];
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ freemsg(mp);
+ break;
+ case PPPCTL_UNIT:
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ state->unit = mp->b_rptr[1];
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ break;
+ default:
+ putnext(q, mp);
+ }
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+
+ return 0;
+}
+
+/*
+ * Read side put routine
+ */
+static int
+ahdlc_rput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ ahdlc_state_t *state;
+
+ state = (ahdlc_state_t *) q->q_ptr;
+ if (state == 0) {
+ DPRINT("state == 0 in ahdlc_rput\n");
+ freemsg(mp);
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+ case M_DATA:
+ ahdlc_decode(q, mp);
+ freemsg(mp);
+ break;
+
+ case M_HANGUP:
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+ if (state->rx_buf != 0) {
+ /* XXX would like to send this up for debugging */
+ freemsg(state->rx_buf);
+ state->rx_buf = 0;
+ }
+ state->flags = IFLUSH;
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ putnext(q, mp);
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+ return 0;
+}
+
+/*
+ * Extract bit c from map m, to determine if c needs to be escaped
+ */
+#define IN_TX_MAP(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
+
+static void
+ahdlc_encode(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ ahdlc_state_t *state;
+ u_int32_t *xaccm, loc_xaccm[8];
+ ushort_t fcs;
+ size_t outmp_len;
+ mblk_t *outmp, *tmp;
+ uchar_t *dp, fcs_val;
+ int is_lcp, code;
+#if defined(SOL2)
+ clock_t lbolt;
+#endif /* SOL2 */
+
+ if (msgdsize(mp) < 4) {
+ return;
+ }
+
+ state = (ahdlc_state_t *)q->q_ptr;
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+ /*
+ * Allocate an output buffer large enough to handle a case where all
+ * characters need to be escaped
+ */
+ outmp_len = (msgdsize(mp) << 1) + /* input block x 2 */
+ (sizeof(fcs) << 2) + /* HDLC FCS x 4 */
+ (sizeof(uchar_t) << 1); /* HDLC flags x 2 */
+
+ outmp = allocb(outmp_len, BPRI_MED);
+ if (outmp == NULL) {
+ state->stats.ppp_oerrors++;
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+ putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+ return;
+ }
+
+#if defined(SOL2)
+ /*
+ * Check if our last transmit happenned within flag_time, using
+ * the system's LBOLT value in clock ticks
+ */
+ if (drv_getparm(LBOLT, &lbolt) != -1) {
+ if (ABS((clock_t)lbolt - state->lbolt) > state->flag_time) {
+ *outmp->b_wptr++ = PPP_FLAG;
+ }
+ state->lbolt = lbolt;
+ } else {
+ *outmp->b_wptr++ = PPP_FLAG;
+ }
+#else
+ /*
+ * If the driver below still has a message to process, skip the
+ * HDLC flag, otherwise, put one in the beginning
+ */
+ if (qsize(q->q_next) == 0) {
+ *outmp->b_wptr++ = PPP_FLAG;
+ }
+#endif
+
+ /*
+ * All control characters must be escaped for LCP packets with code
+ * values between 1 (Conf-Req) and 7 (Code-Rej).
+ */
+ is_lcp = ((MSG_BYTE(mp, 0) == PPP_ALLSTATIONS) &&
+ (MSG_BYTE(mp, 1) == PPP_UI) &&
+ (MSG_BYTE(mp, 2) == (PPP_LCP >> 8)) &&
+ (MSG_BYTE(mp, 3) == (PPP_LCP & 0xff)) &&
+ LCP_USE_DFLT(mp));
+
+ xaccm = state->xaccm;
+ if (is_lcp) {
+ bcopy((caddr_t)state->xaccm, (caddr_t)loc_xaccm, sizeof(loc_xaccm));
+ loc_xaccm[0] = ~0; /* force escape on 0x00 through 0x1f */
+ xaccm = loc_xaccm;
+ }
+
+ fcs = PPP_INITFCS; /* Initial FCS is 0xffff */
+
+ /*
+ * Process this block and the rest (if any) attached to the this one
+ */
+ for (tmp = mp; tmp; tmp = tmp->b_cont) {
+ if (tmp->b_datap->db_type == M_DATA) {
+ for (dp = tmp->b_rptr; dp < tmp->b_wptr; dp++) {
+ fcs = PPP_FCS(fcs, *dp);
+ if (IN_TX_MAP(*dp, xaccm)) {
+ *outmp->b_wptr++ = PPP_ESCAPE;
+ *outmp->b_wptr++ = *dp ^ PPP_TRANS;
+ } else {
+ *outmp->b_wptr++ = *dp;
+ }
+ }
+ } else {
+ continue; /* skip if db_type is something other than M_DATA */
+ }
+ }
+
+ /*
+ * Append the HDLC FCS, making sure that escaping is done on any
+ * necessary bytes
+ */
+ fcs_val = (fcs ^ 0xffff) & 0xff;
+ if (IN_TX_MAP(fcs_val, xaccm)) {
+ *outmp->b_wptr++ = PPP_ESCAPE;
+ *outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+ } else {
+ *outmp->b_wptr++ = fcs_val;
+ }
+
+ fcs_val = ((fcs ^ 0xffff) >> 8) & 0xff;
+ if (IN_TX_MAP(fcs_val, xaccm)) {
+ *outmp->b_wptr++ = PPP_ESCAPE;
+ *outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+ } else {
+ *outmp->b_wptr++ = fcs_val;
+ }
+
+ /*
+ * And finally, append the HDLC flag, and send it away
+ */
+ *outmp->b_wptr++ = PPP_FLAG;
+
+ state->stats.ppp_obytes += msgdsize(outmp);
+ state->stats.ppp_opackets++;
+
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+
+ putnext(q, outmp);
+ return;
+}
+
+/*
+ * Checks the 32-bit receive ACCM to see if the byte needs un-escaping
+ */
+#define IN_RX_MAP(c, m) ((((unsigned int) (uchar_t) (c)) < 0x20) && \
+ (m) & (1 << (c)))
+
+
+/*
+ * Process received characters.
+ */
+static void
+ahdlc_decode(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ ahdlc_state_t *state;
+ mblk_t *om;
+ uchar_t *dp;
+ ushort_t fcs;
+#if defined(SOL2)
+ mblk_t *zmp;
+#endif /* SOL2 */
+
+#if defined(SOL2)
+ /*
+ * In case the driver (or something below) doesn't send
+ * data upstream in one message block, concatenate everything
+ */
+ if (!((mp->b_wptr - mp->b_rptr == msgdsize(mp)) &&
+ ((intpointer_t)mp->b_rptr % sizeof(intpointer_t) == 0))) {
+
+ zmp = msgpullup(mp, -1);
+ freemsg(mp);
+ mp = zmp;
+ if (mp == 0)
+ return;
+ }
+#endif /* SOL2 */
+
+ state = (ahdlc_state_t *) q->q_ptr;
+
+#if defined(USE_MUTEX)
+ mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+ state->stats.ppp_ibytes += msgdsize(mp);
+
+ for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) {
+
+ /*
+ * This should detect the lack of 8-bit communication channel
+ * which is necessary for PPP to work. In addition, it also
+ * checks on the parity.
+ */
+ if (*dp & 0x80)
+ state->flags |= RCV_B7_1;
+ else
+ state->flags |= RCV_B7_0;
+
+ if (paritytab[*dp >> 5] & (1 << (*dp & 0x1f)))
+ state->flags |= RCV_ODDP;
+ else
+ state->flags |= RCV_EVNP;
+
+ /*
+ * So we have a HDLC flag ...
+ */
+ if (*dp == PPP_FLAG) {
+
+ /*
+ * If we think that it marks the beginning of the frame,
+ * then continue to process the next octects
+ */
+ if ((state->flags & IFLUSH) ||
+ (state->rx_buf == 0) ||
+ (msgdsize(state->rx_buf) == 0)) {
+
+ state->flags &= ~IFLUSH;
+ continue;
+ }
+
+ /*
+ * We get here because the above condition isn't true,
+ * in which case the HDLC flag was there to mark the end
+ * of the frame (or so we think)
+ */
+ om = state->rx_buf;
+
+ if (state->infcs == PPP_GOODFCS) {
+ state->stats.ppp_ipackets++;
+ adjmsg(om, -PPP_FCSLEN);
+ putnext(q, om);
+ } else {
+ DPRINT2("ppp%d: bad fcs (len=%d)\n",
+ state->unit, msgdsize(state->rx_buf));
+ freemsg(state->rx_buf);
+ state->flags &= ~(IFLUSH | ESCAPED);
+ state->stats.ppp_ierrors++;
+ putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+ }
+
+ state->rx_buf = 0;
+ continue;
+ }
+
+ if (state->flags & IFLUSH) {
+ continue;
+ }
+
+ /*
+ * Allocate a receive buffer, large enough to store a frame (after
+ * un-escaping) of at least 1500 octets. If MRU is negotiated to
+ * be more than the default, then allocate that much. In addition,
+ * we add an extra 32-bytes for a fudge factor
+ */
+ if (state->rx_buf == 0) {
+ state->rx_buf_size = (state->mru < PPP_MRU ? PPP_MRU : state->mru);
+ state->rx_buf_size += (sizeof(u_int32_t) << 3);
+ state->rx_buf = allocb(state->rx_buf_size, BPRI_MED);
+
+ /*
+ * If allocation fails, try again on the next frame
+ */
+ if (state->rx_buf == 0) {
+ state->flags |= IFLUSH;
+ continue;
+ }
+ state->flags &= ~(IFLUSH | ESCAPED);
+ state->infcs = PPP_INITFCS;
+ }
+
+ if (*dp == PPP_ESCAPE) {
+ state->flags |= ESCAPED;
+ continue;
+ }
+
+ /*
+ * Make sure we un-escape the necessary characters, as well as the
+ * ones in our receive async control character map
+ */
+ if (state->flags & ESCAPED) {
+ *dp ^= PPP_TRANS;
+ state->flags &= ~ESCAPED;
+ } else if (IN_RX_MAP(*dp, state->raccm))
+ continue;
+
+ /*
+ * Unless the peer lied to us about the negotiated MRU, we should
+ * never get a frame which is too long. If it happens, toss it away
+ * and grab the next incoming one
+ */
+ if (msgdsize(state->rx_buf) < state->rx_buf_size) {
+ state->infcs = PPP_FCS(state->infcs, *dp);
+ *state->rx_buf->b_wptr++ = *dp;
+ } else {
+ DPRINT2("ppp%d: frame too long (%d)\n",
+ state->unit, msgdsize(state->rx_buf));
+ freemsg(state->rx_buf);
+ state->rx_buf = 0;
+ state->flags |= IFLUSH;
+ }
+ }
+
+#if defined(USE_MUTEX)
+ mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+}
+
+static int
+msg_byte(mp, i)
+ mblk_t *mp;
+ unsigned int i;
+{
+ while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
+ mp = mp->b_cont;
+ if (mp == 0)
+ return -1;
+ return mp->b_rptr[i];
+}
diff --git a/mdk-stage1/ppp/solaris/ppp_ahdlc_mod.c b/mdk-stage1/ppp/solaris/ppp_ahdlc_mod.c
new file mode 100644
index 000000000..f81be8abb
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/ppp_ahdlc_mod.c
@@ -0,0 +1,49 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+
+extern struct streamtab ppp_ahdlcinfo;
+
+static struct fmodsw fsw = {
+ "ppp_ahdl",
+ &ppp_ahdlcinfo,
+ D_NEW | D_MP | D_MTQPAIR
+};
+
+extern struct mod_ops mod_strmodops;
+
+static struct modlstrmod modlstrmod = {
+ &mod_strmodops,
+ "PPP async HDLC module",
+ &fsw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *) &modlstrmod,
+ NULL
+};
+
+/*
+ * Entry points for modloading.
+ */
+int
+_init(void)
+{
+ return mod_install(&modlinkage);
+}
+
+int
+_fini(void)
+{
+ return mod_remove(&modlinkage);
+}
+
+int
+_info(mip)
+ struct modinfo *mip;
+{
+ return mod_info(&modlinkage, mip);
+}
diff --git a/mdk-stage1/ppp/solaris/ppp_comp.c b/mdk-stage1/ppp/solaris/ppp_comp.c
new file mode 100644
index 000000000..f6eef5ab1
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/ppp_comp.c
@@ -0,0 +1,1126 @@
+/*
+ * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/stream.h>
+
+#ifdef SVR4
+#include <sys/conf.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#else
+#include <sys/user.h>
+#ifdef __osf__
+#include <sys/cmn_err.h>
+#endif
+#endif /* SVR4 */
+
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include "ppp_mod.h"
+
+#ifdef __osf__
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <net/vjcompress.h>
+
+#define PACKETPTR mblk_t *
+#include <net/ppp-comp.h>
+
+MOD_OPEN_DECL(ppp_comp_open);
+MOD_CLOSE_DECL(ppp_comp_close);
+static int ppp_comp_rput __P((queue_t *, mblk_t *));
+static int ppp_comp_rsrv __P((queue_t *));
+static int ppp_comp_wput __P((queue_t *, mblk_t *));
+static int ppp_comp_wsrv __P((queue_t *));
+static void ppp_comp_ccp __P((queue_t *, mblk_t *, int));
+static int msg_byte __P((mblk_t *, unsigned int));
+
+/* Extract byte i of message mp. */
+#define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
+ msg_byte((mp), (i)))
+
+/* Is this LCP packet one we have to transmit using LCP defaults? */
+#define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
+
+#define PPP_COMP_ID 0xbadf
+static struct module_info minfo = {
+#ifdef PRIOQ
+ PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16512, 16384,
+#else
+ PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16384, 4096,
+#endif
+};
+
+static struct qinit r_init = {
+ ppp_comp_rput, ppp_comp_rsrv, ppp_comp_open, ppp_comp_close,
+ NULL, &minfo, NULL
+};
+
+static struct qinit w_init = {
+ ppp_comp_wput, ppp_comp_wsrv, NULL, NULL, NULL, &minfo, NULL
+};
+
+#if defined(SVR4) && !defined(SOL2)
+int pcmpdevflag = 0;
+#define ppp_compinfo pcmpinfo
+#endif
+struct streamtab ppp_compinfo = {
+ &r_init, &w_init, NULL, NULL
+};
+
+int ppp_comp_count; /* number of module instances in use */
+
+#ifdef __osf__
+
+static void ppp_comp_alloc __P((comp_state_t *));
+typedef struct memreq {
+ unsigned char comp_opts[20];
+ int cmd;
+ int thread_status;
+ char *returned_mem;
+} memreq_t;
+
+#endif
+
+typedef struct comp_state {
+ int flags;
+ int mru;
+ int mtu;
+ int unit;
+ struct compressor *xcomp;
+ void *xstate;
+ struct compressor *rcomp;
+ void *rstate;
+ struct vjcompress vj_comp;
+ int vj_last_ierrors;
+ struct pppstat stats;
+#ifdef __osf__
+ memreq_t memreq;
+ thread_t thread;
+#endif
+} comp_state_t;
+
+
+#ifdef __osf__
+extern task_t first_task;
+#endif
+
+/* Bits in flags are as defined in pppio.h. */
+#define CCP_ERR (CCP_ERROR | CCP_FATALERROR)
+#define LAST_MOD 0x1000000 /* no ppp modules below us */
+#define DBGLOG 0x2000000 /* log debugging stuff */
+
+#define MAX_IPHDR 128 /* max TCP/IP header size */
+#define MAX_VJHDR 20 /* max VJ compressed header size (?) */
+
+#undef MIN /* just in case */
+#define MIN(a, b) ((a) < (b)? (a): (b))
+
+/*
+ * List of compressors we know about.
+ */
+
+#if DO_BSD_COMPRESS
+extern struct compressor ppp_bsd_compress;
+#endif
+#if DO_DEFLATE
+extern struct compressor ppp_deflate, ppp_deflate_draft;
+#endif
+
+struct compressor *ppp_compressors[] = {
+#if DO_BSD_COMPRESS
+ &ppp_bsd_compress,
+#endif
+#if DO_DEFLATE
+ &ppp_deflate,
+ &ppp_deflate_draft,
+#endif
+ NULL
+};
+
+/*
+ * STREAMS module entry points.
+ */
+MOD_OPEN(ppp_comp_open)
+{
+ comp_state_t *cp;
+#ifdef __osf__
+ thread_t thread;
+#endif
+
+ if (q->q_ptr == NULL) {
+ cp = (comp_state_t *) ALLOC_SLEEP(sizeof(comp_state_t));
+ if (cp == NULL)
+ OPEN_ERROR(ENOSR);
+ bzero((caddr_t)cp, sizeof(comp_state_t));
+ WR(q)->q_ptr = q->q_ptr = (caddr_t) cp;
+ cp->mru = PPP_MRU;
+ cp->mtu = PPP_MTU;
+ cp->xstate = NULL;
+ cp->rstate = NULL;
+ vj_compress_init(&cp->vj_comp, -1);
+#ifdef __osf__
+ if (!(thread = kernel_thread_w_arg(first_task, ppp_comp_alloc, (void *)cp)))
+ OPEN_ERROR(ENOSR);
+ cp->thread = thread;
+#endif
+ ++ppp_comp_count;
+ qprocson(q);
+ }
+ return 0;
+}
+
+MOD_CLOSE(ppp_comp_close)
+{
+ comp_state_t *cp;
+
+ qprocsoff(q);
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp != NULL) {
+ if (cp->xstate != NULL)
+ (*cp->xcomp->comp_free)(cp->xstate);
+ if (cp->rstate != NULL)
+ (*cp->rcomp->decomp_free)(cp->rstate);
+#ifdef __osf__
+ if (!cp->thread)
+ printf("ppp_comp_close: NULL thread!\n");
+ else
+ thread_terminate(cp->thread);
+#endif
+ FREE(cp, sizeof(comp_state_t));
+ q->q_ptr = NULL;
+ OTHERQ(q)->q_ptr = NULL;
+ --ppp_comp_count;
+ }
+ return 0;
+}
+
+#ifdef __osf__
+
+/* thread for calling back to a compressor's memory allocator
+ * Needed for Digital UNIX since it's VM can't handle requests
+ * for large amounts of memory without blocking. The thread
+ * provides a context in which we can call a memory allocator
+ * that may block.
+ */
+static void
+ppp_comp_alloc(comp_state_t *cp)
+{
+ int len, cmd;
+ unsigned char *compressor_options;
+ thread_t thread;
+ void *(*comp_allocator)();
+
+
+#if defined(MAJOR_VERSION) && (MAJOR_VERSION <= 2)
+
+ /* In 2.x and earlier the argument gets passed
+ * in the thread structure itself. Yuck.
+ */
+ thread = current_thread();
+ cp = thread->reply_port;
+ thread->reply_port = PORT_NULL;
+
+#endif
+
+ for (;;) {
+ assert_wait((vm_offset_t)&cp->memreq.thread_status, TRUE);
+ thread_block();
+
+ if (thread_should_halt(current_thread()))
+ thread_halt_self();
+ cmd = cp->memreq.cmd;
+ compressor_options = &cp->memreq.comp_opts[0];
+ len = compressor_options[1];
+ if (cmd == PPPIO_XCOMP) {
+ cp->memreq.returned_mem = cp->xcomp->comp_alloc(compressor_options, len);
+ if (!cp->memreq.returned_mem) {
+ cp->memreq.thread_status = ENOSR;
+ } else {
+ cp->memreq.thread_status = 0;
+ }
+ } else {
+ cp->memreq.returned_mem = cp->rcomp->decomp_alloc(compressor_options, len);
+ if (!cp->memreq.returned_mem) {
+ cp->memreq.thread_status = ENOSR;
+ } else {
+ cp->memreq.thread_status = 0;
+ }
+ }
+ }
+}
+
+#endif /* __osf__ */
+
+/* here's the deal with memory allocation under Digital UNIX.
+ * Some other may also benefit from this...
+ * We can't ask for huge chunks of memory in a context where
+ * the caller can't be put to sleep (like, here.) The alloc
+ * is likely to fail. Instead we do this: the first time we
+ * get called, kick off a thread to do the allocation. Return
+ * immediately to the caller with EAGAIN, as an indication that
+ * they should send down the ioctl again. By the time the
+ * second call comes in it's likely that the memory allocation
+ * thread will have returned with the requested memory. We will
+ * continue to return EAGAIN however until the thread has completed.
+ * When it has, we return zero (and the memory) if the allocator
+ * was successful and ENOSR otherwise.
+ *
+ * Callers of the RCOMP and XCOMP ioctls are encouraged (but not
+ * required) to loop for some number of iterations with a small
+ * delay in the loop body (for instance a 1/10-th second "sleep"
+ * via select.)
+ */
+static int
+ppp_comp_wput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ struct iocblk *iop;
+ comp_state_t *cp;
+ int error, len, n;
+ int flags, mask;
+ mblk_t *np;
+ struct compressor **comp;
+ struct ppp_stats *psp;
+ struct ppp_comp_stats *csp;
+ unsigned char *opt_data;
+ int nxslots, nrslots;
+
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp == 0) {
+ DPRINT("cp == 0 in ppp_comp_wput\n");
+ freemsg(mp);
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+
+ case M_DATA:
+ putq(q, mp);
+ break;
+
+ case M_IOCTL:
+ iop = (struct iocblk *) mp->b_rptr;
+ error = EINVAL;
+ switch (iop->ioc_cmd) {
+
+ case PPPIO_CFLAGS:
+ /* set/get CCP state */
+ if (iop->ioc_count != 2 * sizeof(int))
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ppp_comp_wput/%d: PPPIO_CFLAGS b_cont = 0!\n", cp->unit);
+ break;
+ }
+ flags = ((int *) mp->b_cont->b_rptr)[0];
+ mask = ((int *) mp->b_cont->b_rptr)[1];
+ cp->flags = (cp->flags & ~mask) | (flags & mask);
+ if ((mask & CCP_ISOPEN) && (flags & CCP_ISOPEN) == 0) {
+ if (cp->xstate != NULL) {
+ (*cp->xcomp->comp_free)(cp->xstate);
+ cp->xstate = NULL;
+ }
+ if (cp->rstate != NULL) {
+ (*cp->rcomp->decomp_free)(cp->rstate);
+ cp->rstate = NULL;
+ }
+ cp->flags &= ~CCP_ISUP;
+ }
+ error = 0;
+ iop->ioc_count = sizeof(int);
+ ((int *) mp->b_cont->b_rptr)[0] = cp->flags;
+ mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int);
+ break;
+
+ case PPPIO_VJINIT:
+ /*
+ * Initialize VJ compressor/decompressor
+ */
+ if (iop->ioc_count != 2)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ppp_comp_wput/%d: PPPIO_VJINIT b_cont = 0!\n", cp->unit);
+ break;
+ }
+ nxslots = mp->b_cont->b_rptr[0] + 1;
+ nrslots = mp->b_cont->b_rptr[1] + 1;
+ if (nxslots > MAX_STATES || nrslots > MAX_STATES)
+ break;
+ vj_compress_init(&cp->vj_comp, nxslots);
+ cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+ error = 0;
+ iop->ioc_count = 0;
+ break;
+
+ case PPPIO_XCOMP:
+ case PPPIO_RCOMP:
+ if (iop->ioc_count <= 0)
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ppp_comp_wput/%d: PPPIO_[XR]COMP b_cont = 0!\n", cp->unit);
+ break;
+ }
+ opt_data = mp->b_cont->b_rptr;
+ len = mp->b_cont->b_wptr - opt_data;
+ if (len > iop->ioc_count)
+ len = iop->ioc_count;
+ if (opt_data[1] < 2 || opt_data[1] > len)
+ break;
+ for (comp = ppp_compressors; *comp != NULL; ++comp)
+ if ((*comp)->compress_proto == opt_data[0]) {
+ /* here's the handler! */
+ error = 0;
+#ifndef __osf__
+ if (iop->ioc_cmd == PPPIO_XCOMP) {
+ /* A previous call may have fetched memory for a compressor
+ * that's now being retired or reset. Free it using it's
+ * mechanism for freeing stuff.
+ */
+ if (cp->xstate != NULL) {
+ (*cp->xcomp->comp_free)(cp->xstate);
+ cp->xstate = NULL;
+ }
+ cp->xcomp = *comp;
+ cp->xstate = (*comp)->comp_alloc(opt_data, len);
+ if (cp->xstate == NULL)
+ error = ENOSR;
+ } else {
+ if (cp->rstate != NULL) {
+ (*cp->rcomp->decomp_free)(cp->rstate);
+ cp->rstate = NULL;
+ }
+ cp->rcomp = *comp;
+ cp->rstate = (*comp)->decomp_alloc(opt_data, len);
+ if (cp->rstate == NULL)
+ error = ENOSR;
+ }
+#else
+ if ((error = cp->memreq.thread_status) != EAGAIN)
+ if (iop->ioc_cmd == PPPIO_XCOMP) {
+ if (cp->xstate) {
+ (*cp->xcomp->comp_free)(cp->xstate);
+ cp->xstate = 0;
+ }
+ /* sanity check for compressor options
+ */
+ if (sizeof (cp->memreq.comp_opts) < len) {
+ printf("can't handle options for compressor %d (%d)\n", opt_data[0],
+ opt_data[1]);
+ cp->memreq.thread_status = ENOSR;
+ cp->memreq.returned_mem = 0;
+ }
+ /* fill in request for the thread and kick it off
+ */
+ if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) {
+ bcopy(opt_data, cp->memreq.comp_opts, len);
+ cp->memreq.cmd = PPPIO_XCOMP;
+ cp->xcomp = *comp;
+ error = cp->memreq.thread_status = EAGAIN;
+ thread_wakeup((vm_offset_t)&cp->memreq.thread_status);
+ } else {
+ cp->xstate = cp->memreq.returned_mem;
+ cp->memreq.returned_mem = 0;
+ cp->memreq.thread_status = 0;
+ }
+ } else {
+ if (cp->rstate) {
+ (*cp->rcomp->decomp_free)(cp->rstate);
+ cp->rstate = NULL;
+ }
+ if (sizeof (cp->memreq.comp_opts) < len) {
+ printf("can't handle options for compressor %d (%d)\n", opt_data[0],
+ opt_data[1]);
+ cp->memreq.thread_status = ENOSR;
+ cp->memreq.returned_mem = 0;
+ }
+ if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) {
+ bcopy(opt_data, cp->memreq.comp_opts, len);
+ cp->memreq.cmd = PPPIO_RCOMP;
+ cp->rcomp = *comp;
+ error = cp->memreq.thread_status = EAGAIN;
+ thread_wakeup((vm_offset_t)&cp->memreq.thread_status);
+ } else {
+ cp->rstate = cp->memreq.returned_mem;
+ cp->memreq.returned_mem = 0;
+ cp->memreq.thread_status = 0;
+ }
+ }
+#endif
+ break;
+ }
+ iop->ioc_count = 0;
+ break;
+
+ case PPPIO_GETSTAT:
+ if ((cp->flags & LAST_MOD) == 0) {
+ error = -1; /* let the ppp_ahdl module handle it */
+ break;
+ }
+ np = allocb(sizeof(struct ppp_stats), BPRI_HI);
+ if (np == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = np;
+ psp = (struct ppp_stats *) np->b_wptr;
+ np->b_wptr += sizeof(struct ppp_stats);
+ iop->ioc_count = sizeof(struct ppp_stats);
+ psp->p = cp->stats;
+ psp->vj = cp->vj_comp.stats;
+ error = 0;
+ break;
+
+ case PPPIO_GETCSTAT:
+ np = allocb(sizeof(struct ppp_comp_stats), BPRI_HI);
+ if (np == 0) {
+ error = ENOSR;
+ break;
+ }
+ if (mp->b_cont != 0)
+ freemsg(mp->b_cont);
+ mp->b_cont = np;
+ csp = (struct ppp_comp_stats *) np->b_wptr;
+ np->b_wptr += sizeof(struct ppp_comp_stats);
+ iop->ioc_count = sizeof(struct ppp_comp_stats);
+ bzero((caddr_t)csp, sizeof(struct ppp_comp_stats));
+ if (cp->xstate != 0)
+ (*cp->xcomp->comp_stat)(cp->xstate, &csp->c);
+ if (cp->rstate != 0)
+ (*cp->rcomp->decomp_stat)(cp->rstate, &csp->d);
+ error = 0;
+ break;
+
+ case PPPIO_DEBUG:
+ if (iop->ioc_count != sizeof(int))
+ break;
+ if (mp->b_cont == 0) {
+ DPRINT1("ppp_comp_wput/%d: PPPIO_DEBUG b_cont = 0!\n", cp->unit);
+ break;
+ }
+ n = *(int *)mp->b_cont->b_rptr;
+ if (n == PPPDBG_LOG + PPPDBG_COMP) {
+ DPRINT1("ppp_comp%d: debug log enabled\n", cp->unit);
+ cp->flags |= DBGLOG;
+ error = 0;
+ iop->ioc_count = 0;
+ } else {
+ error = -1;
+ }
+ break;
+
+ case PPPIO_LASTMOD:
+ cp->flags |= LAST_MOD;
+ error = 0;
+ break;
+
+ default:
+ error = -1;
+ break;
+ }
+
+ if (error < 0)
+ putnext(q, mp);
+ else if (error == 0) {
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q, mp);
+ } else {
+ mp->b_datap->db_type = M_IOCNAK;
+ iop->ioc_error = error;
+ iop->ioc_count = 0;
+ qreply(q, mp);
+ }
+ break;
+
+ case M_CTL:
+ switch (*mp->b_rptr) {
+ case PPPCTL_MTU:
+ cp->mtu = ((unsigned short *)mp->b_rptr)[1];
+ break;
+ case PPPCTL_MRU:
+ cp->mru = ((unsigned short *)mp->b_rptr)[1];
+ break;
+ case PPPCTL_UNIT:
+ cp->unit = mp->b_rptr[1];
+ break;
+ }
+ putnext(q, mp);
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+
+ return 0;
+}
+
+static int
+ppp_comp_wsrv(q)
+ queue_t *q;
+{
+ mblk_t *mp, *cmp = NULL;
+ comp_state_t *cp;
+ int len, proto, type, hlen, code;
+ struct ip *ip;
+ unsigned char *vjhdr, *dp;
+
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp == 0) {
+ DPRINT("cp == 0 in ppp_comp_wsrv\n");
+ return 0;
+ }
+
+ while ((mp = getq(q)) != 0) {
+ /* assert(mp->b_datap->db_type == M_DATA) */
+#ifdef PRIOQ
+ if (!bcanputnext(q,mp->b_band))
+#else
+ if (!canputnext(q))
+#endif PRIOQ
+ {
+ putbq(q, mp);
+ break;
+ }
+
+ /*
+ * First check the packet length and work out what the protocol is.
+ */
+ len = msgdsize(mp);
+ if (len < PPP_HDRLEN) {
+ DPRINT1("ppp_comp_wsrv: bogus short packet (%d)\n", len);
+ freemsg(mp);
+ cp->stats.ppp_oerrors++;
+ putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+ continue;
+ }
+ proto = (MSG_BYTE(mp, 2) << 8) + MSG_BYTE(mp, 3);
+
+ /*
+ * Make sure we've got enough data in the first mblk
+ * and that we are its only user.
+ */
+ if (proto == PPP_CCP)
+ hlen = len;
+ else if (proto == PPP_IP)
+ hlen = PPP_HDRLEN + MAX_IPHDR;
+ else
+ hlen = PPP_HDRLEN;
+ if (hlen > len)
+ hlen = len;
+ if (mp->b_wptr < mp->b_rptr + hlen || mp->b_datap->db_ref > 1) {
+ PULLUP(mp, hlen);
+ if (mp == 0) {
+ DPRINT1("ppp_comp_wsrv: pullup failed (%d)\n", hlen);
+ cp->stats.ppp_oerrors++;
+ putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+ continue;
+ }
+ }
+
+ /*
+ * Do VJ compression if requested.
+ */
+ if (proto == PPP_IP && (cp->flags & COMP_VJC)) {
+ ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN);
+ if (ip->ip_p == IPPROTO_TCP) {
+ type = vj_compress_tcp(ip, len - PPP_HDRLEN, &cp->vj_comp,
+ (cp->flags & COMP_VJCCID), &vjhdr);
+ switch (type) {
+ case TYPE_UNCOMPRESSED_TCP:
+ mp->b_rptr[3] = proto = PPP_VJC_UNCOMP;
+ break;
+ case TYPE_COMPRESSED_TCP:
+ dp = vjhdr - PPP_HDRLEN;
+ dp[1] = mp->b_rptr[1]; /* copy control field */
+ dp[0] = mp->b_rptr[0]; /* copy address field */
+ dp[2] = 0; /* set protocol field */
+ dp[3] = proto = PPP_VJC_COMP;
+ mp->b_rptr = dp;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Do packet compression if enabled.
+ */
+ if (proto == PPP_CCP)
+ ppp_comp_ccp(q, mp, 0);
+ else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN)
+ && cp->xstate != NULL) {
+ len = msgdsize(mp);
+ (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len,
+ (cp->flags & CCP_ISUP? cp->mtu + PPP_HDRLEN: 0));
+ if (cmp != NULL) {
+#ifdef PRIOQ
+ cmp->b_band=mp->b_band;
+#endif PRIOQ
+ freemsg(mp);
+ mp = cmp;
+ }
+ }
+
+ /*
+ * Do address/control and protocol compression if enabled.
+ */
+ if ((cp->flags & COMP_AC)
+ && !(proto == PPP_LCP && LCP_USE_DFLT(mp))) {
+ mp->b_rptr += 2; /* drop the address & ctrl fields */
+ if (proto < 0x100 && (cp->flags & COMP_PROT))
+ ++mp->b_rptr; /* drop the high protocol byte */
+ } else if (proto < 0x100 && (cp->flags & COMP_PROT)) {
+ /* shuffle up the address & ctrl fields */
+ mp->b_rptr[2] = mp->b_rptr[1];
+ mp->b_rptr[1] = mp->b_rptr[0];
+ ++mp->b_rptr;
+ }
+
+ cp->stats.ppp_opackets++;
+ cp->stats.ppp_obytes += msgdsize(mp);
+ putnext(q, mp);
+ }
+
+ return 0;
+}
+
+static int
+ppp_comp_rput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ comp_state_t *cp;
+ struct iocblk *iop;
+ struct ppp_stats *psp;
+
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp == 0) {
+ DPRINT("cp == 0 in ppp_comp_rput\n");
+ freemsg(mp);
+ return 0;
+ }
+
+ switch (mp->b_datap->db_type) {
+
+ case M_DATA:
+ putq(q, mp);
+ break;
+
+ case M_IOCACK:
+ iop = (struct iocblk *) mp->b_rptr;
+ switch (iop->ioc_cmd) {
+ case PPPIO_GETSTAT:
+ /*
+ * Catch this on the way back from the ppp_ahdl module
+ * so we can fill in the VJ stats.
+ */
+ if (mp->b_cont == 0 || iop->ioc_count != sizeof(struct ppp_stats))
+ break;
+ psp = (struct ppp_stats *) mp->b_cont->b_rptr;
+ psp->vj = cp->vj_comp.stats;
+ break;
+ }
+ putnext(q, mp);
+ break;
+
+ case M_CTL:
+ switch (mp->b_rptr[0]) {
+ case PPPCTL_IERROR:
+ ++cp->stats.ppp_ierrors;
+ break;
+ case PPPCTL_OERROR:
+ ++cp->stats.ppp_oerrors;
+ break;
+ }
+ putnext(q, mp);
+ break;
+
+ default:
+ putnext(q, mp);
+ }
+
+ return 0;
+}
+
+static int
+ppp_comp_rsrv(q)
+ queue_t *q;
+{
+ int proto, rv, i;
+ mblk_t *mp, *dmp = NULL, *np;
+ uchar_t *dp, *iphdr;
+ comp_state_t *cp;
+ int len, hlen, vjlen;
+ u_int iphlen;
+
+ cp = (comp_state_t *) q->q_ptr;
+ if (cp == 0) {
+ DPRINT("cp == 0 in ppp_comp_rsrv\n");
+ return 0;
+ }
+
+ while ((mp = getq(q)) != 0) {
+ /* assert(mp->b_datap->db_type == M_DATA) */
+ if (!canputnext(q)) {
+ putbq(q, mp);
+ break;
+ }
+
+ len = msgdsize(mp);
+ cp->stats.ppp_ibytes += len;
+ cp->stats.ppp_ipackets++;
+
+ /*
+ * First work out the protocol and where the PPP header ends.
+ */
+ i = 0;
+ proto = MSG_BYTE(mp, 0);
+ if (proto == PPP_ALLSTATIONS) {
+ i = 2;
+ proto = MSG_BYTE(mp, 2);
+ }
+ if ((proto & 1) == 0) {
+ ++i;
+ proto = (proto << 8) + MSG_BYTE(mp, i);
+ }
+ hlen = i + 1;
+
+ /*
+ * Now reconstruct a complete, contiguous PPP header at the
+ * start of the packet.
+ */
+ if (hlen < ((cp->flags & DECOMP_AC)? 0: 2)
+ + ((cp->flags & DECOMP_PROT)? 1: 2)) {
+ /* count these? */
+ goto bad;
+ }
+ if (mp->b_rptr + hlen > mp->b_wptr) {
+ adjmsg(mp, hlen); /* XXX check this call */
+ hlen = 0;
+ }
+ if (hlen != PPP_HDRLEN) {
+ /*
+ * We need to put some bytes on the front of the packet
+ * to make a full-length PPP header.
+ * If we can put them in *mp, we do, otherwise we
+ * tack another mblk on the front.
+ * XXX we really shouldn't need to carry around
+ * the address and control at this stage.
+ */
+ dp = mp->b_rptr + hlen - PPP_HDRLEN;
+ if (dp < mp->b_datap->db_base || mp->b_datap->db_ref > 1) {
+ np = allocb(PPP_HDRLEN, BPRI_MED);
+ if (np == 0)
+ goto bad;
+ np->b_cont = mp;
+ mp->b_rptr += hlen;
+ mp = np;
+ dp = mp->b_wptr;
+ mp->b_wptr += PPP_HDRLEN;
+ } else
+ mp->b_rptr = dp;
+
+ dp[0] = PPP_ALLSTATIONS;
+ dp[1] = PPP_UI;
+ dp[2] = proto >> 8;
+ dp[3] = proto;
+ }
+
+ /*
+ * Now see if we have a compressed packet to decompress,
+ * or a CCP packet to take notice of.
+ */
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ if (proto == PPP_CCP) {
+ len = msgdsize(mp);
+ if (mp->b_wptr < mp->b_rptr + len) {
+ PULLUP(mp, len);
+ if (mp == 0)
+ goto bad;
+ }
+ ppp_comp_ccp(q, mp, 1);
+ } else if (proto == PPP_COMP) {
+ if ((cp->flags & CCP_ISUP)
+ && (cp->flags & CCP_DECOMP_RUN) && cp->rstate
+ && (cp->flags & CCP_ERR) == 0) {
+ rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp);
+ switch (rv) {
+ case DECOMP_OK:
+ freemsg(mp);
+ mp = dmp;
+ if (mp == NULL) {
+ /* no error, but no packet returned either. */
+ continue;
+ }
+ break;
+ case DECOMP_ERROR:
+ cp->flags |= CCP_ERROR;
+ ++cp->stats.ppp_ierrors;
+ putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+ break;
+ case DECOMP_FATALERROR:
+ cp->flags |= CCP_FATALERROR;
+ ++cp->stats.ppp_ierrors;
+ putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+ break;
+ }
+ }
+ } else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
+ (*cp->rcomp->incomp)(cp->rstate, mp);
+ }
+
+ /*
+ * Now do VJ decompression.
+ */
+ proto = PPP_PROTOCOL(mp->b_rptr);
+ if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) {
+ len = msgdsize(mp) - PPP_HDRLEN;
+ if ((cp->flags & DECOMP_VJC) == 0 || len <= 0)
+ goto bad;
+
+ /*
+ * Advance past the ppp header.
+ * Here we assume that the whole PPP header is in the first mblk.
+ */
+ np = mp;
+ dp = np->b_rptr + PPP_HDRLEN;
+ if (dp >= mp->b_wptr) {
+ np = np->b_cont;
+ dp = np->b_rptr;
+ }
+
+ /*
+ * Make sure we have sufficient contiguous data at this point.
+ */
+ hlen = (proto == PPP_VJC_COMP)? MAX_VJHDR: MAX_IPHDR;
+ if (hlen > len)
+ hlen = len;
+ if (np->b_wptr < dp + hlen || np->b_datap->db_ref > 1) {
+ PULLUP(mp, hlen + PPP_HDRLEN);
+ if (mp == 0)
+ goto bad;
+ np = mp;
+ dp = np->b_rptr + PPP_HDRLEN;
+ }
+
+ if (proto == PPP_VJC_COMP) {
+ /*
+ * Decompress VJ-compressed packet.
+ * First reset compressor if an input error has occurred.
+ */
+ if (cp->stats.ppp_ierrors != cp->vj_last_ierrors) {
+ if (cp->flags & DBGLOG)
+ DPRINT1("ppp%d: resetting VJ\n", cp->unit);
+ vj_uncompress_err(&cp->vj_comp);
+ cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+ }
+
+ vjlen = vj_uncompress_tcp(dp, np->b_wptr - dp, len,
+ &cp->vj_comp, &iphdr, &iphlen);
+ if (vjlen < 0) {
+ if (cp->flags & DBGLOG)
+ DPRINT2("ppp%d: vj_uncomp_tcp failed, pkt len %d\n",
+ cp->unit, len);
+ ++cp->vj_last_ierrors; /* so we don't reset next time */
+ goto bad;
+ }
+
+ /* drop ppp and vj headers off */
+ if (mp != np) {
+ freeb(mp);
+ mp = np;
+ }
+ mp->b_rptr = dp + vjlen;
+
+ /* allocate a new mblk for the ppp and ip headers */
+ if ((np = allocb(iphlen + PPP_HDRLEN + 4, BPRI_MED)) == 0)
+ goto bad;
+ dp = np->b_rptr; /* prepend mblk with TCP/IP hdr */
+ dp[0] = PPP_ALLSTATIONS; /* reconstruct PPP header */
+ dp[1] = PPP_UI;
+ dp[2] = PPP_IP >> 8;
+ dp[3] = PPP_IP;
+ bcopy((caddr_t)iphdr, (caddr_t)dp + PPP_HDRLEN, iphlen);
+ np->b_wptr = dp + iphlen + PPP_HDRLEN;
+ np->b_cont = mp;
+
+ /* XXX there seems to be a bug which causes panics in strread
+ if we make an mbuf with only the IP header in it :-( */
+ if (mp->b_wptr - mp->b_rptr > 4) {
+ bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, 4);
+ mp->b_rptr += 4;
+ np->b_wptr += 4;
+ } else {
+ bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr,
+ mp->b_wptr - mp->b_rptr);
+ np->b_wptr += mp->b_wptr - mp->b_rptr;
+ np->b_cont = mp->b_cont;
+ freeb(mp);
+ }
+
+ mp = np;
+
+ } else {
+ /*
+ * "Decompress" a VJ-uncompressed packet.
+ */
+ cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+ if (!vj_uncompress_uncomp(dp, hlen, &cp->vj_comp)) {
+ if (cp->flags & DBGLOG)
+ DPRINT2("ppp%d: vj_uncomp_uncomp failed, pkt len %d\n",
+ cp->unit, len);
+ ++cp->vj_last_ierrors; /* don't need to reset next time */
+ goto bad;
+ }
+ mp->b_rptr[3] = PPP_IP; /* fix up the PPP protocol field */
+ }
+ }
+
+ putnext(q, mp);
+ continue;
+
+ bad:
+ if (mp != 0)
+ freemsg(mp);
+ cp->stats.ppp_ierrors++;
+ putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+ }
+
+ return 0;
+}
+
+/*
+ * Handle a CCP packet being sent or received.
+ * Here all the data in the packet is in a single mbuf.
+ */
+static void
+ppp_comp_ccp(q, mp, rcvd)
+ queue_t *q;
+ mblk_t *mp;
+ int rcvd;
+{
+ int len, clen;
+ comp_state_t *cp;
+ unsigned char *dp;
+
+ len = msgdsize(mp);
+ if (len < PPP_HDRLEN + CCP_HDRLEN)
+ return;
+
+ cp = (comp_state_t *) q->q_ptr;
+ dp = mp->b_rptr + PPP_HDRLEN;
+ len -= PPP_HDRLEN;
+ clen = CCP_LENGTH(dp);
+ if (clen > len)
+ return;
+
+ switch (CCP_CODE(dp)) {
+ case CCP_CONFREQ:
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+ cp->flags &= ~CCP_ISUP;
+ break;
+
+ case CCP_CONFACK:
+ if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN
+ && clen >= CCP_HDRLEN + CCP_OPT_MINLEN
+ && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) {
+ if (!rcvd) {
+ if (cp->xstate != NULL
+ && (*cp->xcomp->comp_init)
+ (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
+ cp->unit, 0, ((cp->flags & DBGLOG) != 0)))
+ cp->flags |= CCP_COMP_RUN;
+ } else {
+ if (cp->rstate != NULL
+ && (*cp->rcomp->decomp_init)
+ (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
+ cp->unit, 0, cp->mru, ((cp->flags & DBGLOG) != 0)))
+ cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
+ }
+ }
+ break;
+
+ case CCP_RESETACK:
+ if (cp->flags & CCP_ISUP) {
+ if (!rcvd) {
+ if (cp->xstate && (cp->flags & CCP_COMP_RUN))
+ (*cp->xcomp->comp_reset)(cp->xstate);
+ } else {
+ if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
+ (*cp->rcomp->decomp_reset)(cp->rstate);
+ cp->flags &= ~CCP_ERROR;
+ }
+ }
+ }
+ break;
+ }
+}
+
+#if 0
+dump_msg(mp)
+ mblk_t *mp;
+{
+ dblk_t *db;
+
+ while (mp != 0) {
+ db = mp->b_datap;
+ DPRINT2("mp=%x cont=%x ", mp, mp->b_cont);
+ DPRINT3("rptr=%x wptr=%x datap=%x\n", mp->b_rptr, mp->b_wptr, db);
+ DPRINT2(" base=%x lim=%x", db->db_base, db->db_lim);
+ DPRINT2(" ref=%d type=%d\n", db->db_ref, db->db_type);
+ mp = mp->b_cont;
+ }
+}
+#endif
+
+static int
+msg_byte(mp, i)
+ mblk_t *mp;
+ unsigned int i;
+{
+ while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
+ mp = mp->b_cont;
+ if (mp == 0)
+ return -1;
+ return mp->b_rptr[i];
+}
diff --git a/mdk-stage1/ppp/solaris/ppp_comp_mod.c b/mdk-stage1/ppp/solaris/ppp_comp_mod.c
new file mode 100644
index 000000000..83ff8e252
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/ppp_comp_mod.c
@@ -0,0 +1,81 @@
+/*
+ * ppp_comp_mod.c - modload support for PPP compression STREAMS module.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under Solaris 2.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+
+extern struct streamtab ppp_compinfo;
+
+static struct fmodsw fsw = {
+ "ppp_comp",
+ &ppp_compinfo,
+ D_NEW | D_MP | D_MTQPAIR
+};
+
+extern struct mod_ops mod_strmodops;
+
+static struct modlstrmod modlstrmod = {
+ &mod_strmodops,
+ "PPP compression module",
+ &fsw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *) &modlstrmod,
+ NULL
+};
+
+/*
+ * Entry points for modloading.
+ */
+int
+_init(void)
+{
+ return mod_install(&modlinkage);
+}
+
+int
+_fini(void)
+{
+ return mod_remove(&modlinkage);
+}
+
+int
+_info(mip)
+ struct modinfo *mip;
+{
+ return mod_info(&modlinkage, mip);
+}
diff --git a/mdk-stage1/ppp/solaris/ppp_mod.c b/mdk-stage1/ppp/solaris/ppp_mod.c
new file mode 100644
index 000000000..a4b1538a5
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/ppp_mod.c
@@ -0,0 +1,174 @@
+/*
+ * ppp_mod.c - modload support for PPP pseudo-device driver.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under Solaris 2.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/ksynch.h>
+
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+
+static int ppp_identify __P((dev_info_t *));
+static int ppp_attach __P((dev_info_t *, ddi_attach_cmd_t));
+static int ppp_detach __P((dev_info_t *, ddi_detach_cmd_t));
+static int ppp_devinfo __P((dev_info_t *, ddi_info_cmd_t, void *, void **));
+
+extern struct streamtab pppinfo;
+extern krwlock_t ppp_lower_lock;
+
+static dev_info_t *ppp_dip;
+
+static struct cb_ops cb_ppp_ops = {
+ nulldev, nulldev, nodev, nodev, /* cb_open, ... */
+ nodev, nodev, nodev, nodev, /* cb_dump, ... */
+ nodev, nodev, nodev, nochpoll, /* cb_devmap, ... */
+ ddi_prop_op, /* cb_prop_op */
+ &pppinfo, /* cb_stream */
+ D_NEW|D_MP|D_MTQPAIR|D_MTOUTPERIM|D_MTOCEXCL /* cb_flag */
+};
+
+static struct dev_ops ppp_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ ppp_devinfo, /* devo_getinfo */
+ ppp_identify, /* devo_identify */
+ nulldev, /* devo_probe */
+ ppp_attach, /* devo_attach */
+ ppp_detach, /* devo_detach */
+ nodev, /* devo_reset */
+ &cb_ppp_ops, /* devo_cb_ops */
+ NULL /* devo_bus_ops */
+};
+
+/*
+ * Module linkage information
+ */
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* says this is a pseudo driver */
+ "PPP-2.3 multiplexing driver",
+ &ppp_ops /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *) &modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return mod_install(&modlinkage);
+}
+
+int
+_fini(void)
+{
+ return mod_remove(&modlinkage);
+}
+
+int
+_info(mip)
+ struct modinfo *mip;
+{
+ return mod_info(&modlinkage, mip);
+}
+
+static int
+ppp_identify(dip)
+ dev_info_t *dip;
+{
+ return strcmp(ddi_get_name(dip), "ppp") == 0? DDI_IDENTIFIED:
+ DDI_NOT_IDENTIFIED;
+}
+
+static int
+ppp_attach(dip, cmd)
+ dev_info_t *dip;
+ ddi_attach_cmd_t cmd;
+{
+
+ if (cmd != DDI_ATTACH)
+ return DDI_FAILURE;
+ if (ddi_create_minor_node(dip, "ppp", S_IFCHR, 0, DDI_PSEUDO, CLONE_DEV)
+ == DDI_FAILURE) {
+ ddi_remove_minor_node(dip, NULL);
+ return DDI_FAILURE;
+ }
+ rw_init(&ppp_lower_lock, NULL, RW_DRIVER, NULL);
+ return DDI_SUCCESS;
+}
+
+static int
+ppp_detach(dip, cmd)
+ dev_info_t *dip;
+ ddi_detach_cmd_t cmd;
+{
+ rw_destroy(&ppp_lower_lock);
+ ddi_remove_minor_node(dip, NULL);
+ return DDI_SUCCESS;
+}
+
+static int
+ppp_devinfo(dip, cmd, arg, result)
+ dev_info_t *dip;
+ ddi_info_cmd_t cmd;
+ void *arg;
+ void **result;
+{
+ int error;
+
+ error = DDI_SUCCESS;
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if (ppp_dip == NULL)
+ error = DDI_FAILURE;
+ else
+ *result = (void *) ppp_dip;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = NULL;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return error;
+}
diff --git a/mdk-stage1/ppp/solaris/ppp_mod.h b/mdk-stage1/ppp/solaris/ppp_mod.h
new file mode 100644
index 000000000..f0af00886
--- /dev/null
+++ b/mdk-stage1/ppp/solaris/ppp_mod.h
@@ -0,0 +1,190 @@
+/*
+ * Miscellaneous definitions for PPP STREAMS modules.
+ */
+
+/*
+ * Macros for allocating and freeing kernel memory.
+ */
+#ifdef SVR4 /* SVR4, including Solaris 2 */
+#include <sys/kmem.h>
+#define ALLOC_SLEEP(n) kmem_alloc((n), KM_SLEEP)
+#define ALLOC_NOSLEEP(n) kmem_alloc((n), KM_NOSLEEP)
+#define FREE(p, n) kmem_free((p), (n))
+#endif
+
+#ifdef SUNOS4
+#include <sys/kmem_alloc.h> /* SunOS 4.x */
+#define ALLOC_SLEEP(n) kmem_alloc((n), KMEM_SLEEP)
+#define ALLOC_NOSLEEP(n) kmem_alloc((n), KMEM_NOSLEEP)
+#define FREE(p, n) kmem_free((p), (n))
+#define NOTSUSER() (suser()? 0: EPERM)
+#define bcanputnext(q, band) canputnext((q))
+#endif /* SunOS 4 */
+
+#ifdef __osf__
+#include <sys/malloc.h>
+
+/* caution: this mirrors macros in sys/malloc.h, and uses interfaces
+ * which are subject to change.
+ * The problems are that:
+ * - the official MALLOC macro wants the lhs of the assignment as an argument,
+ * and it takes care of the assignment itself (yuck.)
+ * - PPP insists on using "FREE" which conflicts with a macro of the same name.
+ *
+ */
+#ifdef BUCKETINDX /* V2.0 */
+#define ALLOC_SLEEP(n) (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_WAITOK)
+#define ALLOC_NOSLEEP(n) (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_NOWAIT)
+#else
+#define ALLOC_SLEEP(n) (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_WAITOK)
+#define ALLOC_NOSLEEP(n) (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_NOWAIT)
+#endif
+
+#define bcanputnext(q, band) canputnext((q))
+
+#ifdef FREE
+#undef FREE
+#endif
+#define FREE(p, n) free((void *)(p), M_DEVBUF)
+
+#define NO_DLPI 1
+
+#ifndef IFT_PPP
+#define IFT_PPP 0x17
+#endif
+
+#include <sys/proc.h>
+#define NOTSUSER() (suser(u.u_procp->p_rcred, &u.u_acflag) ? EPERM : 0)
+
+/* #include "ppp_osf.h" */
+
+#endif /* __osf__ */
+
+#ifdef AIX4
+#define ALLOC_SLEEP(n) xmalloc((n), 0, pinned_heap) /* AIX V4.x */
+#define ALLOC_NOSLEEP(n) xmalloc((n), 0, pinned_heap) /* AIX V4.x */
+#define FREE(p, n) xmfree((p), pinned_heap)
+#define NOTSUSER() (suser()? 0: EPERM)
+#endif /* AIX */
+
+/*
+ * Macros for printing debugging stuff.
+ */
+#ifdef DEBUG
+#if defined(SVR4) || defined(__osf__)
+#if defined(SNI)
+#include <sys/strlog.h>
+#define STRLOG_ID 4712
+#define DPRINT(f) strlog(STRLOG_ID, 0, 0, SL_TRACE, f)
+#define DPRINT1(f, a1) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1)
+#define DPRINT2(f, a1, a2) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2)
+#define DPRINT3(f, a1, a2, a3) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2, a3)
+#else
+#define DPRINT(f) cmn_err(CE_CONT, f)
+#define DPRINT1(f, a1) cmn_err(CE_CONT, f, a1)
+#define DPRINT2(f, a1, a2) cmn_err(CE_CONT, f, a1, a2)
+#define DPRINT3(f, a1, a2, a3) cmn_err(CE_CONT, f, a1, a2, a3)
+#endif /* SNI */
+#else
+#define DPRINT(f) printf(f)
+#define DPRINT1(f, a1) printf(f, a1)
+#define DPRINT2(f, a1, a2) printf(f, a1, a2)
+#define DPRINT3(f, a1, a2, a3) printf(f, a1, a2, a3)
+#endif /* SVR4 or OSF */
+
+#else
+#define DPRINT(f) 0
+#define DPRINT1(f, a1) 0
+#define DPRINT2(f, a1, a2) 0
+#define DPRINT3(f, a1, a2, a3) 0
+#endif /* DEBUG */
+
+#ifndef SVR4
+typedef unsigned char uchar_t;
+typedef unsigned short ushort_t;
+#ifndef __osf__
+typedef int minor_t;
+#endif
+#endif
+
+/*
+ * If we don't have multithreading support, define substitutes.
+ */
+#ifndef D_MP
+# define qprocson(q)
+# define qprocsoff(q)
+# define put(q, mp) ((*(q)->q_qinfo->qi_putp)((q), (mp)))
+# define canputnext(q) canput((q)->q_next)
+# define qwriter(q, mp, func, scope) (func)((q), (mp))
+#endif
+
+#ifdef D_MP
+/* Use msgpullup if we have other multithreading support. */
+#define PULLUP(mp, len) \
+ do { \
+ mblk_t *np = msgpullup((mp), (len)); \
+ freemsg((mp)); \
+ mp = np; \
+ } while (0)
+
+#else
+/* Use pullupmsg if we don't have any multithreading support. */
+#define PULLUP(mp, len) \
+ do { \
+ if (!pullupmsg((mp), (len))) { \
+ freemsg((mp)); \
+ mp = 0; \
+ } \
+ } while (0)
+#endif
+
+/*
+ * How to declare the open and close procedures for a module.
+ */
+#ifdef SVR4
+#define MOD_OPEN_DECL(name) \
+static int name __P((queue_t *, dev_t *, int, int, cred_t *))
+
+#define MOD_CLOSE_DECL(name) \
+static int name __P((queue_t *, int, cred_t *))
+
+#define MOD_OPEN(name) \
+static int name(q, devp, flag, sflag, credp) \
+ queue_t *q; \
+ dev_t *devp; \
+ int flag, sflag; \
+ cred_t *credp;
+
+#define MOD_CLOSE(name) \
+static int name(q, flag, credp) \
+ queue_t *q; \
+ int flag; \
+ cred_t *credp;
+
+#define OPEN_ERROR(x) return (x)
+#define DRV_OPEN_OK(dev) return 0
+
+#define NOTSUSER() (drv_priv(credp))
+
+#else /* not SVR4 */
+#define MOD_OPEN_DECL(name) \
+static int name __P((queue_t *, int, int, int))
+
+#define MOD_CLOSE_DECL(name) \
+static int name __P((queue_t *, int))
+
+#define MOD_OPEN(name) \
+static int name(q, dev, flag, sflag) \
+ queue_t *q; \
+ int dev; \
+ int flag, sflag;
+
+#define MOD_CLOSE(name) \
+static int name(q, flag) \
+ queue_t *q; \
+ int flag;
+
+#define OPEN_ERROR(x) { u.u_error = (x); return OPENFAIL; }
+#define DRV_OPEN_OK(dev) return (dev)
+
+#endif /* SVR4 */
diff --git a/mdk-stage1/ppp/sunos4/Makedefs b/mdk-stage1/ppp/sunos4/Makedefs
new file mode 100644
index 000000000..8b56a2b67
--- /dev/null
+++ b/mdk-stage1/ppp/sunos4/Makedefs
@@ -0,0 +1,13 @@
+#
+# defines common to several Makefiles
+#
+
+INSTALL= install -o root -g daemon
+
+BINDIR = /usr/local/etc
+MANDIR = /usr/local/man
+ETCDIR = /etc/ppp
+
+# To use gcc, uncomment the next line.
+#CC = gcc
+COPTS = -O
diff --git a/mdk-stage1/ppp/sunos4/Makefile b/mdk-stage1/ppp/sunos4/Makefile
new file mode 100644
index 000000000..701e24e14
--- /dev/null
+++ b/mdk-stage1/ppp/sunos4/Makefile
@@ -0,0 +1,57 @@
+#
+# Makefile for STREAMS modules for SunOS 4.
+#
+# $Id$
+#
+
+include Makedefs
+
+LD = /usr/bin/ld # make sure we don't get gnu ld
+
+# Defining __$(ARCH)__ is for gcc's broken version of sun/vddrv.h.
+ARCH = `/bin/arch -k`
+DEFINES= -DKERNEL -D_KERNEL -DSUNOS4 -D$(ARCH) -D__$(ARCH)__ \
+ -DDEBUG -DNO_DLPI -DSNIT_SUPPORT
+CFLAGS= $(DEFINES) -I../include $(COPTS)
+
+MODULES= ppp_mod.o ppp_ahdl_mod.o ppp_comp_mod.o if_ppp_mod.o
+
+all: $(MODULES)
+
+ppp_mod.o: ppp.o ppp_vdcmd.o
+ $(LD) -r -o ppp_mod.o ppp.o ppp_vdcmd.o
+
+ppp_ahdl_mod.o: ppp_ahdlc.o ppp_ahdlc_vdcmd.o
+ $(LD) -r -o ppp_ahdl_mod.o ppp_ahdlc.o ppp_ahdlc_vdcmd.o
+
+COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \
+ ppp_comp_vdcmd.o
+ppp_comp_mod.o: $(COMP_OBJS)
+ $(LD) -r -o $@ $(COMP_OBJS)
+
+if_ppp.o: ../modules/if_ppp.c
+ $(CC) $(CFLAGS) -c $?
+bsd-comp.o: ../modules/bsd-comp.c
+ $(CC) $(CFLAGS) -c $?
+deflate.o: ../modules/deflate.c
+ $(CC) $(CFLAGS) -c $?
+ppp.o: ../modules/ppp.c
+ $(CC) $(CFLAGS) -c $?
+ppp_ahdlc.o: ../modules/ppp_ahdlc.c
+ $(CC) $(CFLAGS) -c $?
+ppp_comp.o: ../modules/ppp_comp.c
+ $(CC) $(CFLAGS) -c $?
+vjcompress.o: ../modules/vjcompress.c
+ $(CC) $(CFLAGS) -c $?
+zlib.o: ../common/zlib.c
+ $(CC) $(CFLAGS) -c $?
+
+if_ppp_mod.o: if_ppp.o if_ppp_vdcmd.o
+ $(LD) -r -o if_ppp_mod.o if_ppp.o if_ppp_vdcmd.o
+
+install: all
+ $(INSTALL) $(MODULES) $(BINDIR)
+ ./ppp.INSTALL
+
+clean:
+ rm -f ppp ppp_comp ppp_ahdl *.o *~ core
diff --git a/mdk-stage1/ppp/sunos4/Makefile.top b/mdk-stage1/ppp/sunos4/Makefile.top
new file mode 100644
index 000000000..c86e0884d
--- /dev/null
+++ b/mdk-stage1/ppp/sunos4/Makefile.top
@@ -0,0 +1,46 @@
+#
+# ppp top level makefile
+#
+
+include sunos4/Makedefs
+
+all:
+ cd chat; $(MAKE) all
+ cd pppd; $(MAKE) all
+ cd pppstats; $(MAKE) all
+ cd sunos4; $(MAKE) all
+ cd pppdump; $(MAKE) all
+
+install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp
+
+install-progs:
+ cd chat; $(MAKE) install
+ cd pppd; $(MAKE) install
+ cd pppstats; $(MAKE) install
+ cd pppdump; $(MAKE) install
+ cd sunos4; $(MAKE) install
+
+install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
+ $(ETCDIR)/chap-secrets
+
+$(ETCDIR)/options:
+ $(INSTALL) -c -m 644 etc.ppp/options $@
+$(ETCDIR)/pap-secrets:
+ $(INSTALL) -c -m 600 etc.ppp/pap-secrets $@
+$(ETCDIR)/chap-secrets:
+ $(INSTALL) -c -m 600 etc.ppp/chap-secrets $@
+
+$(BINDIR):
+ $(INSTALL) -d -m 755 $@
+$(MANDIR)/man8:
+ $(INSTALL) -d -m 755 $@
+$(ETCDIR):
+ $(INSTALL) -d -m 755 $@
+
+clean:
+ rm -f *~
+ cd chat; $(MAKE) clean
+ cd pppd; $(MAKE) clean
+ cd pppstats; $(MAKE) clean
+ cd sunos4; $(MAKE) clean
+
diff --git a/mdk-stage1/ppp/sunos4/if_ppp_vdcmd.c b/mdk-stage1/ppp/sunos4/if_ppp_vdcmd.c
new file mode 100644
index 000000000..2bf9710f4
--- /dev/null
+++ b/mdk-stage1/ppp/sunos4/if_ppp_vdcmd.c
@@ -0,0 +1,57 @@
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/conf.h>
+#include <sun/vddrv.h>
+
+extern struct streamtab if_pppinfo;
+
+static struct vdldrv vd = {
+ VDMAGIC_USER,
+ "if_ppp"
+};
+
+static int fmodsw_index = -1;
+
+int
+if_ppp_vdcmd(fun, vdp, vdi, vds)
+ unsigned int fun;
+ struct vddrv *vdp;
+ addr_t vdi;
+ struct vdstat *vds;
+{
+ int n, error;
+
+ switch (fun) {
+ case VDLOAD:
+ vdp->vdd_vdtab = (struct vdlinkage *) &vd;
+ if (fmodsw_index >= 0)
+ return EBUSY;
+ for (n = 0; n < fmodcnt; ++n)
+ if (fmodsw[n].f_str == 0)
+ break;
+ if (n >= fmodcnt)
+ return ENODEV;
+ strncpy(fmodsw[n].f_name, vd.Drv_name, FMNAMESZ+1);
+ fmodsw[n].f_str = &if_pppinfo;
+ fmodsw_index = n;
+ break;
+
+ case VDUNLOAD:
+ if (fmodsw_index <= 0)
+ return EINVAL;
+ error = if_ppp_unload();
+ if (error != 0)
+ return error;
+ fmodsw[fmodsw_index].f_name[0] = 0;
+ fmodsw[fmodsw_index].f_str = 0;
+ fmodsw_index = -1;
+ break;
+
+ case VDSTAT:
+ break;
+
+ default:
+ return EIO;
+ }
+ return 0;
+}
diff --git a/mdk-stage1/ppp/sunos4/ppp.INSTALL b/mdk-stage1/ppp/sunos4/ppp.INSTALL
new file mode 100755
index 000000000..0018bf8d0
--- /dev/null
+++ b/mdk-stage1/ppp/sunos4/ppp.INSTALL
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+# Script for loading, unloading, etc. ppp modules.
+
+moddir=/usr/local/etc
+etcppp=/etc/ppp
+
+PATH=/usr/etc:/usr/bin
+
+# Check that we're superuser
+touch /tmp/su$$
+if chown root /tmp/su$$ >/dev/null; then :
+else
+ echo "$0: must be root."
+ rm -f /tmp/su$$
+ exit 1
+fi
+rm -f /tmp/su$$
+
+case "$0" in
+*ppp.INSTALL)
+ if [ ! -f ppp.INSTALL ]; then
+ echo "ppp.INSTALL: not found"
+ exit 1
+ fi
+ for n in INSTALL LOAD UNLOAD MKDEV RMDEV; do
+ if [ -h /dev/ppp.$n -o -f /dev/ppp.$n ]; then
+ rm /dev/ppp.$n
+ fi
+ done
+ cp ppp.INSTALL /dev
+ for n in LOAD UNLOAD MKDEV RMDEV; do
+ ln -s ppp.INSTALL /dev/ppp.$n
+ done
+ ;;
+
+*ppp.LOAD)
+ if modstat | grep -w ppp >/dev/null; then
+ echo "ppp driver is already loaded."
+ exit 1
+ fi
+ if modstat | grep -w if_ppp >/dev/null; then
+ echo "if_ppp module already loaded: not reloading."
+ else
+ echo -n "if_ppp: "
+ modload $moddir/if_ppp_mod.o -sym -entry _if_ppp_vdcmd \
+ -o $etcppp/if_ppp_mod
+ fi
+ echo -n "ppp: "
+ modload $moddir/ppp_mod.o -sym -entry _ppp_vdcmd -exec /dev/ppp.MKDEV \
+ -o $etcppp/ppp_mod
+ echo -n "ppp_comp: "
+ modload $moddir/ppp_comp_mod.o -sym -entry _ppp_comp_vdcmd \
+ -o $etcppp/ppp_comp
+ echo -n "ppp_ahdl: "
+ modload $moddir/ppp_ahdl_mod.o -sym -entry _ppp_ahdlc_vdcmd \
+ -o $etcppp/ppp_ahdl
+ exit 0
+ ;;
+
+*ppp.MKDEV)
+ # args: module number, type, b-major, c-major
+ if [ $# -ne 4 ]; then
+ echo "Usage: $0 module-id module-type b-major c-major"
+ exit 1
+ fi
+ if [ "$2" -ne "12345607" -a "$2" -ne "12345600" ]; then
+ echo "$0: $2: bad module type"
+ exit 1
+ fi
+ rm -f /dev/ppp
+ # we "just know" that 37 is the major number of the clone driver
+ mknod /dev/ppp c 37 $4
+ chmod 644 /dev/ppp
+ exit 0
+ ;;
+
+*ppp.UNLOAD)
+ stat=0
+ if modstat | grep -w if_ppp >/dev/null; then
+ echo "$0: not unloading if_ppp module."
+ fi
+ for mod in ppp ppp_comp ppp_ahdl; do
+ id=`modstat | grep -w $mod | awk '{print $1}'`
+ if [ x$id = x ]; then
+ echo "$mod is not loaded."
+ stat=1
+ else
+ modunload -id $id
+ fi
+ done
+ exit $stat
+ ;;
+
+*ppp.RMDEV)
+ rm -f /dev/ppp
+ exit 0
+ ;;
+
+*)
+ echo "Invocation names: ppp.INSTALL ppp.LOAD ppp.UNLOAD ppp.MKDEV ppp.RMDEV"
+ exit 1
+ ;;
+esac
diff --git a/mdk-stage1/ppp/sunos4/ppp_ahdlc_vdcmd.c b/mdk-stage1/ppp/sunos4/ppp_ahdlc_vdcmd.c
new file mode 100644
index 000000000..2dbe8262b
--- /dev/null
+++ b/mdk-stage1/ppp/sunos4/ppp_ahdlc_vdcmd.c
@@ -0,0 +1,57 @@
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/conf.h>
+#include <sun/vddrv.h>
+
+extern struct streamtab ppp_ahdlcinfo;
+extern int ppp_ahdlc_count;
+
+static struct vdldrv vd = {
+ VDMAGIC_USER,
+ "ppp_ahdl"
+};
+
+static int fmodsw_index = -1;
+
+int
+ppp_ahdlc_vdcmd(fun, vdp, vdi, vds)
+ unsigned int fun;
+ struct vddrv *vdp;
+ addr_t vdi;
+ struct vdstat *vds;
+{
+ int n;
+
+ switch (fun) {
+ case VDLOAD:
+ vdp->vdd_vdtab = (struct vdlinkage *) &vd;
+ if (fmodsw_index >= 0)
+ return EBUSY;
+ for (n = 0; n < fmodcnt; ++n)
+ if (fmodsw[n].f_str == 0)
+ break;
+ if (n >= fmodcnt)
+ return ENODEV;
+ strncpy(fmodsw[n].f_name, vd.Drv_name, FMNAMESZ+1);
+ fmodsw[n].f_str = &ppp_ahdlcinfo;
+ fmodsw_index = n;
+ break;
+
+ case VDUNLOAD:
+ if (ppp_ahdlc_count > 0)
+ return EBUSY;
+ if (fmodsw_index <= 0)
+ return EINVAL;
+ fmodsw[fmodsw_index].f_name[0] = 0;
+ fmodsw[fmodsw_index].f_str = 0;
+ fmodsw_index = -1;
+ break;
+
+ case VDSTAT:
+ break;
+
+ default:
+ return EIO;
+ }
+ return 0;
+}
diff --git a/mdk-stage1/ppp/sunos4/ppp_comp_vdcmd.c b/mdk-stage1/ppp/sunos4/ppp_comp_vdcmd.c
new file mode 100644
index 000000000..b81bc47e8
--- /dev/null
+++ b/mdk-stage1/ppp/sunos4/ppp_comp_vdcmd.c
@@ -0,0 +1,57 @@
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/conf.h>
+#include <sun/vddrv.h>
+
+extern struct streamtab ppp_compinfo;
+extern int ppp_comp_count;
+
+static struct vdldrv vd = {
+ VDMAGIC_USER,
+ "ppp_comp"
+};
+
+static int fmodsw_index = -1;
+
+int
+ppp_comp_vdcmd(fun, vdp, vdi, vds)
+ unsigned int fun;
+ struct vddrv *vdp;
+ addr_t vdi;
+ struct vdstat *vds;
+{
+ int n;
+
+ switch (fun) {
+ case VDLOAD:
+ vdp->vdd_vdtab = (struct vdlinkage *) &vd;
+ if (fmodsw_index >= 0)
+ return EBUSY;
+ for (n = 0; n < fmodcnt; ++n)
+ if (fmodsw[n].f_str == 0)
+ break;
+ if (n >= fmodcnt)
+ return ENODEV;
+ strncpy(fmodsw[n].f_name, vd.Drv_name, FMNAMESZ+1);
+ fmodsw[n].f_str = &ppp_compinfo;
+ fmodsw_index = n;
+ break;
+
+ case VDUNLOAD:
+ if (ppp_comp_count > 0)
+ return EBUSY;
+ if (fmodsw_index <= 0)
+ return EINVAL;
+ fmodsw[fmodsw_index].f_name[0] = 0;
+ fmodsw[fmodsw_index].f_str = 0;
+ fmodsw_index = -1;
+ break;
+
+ case VDSTAT:
+ break;
+
+ default:
+ return EIO;
+ }
+ return 0;
+}
diff --git a/mdk-stage1/ppp/sunos4/ppp_vdcmd.c b/mdk-stage1/ppp/sunos4/ppp_vdcmd.c
new file mode 100644
index 000000000..68095c92e
--- /dev/null
+++ b/mdk-stage1/ppp/sunos4/ppp_vdcmd.c
@@ -0,0 +1,81 @@
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/conf.h>
+#include <sun/vddrv.h>
+
+extern struct streamtab pppinfo;
+extern int ppp_count;
+extern int nchrdev;
+
+static struct vdldrv vd = {
+ VDMAGIC_PSEUDO,
+ "ppp"
+};
+
+extern int nodev();
+
+static struct cdevsw ppp_cdevsw = {
+ nodev, nodev, nodev, nodev, nodev, nodev, nodev, 0,
+ &pppinfo
+};
+
+static struct cdevsw old_entry;
+
+int
+ppp_vdcmd(fun, vdp, vdi, vds)
+ unsigned int fun;
+ struct vddrv *vdp;
+ addr_t vdi;
+ struct vdstat *vds;
+{
+ static int majnum = -1;
+ int n, maj;
+
+ switch (fun) {
+ case VDLOAD:
+ /*
+ * It seems like modload doesn't install the cdevsw entry
+ * for us. Oh well...
+ */
+ for (maj = 1; maj < nchrdev; ++maj)
+ if (cdevsw[maj].d_open == vd_unuseddev)
+ break;
+ if (maj >= nchrdev)
+ return ENODEV;
+ vd.Drv_charmajor = maj;
+ old_entry = cdevsw[maj];
+ cdevsw[maj] = ppp_cdevsw;
+ vd.Drv_cdevsw = &ppp_cdevsw;
+ vdp->vdd_vdtab = (struct vdlinkage *) &vd;
+ majnum = maj;
+ break;
+
+ case VDUNLOAD:
+ if (ppp_count > 0)
+ return EBUSY;
+ if (vd.Drv_charmajor > 0)
+ cdevsw[vd.Drv_charmajor] = old_entry;
+ break;
+
+ case VDSTAT:
+ /*
+ * We have to fool the modstat command into thinking
+ * that this module is actually a driver! This is
+ * so that installation commands that use the -exec
+ * option of modload to run a shell script find out
+ * the block and/or char major numbers of the driver
+ * loaded (so that the shell script can go off to
+ * /dev and *MAKE* the bloody device nodes- remember
+ * they might change from one load to another if
+ * you don't hardwire the number!).
+ */
+ vds->vds_magic = VDMAGIC_DRV;
+ vds->vds_modinfo[0] = (char) 0;
+ vds->vds_modinfo[1] = (char) majnum;
+ break;
+
+ default:
+ return EIO;
+ }
+ return 0;
+}
diff --git a/mdk-stage1/ppp/svr4/Makedefs b/mdk-stage1/ppp/svr4/Makedefs
new file mode 100644
index 000000000..81db8ab2e
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/Makedefs
@@ -0,0 +1,16 @@
+#
+# defines common to several Makefiles
+#
+
+INSTALL= /usr/sbin/install
+
+BINDIR = /usr/local/bin
+MANDIR = /usr/local/man
+ETCDIR = /etc/ppp
+
+COPTS = -O -Xa
+
+# For compiling with gcc, comment out the COPTS definition above and
+# uncomment the next 2 definitions.
+#CC = gcc
+#COPTS = -O2
diff --git a/mdk-stage1/ppp/svr4/Makedefs.sol2 b/mdk-stage1/ppp/svr4/Makedefs.sol2
new file mode 100644
index 000000000..e8b8d282e
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/Makedefs.sol2
@@ -0,0 +1,59 @@
+#
+# Generic make definitions for Solaris 2
+#
+# $Id$
+#
+
+include ../svr4/Makedefs
+
+CPPFLAGS = -D_KERNEL -DSVR4 -DSOL2 -DPRIOQ -DDEBUG -I../include
+CFLAGS = $(CPPFLAGS) $(COPTS)
+
+# lint-specific variables
+LINT = lint
+LINT_OPT_32 =
+LINT_OPT_64 = -Xarch=v9 -errchk=longptr64
+
+LINT_32 =
+LINT_32 += -erroff=E_BAD_PTR_CAST_ALIGN
+LINT_32 += -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED
+LINT_32 += -erroff=E_SUSPICIOUS_COMPARISON
+LINT_32 += -erroff=E_CAST_UINT_TO_SIGNED_INT
+LINT_32 += -erroff=E_PASS_UINT_TO_SIGNED_INT
+LINT_32 += -erroff=E_INVALID_ANNOTATION_NAME
+LINT_32 += -erroff=E_FUNC_ARG_UNUSED
+# This might be needed, but zlib.c and vjcompress.c will squawk
+# when not ignored
+LINT_32 += -erroff=E_CASE_FALLTHRU
+LINT_32 += -erroff=E_RET_INT_IMPLICITLY
+LINT_32 += -erroff=E_FUNC_NO_RET_VAL
+# Some STREAMS macros will be noisy too when this isn't ignored
+LINT_32 += -erroff=E_CONSTANT_CONDITION
+LINT_32 += -erroff=E_CONST_EXPR
+
+# Extra noise suppressant for 64-bit
+EXTRA_OFF =
+EXTRA_OFF += -erroff=E_CAST_INT_TO_SMALL_INT
+EXTRA_OFF += -erroff=E_CAST_INT_CONST_TO_SMALL_INT
+EXTRA_OFF += -erroff=E_CAST_TO_PTR_FROM_INT
+EXTRA_OFF += -erroff=E_ASSIGN_INT_TO_SMALL_INT
+EXTRA_OFF += -erroff=E_ASSIGN_INT_FROM_BIG_CONST
+EXTRA_OFF += -erroff=E_CONST_PROMOTED_UNSIGNED_LL
+EXTRA_OFF += -erroff=E_CONST_PROMOTED_LONG_LONG
+EXTRA_OFF += -erroff=E_CONST_TRUNCATED_BY_ASSIGN
+EXTRA_OFF += -erroff=E_PASS_INT_FROM_BIG_CONST
+EXTRA_OFF += -erroff=E_COMP_INT_WITH_LARGE_INT
+EXTRA_OFF += -erroff=E_ASSIGN_UINT_TO_SIGNED_INT
+EXTRA_OFF += -erroff=E_ASSIGN_NARROW_CONV
+EXTRA_OFF += -erroff=E_PASS_INT_TO_SMALL_INT
+EXTRA_OFF += -erroff=E_PTR_CONV_LOSES_BITS
+
+LINT_64 = $(LINT_32)
+LINT_64 += $(EXTRA_OFF)
+
+LINTFLAGS64 = -Xa -nsxmuF -errtags=yes $(LINT_OPT_64) $(LINT_64)
+LINT64 = $(LINT) -c $(LINTFLAGS64) $(CPPFLAGS)
+
+LINTFLAGS32 = -Xa -nsxmuF -errtags=yes $(LINT_OPT_32) $(LINT_32)
+LINT32 = $(LINT) -c $(LINTFLAGS32) $(CPPFLAGS)
+
diff --git a/mdk-stage1/ppp/svr4/Makefile.sol2 b/mdk-stage1/ppp/svr4/Makefile.sol2
new file mode 100644
index 000000000..5b2ca8635
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/Makefile.sol2
@@ -0,0 +1,66 @@
+#
+# Makefile for STREAMS modules for Solaris 2.
+#
+# $Id$
+#
+
+include Makedefs.sol2
+
+COPTS += -xO2 -xspace -W0,-Lt
+
+COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \
+ ppp_comp_mod.o
+
+all: ppp ppp_ahdl ppp_comp
+
+ppp: ppp.o ppp_mod.o
+ ld -r -o $@ ppp.o ppp_mod.o
+ chmod +x $@
+
+ppp_ahdl: ppp_ahdlc.o ppp_ahdlc_mod.o
+ ld -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o
+ chmod +x $@
+
+ppp_comp: $(COMP_OBJS)
+ ld -r -o $@ $(COMP_OBJS)
+ chmod +x $@
+
+bsd-comp.o: ../modules/bsd-comp.c
+ $(CC) $(CFLAGS) -c $?
+deflate.o: ../modules/deflate.c
+ $(CC) $(CFLAGS) -c $?
+ppp.o: ../modules/ppp.c
+ $(CC) $(CFLAGS) -c $?
+ppp_mod.o: ppp_mod.c
+ $(CC) $(CFLAGS) -c $?
+ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+ $(CC) $(CFLAGS) -c $?
+ppp_ahdlc.o: ../modules/ppp_ahdlc.c
+ $(CC) $(CFLAGS) -c $?
+ppp_comp.o: ../modules/ppp_comp.c
+ $(CC) $(CFLAGS) -c $?
+ppp_comp_mod.o: ppp_comp_mod.c
+ $(CC) $(CFLAGS) -c $?
+vjcompress.o: ../modules/vjcompress.c
+ $(CC) $(CFLAGS) -c $?
+zlib.o: ../common/zlib.c
+ $(CC) $(CFLAGS) -c $?
+
+install:
+ cp ppp ppp.conf /kernel/drv
+ cp ppp_comp ppp_ahdl /kernel/strmod
+ if grep clone:ppp /etc/minor_perm; then :; else \
+ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+ /usr/sbin/rem_drv ppp 2>/dev/null || true
+ /usr/sbin/add_drv ppp
+
+SRCS = ../modules/ppp.c ppp_mod.c ../modules/ppp_ahdlc.c ppp_ahdlc_mod.c \
+ ../modules/ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+ $(LINT32) $(SRCS)
+
+clean:
+ rm -f ppp ppp_comp ppp_ahdl *.o *~ core
+ rm -f *.ln
diff --git a/mdk-stage1/ppp/svr4/Makefile.sol2-64 b/mdk-stage1/ppp/svr4/Makefile.sol2-64
new file mode 100644
index 000000000..80c6b185e
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/Makefile.sol2-64
@@ -0,0 +1,85 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2.
+#
+# $Id$
+#
+
+include Makedefs.sol2
+
+# Sun's cc flag for LP64 compilation / linkage
+COPTS += -xchip=ultra -xarch=v9 -Wc,-xcode=abs32 -Wc,-Qiselect-regsym=0 -xO3 -xspace -W0,-Lt
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR = sparcv9
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE = Makefile.sol2
+
+COMP_OBJS = $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+ $(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+ $(LP64DIR)/ppp_comp_mod.o
+
+all: std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+ $(MAKE) -f $(STD_MAKE) all
+
+ppp: $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+ ld -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+ chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+ ld -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+ chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+ ld -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+ chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o: ../modules/ppp.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o: ppp_mod.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ../modules/ppp_ahdlc.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ../modules/ppp_comp.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+ $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o: ../common/zlib.c
+ $(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+ mkdir -m 755 -p $@
+
+install:
+ cp ppp ppp.conf /kernel/drv
+ cp ppp_comp ppp_ahdl /kernel/strmod
+ cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+ cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+ if grep clone:ppp /etc/minor_perm; then :; else \
+ echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+ /usr/sbin/rem_drv ppp 2>/dev/null || true
+ /usr/sbin/add_drv ppp
+
+SRCS = ../modules/ppp.c ppp_mod.c ../modules/ppp_ahdlc.c ppp_ahdlc_mod.c \
+ ../modules/ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+ ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+ $(LINT64) $(SRCS)
+
+lint-32:
+ $(LINT32) $(SRCS)
+
+clean:
+ $(MAKE) -f $(STD_MAKE) clean
+ rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff --git a/mdk-stage1/ppp/svr4/Makefile.svr4 b/mdk-stage1/ppp/svr4/Makefile.svr4
new file mode 100644
index 000000000..252c52428
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/Makefile.svr4
@@ -0,0 +1,60 @@
+#
+# Makefile for STREAMS modules for SVR4.
+#
+# $Id$
+#
+
+COPTS = -O
+
+CFLAGS= -D_KERNEL -DSVR4 -DLACHTCP -I../include $(COPTS)
+
+all: ppp ppp_ahdl ppp_comp
+
+ppp: ppp.o
+ ld -r -o $@ ppp.o
+
+ppp_ahdl: ppp_ahdlc.o
+ ld -r -o $@ ppp_ahdlc.o
+
+ppp_comp: ppp_comp.o bsd-comp.o vjcompress.o deflate.o zlib.o
+ ld -r -o $@ ppp_comp.o bsd-comp.o vjcompress.o deflate.o zlib.o
+
+bsd-comp.o: ../modules/bsd-comp.c
+ $(CC) $(CFLAGS) -c $?
+deflate.o: ../modules/deflate.c
+ $(CC) $(CFLAGS) -c $?
+ppp.o: ../modules/ppp.c
+ $(CC) $(CFLAGS) -c $?
+ppp_ahdlc.o: ../modules/ppp_ahdlc.c
+ $(CC) $(CFLAGS) -c $?
+ppp_comp.o: ../modules/ppp_comp.c
+ $(CC) $(CFLAGS) -c $?
+vjcompress.o: ../modules/vjcompress.c
+ $(CC) $(CFLAGS) -c $?
+zlib.o: ../common/zlib.c
+ $(CC) $(CFLAGS) -c $?
+
+install: all
+ cp ppp Driver.o
+ cp ppp.Master Master
+ cp ppp.System System
+ cp ppp.Node Node
+ /etc/conf/bin/idinstall -d ppp
+ /etc/conf/bin/idinstall -a ppp
+ cp ppp_comp Driver.o
+ cp ppp_comp.Master Master
+ cp ppp_comp.System System
+ /etc/conf/bin/idinstall -d ppp_comp
+ /etc/conf/bin/idinstall -a ppp_comp
+ cp ppp_ahdl Driver.o
+ cp ppp_ahdl.Master Master
+ cp ppp_ahdl.System System
+ /etc/conf/bin/idinstall -d ppp_ahdl
+ /etc/conf/bin/idinstall -a ppp_ahdl
+ @echo
+ @echo 'NOTE: You must rebuild your kernel to incorporate the driver.'
+ @echo '(use /etc/conf/bin/idbuild)'
+ @echo
+
+clean:
+ rm -f ppp ppp_comp ppp_ahdl *.o *~ core
diff --git a/mdk-stage1/ppp/svr4/Makefile.top b/mdk-stage1/ppp/svr4/Makefile.top
new file mode 100644
index 000000000..253e48acb
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/Makefile.top
@@ -0,0 +1,50 @@
+#
+# ppp top level makefile for SVR4 and Solaris 2
+#
+# $Id$
+#
+
+include svr4/Makedefs
+
+all:
+ cd chat; $(MAKE) all
+ cd pppd; $(MAKE) all
+ cd pppstats; $(MAKE) all
+ cd pppdump; $(MAKE) all
+ cd svr4; $(MAKE) all
+
+install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp
+
+install-progs:
+ cd chat; $(MAKE) install
+ cd pppd; $(MAKE) install
+ cd pppstats; $(MAKE) install
+ cd pppdump; $(MAKE) install
+ cd svr4; $(MAKE) install
+
+install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
+ $(ETCDIR)/chap-secrets
+
+$(ETCDIR)/options:
+ cp etc.ppp/options $@
+ chmod go-w $@
+$(ETCDIR)/pap-secrets:
+ $(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/pap-secrets
+$(ETCDIR)/chap-secrets:
+ $(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/chap-secrets
+
+$(BINDIR):
+ mkdir -m 755 -p $@
+$(MANDIR)/man8:
+ mkdir -m 755 -p $@
+$(ETCDIR):
+ mkdir -m 755 -p $@
+
+clean:
+ rm -f *~
+ cd chat; $(MAKE) clean
+ cd pppd; $(MAKE) clean
+ cd pppstats; $(MAKE) clean
+ cd pppdump; $(MAKE) clean
+ cd svr4; $(MAKE) clean
+
diff --git a/mdk-stage1/ppp/svr4/ppp.Master b/mdk-stage1/ppp/svr4/ppp.Master
new file mode 100644
index 000000000..346db035b
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp.Master
@@ -0,0 +1 @@
+ppp - Sciof ppp 0 0 1 128 -1
diff --git a/mdk-stage1/ppp/svr4/ppp.Node b/mdk-stage1/ppp/svr4/ppp.Node
new file mode 100644
index 000000000..7767ade71
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp.Node
@@ -0,0 +1 @@
+clone ppp c ppp
diff --git a/mdk-stage1/ppp/svr4/ppp.System b/mdk-stage1/ppp/svr4/ppp.System
new file mode 100644
index 000000000..e60c0eec3
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp.System
@@ -0,0 +1 @@
+ppp Y 1 0 0 0 0 0 0 0
diff --git a/mdk-stage1/ppp/svr4/ppp.conf b/mdk-stage1/ppp/svr4/ppp.conf
new file mode 100644
index 000000000..e443a7aac
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp.conf
@@ -0,0 +1 @@
+name="ppp" parent="pseudo" instance=0;
diff --git a/mdk-stage1/ppp/svr4/ppp_ahdl.Master b/mdk-stage1/ppp/svr4/ppp_ahdl.Master
new file mode 100644
index 000000000..4fde52596
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp_ahdl.Master
@@ -0,0 +1 @@
+ppp_ahdl - iSf phdl 0 0 1 1 -1
diff --git a/mdk-stage1/ppp/svr4/ppp_ahdl.System b/mdk-stage1/ppp/svr4/ppp_ahdl.System
new file mode 100644
index 000000000..f41a500f4
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp_ahdl.System
@@ -0,0 +1 @@
+ppp_ahdl Y 1 0 0 0 0 0 0 0
diff --git a/mdk-stage1/ppp/svr4/ppp_ahdlc_mod.c b/mdk-stage1/ppp/svr4/ppp_ahdlc_mod.c
new file mode 100644
index 000000000..f81be8abb
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp_ahdlc_mod.c
@@ -0,0 +1,49 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+
+extern struct streamtab ppp_ahdlcinfo;
+
+static struct fmodsw fsw = {
+ "ppp_ahdl",
+ &ppp_ahdlcinfo,
+ D_NEW | D_MP | D_MTQPAIR
+};
+
+extern struct mod_ops mod_strmodops;
+
+static struct modlstrmod modlstrmod = {
+ &mod_strmodops,
+ "PPP async HDLC module",
+ &fsw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *) &modlstrmod,
+ NULL
+};
+
+/*
+ * Entry points for modloading.
+ */
+int
+_init(void)
+{
+ return mod_install(&modlinkage);
+}
+
+int
+_fini(void)
+{
+ return mod_remove(&modlinkage);
+}
+
+int
+_info(mip)
+ struct modinfo *mip;
+{
+ return mod_info(&modlinkage, mip);
+}
diff --git a/mdk-stage1/ppp/svr4/ppp_comp.Master b/mdk-stage1/ppp/svr4/ppp_comp.Master
new file mode 100644
index 000000000..78019064e
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp_comp.Master
@@ -0,0 +1 @@
+ppp_comp - iSf pcmp 0 0 1 1 -1
diff --git a/mdk-stage1/ppp/svr4/ppp_comp.System b/mdk-stage1/ppp/svr4/ppp_comp.System
new file mode 100644
index 000000000..e69d4a1a3
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp_comp.System
@@ -0,0 +1 @@
+ppp_comp Y 1 0 0 0 0 0 0 0
diff --git a/mdk-stage1/ppp/svr4/ppp_comp_mod.c b/mdk-stage1/ppp/svr4/ppp_comp_mod.c
new file mode 100644
index 000000000..83ff8e252
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp_comp_mod.c
@@ -0,0 +1,81 @@
+/*
+ * ppp_comp_mod.c - modload support for PPP compression STREAMS module.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under Solaris 2.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+
+extern struct streamtab ppp_compinfo;
+
+static struct fmodsw fsw = {
+ "ppp_comp",
+ &ppp_compinfo,
+ D_NEW | D_MP | D_MTQPAIR
+};
+
+extern struct mod_ops mod_strmodops;
+
+static struct modlstrmod modlstrmod = {
+ &mod_strmodops,
+ "PPP compression module",
+ &fsw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *) &modlstrmod,
+ NULL
+};
+
+/*
+ * Entry points for modloading.
+ */
+int
+_init(void)
+{
+ return mod_install(&modlinkage);
+}
+
+int
+_fini(void)
+{
+ return mod_remove(&modlinkage);
+}
+
+int
+_info(mip)
+ struct modinfo *mip;
+{
+ return mod_info(&modlinkage, mip);
+}
diff --git a/mdk-stage1/ppp/svr4/ppp_mod.c b/mdk-stage1/ppp/svr4/ppp_mod.c
new file mode 100644
index 000000000..a4b1538a5
--- /dev/null
+++ b/mdk-stage1/ppp/svr4/ppp_mod.c
@@ -0,0 +1,174 @@
+/*
+ * ppp_mod.c - modload support for PPP pseudo-device driver.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+/*
+ * This file is used under Solaris 2.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/ksynch.h>
+
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+
+static int ppp_identify __P((dev_info_t *));
+static int ppp_attach __P((dev_info_t *, ddi_attach_cmd_t));
+static int ppp_detach __P((dev_info_t *, ddi_detach_cmd_t));
+static int ppp_devinfo __P((dev_info_t *, ddi_info_cmd_t, void *, void **));
+
+extern struct streamtab pppinfo;
+extern krwlock_t ppp_lower_lock;
+
+static dev_info_t *ppp_dip;
+
+static struct cb_ops cb_ppp_ops = {
+ nulldev, nulldev, nodev, nodev, /* cb_open, ... */
+ nodev, nodev, nodev, nodev, /* cb_dump, ... */
+ nodev, nodev, nodev, nochpoll, /* cb_devmap, ... */
+ ddi_prop_op, /* cb_prop_op */
+ &pppinfo, /* cb_stream */
+ D_NEW|D_MP|D_MTQPAIR|D_MTOUTPERIM|D_MTOCEXCL /* cb_flag */
+};
+
+static struct dev_ops ppp_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ ppp_devinfo, /* devo_getinfo */
+ ppp_identify, /* devo_identify */
+ nulldev, /* devo_probe */
+ ppp_attach, /* devo_attach */
+ ppp_detach, /* devo_detach */
+ nodev, /* devo_reset */
+ &cb_ppp_ops, /* devo_cb_ops */
+ NULL /* devo_bus_ops */
+};
+
+/*
+ * Module linkage information
+ */
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* says this is a pseudo driver */
+ "PPP-2.3 multiplexing driver",
+ &ppp_ops /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *) &modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return mod_install(&modlinkage);
+}
+
+int
+_fini(void)
+{
+ return mod_remove(&modlinkage);
+}
+
+int
+_info(mip)
+ struct modinfo *mip;
+{
+ return mod_info(&modlinkage, mip);
+}
+
+static int
+ppp_identify(dip)
+ dev_info_t *dip;
+{
+ return strcmp(ddi_get_name(dip), "ppp") == 0? DDI_IDENTIFIED:
+ DDI_NOT_IDENTIFIED;
+}
+
+static int
+ppp_attach(dip, cmd)
+ dev_info_t *dip;
+ ddi_attach_cmd_t cmd;
+{
+
+ if (cmd != DDI_ATTACH)
+ return DDI_FAILURE;
+ if (ddi_create_minor_node(dip, "ppp", S_IFCHR, 0, DDI_PSEUDO, CLONE_DEV)
+ == DDI_FAILURE) {
+ ddi_remove_minor_node(dip, NULL);
+ return DDI_FAILURE;
+ }
+ rw_init(&ppp_lower_lock, NULL, RW_DRIVER, NULL);
+ return DDI_SUCCESS;
+}
+
+static int
+ppp_detach(dip, cmd)
+ dev_info_t *dip;
+ ddi_detach_cmd_t cmd;
+{
+ rw_destroy(&ppp_lower_lock);
+ ddi_remove_minor_node(dip, NULL);
+ return DDI_SUCCESS;
+}
+
+static int
+ppp_devinfo(dip, cmd, arg, result)
+ dev_info_t *dip;
+ ddi_info_cmd_t cmd;
+ void *arg;
+ void **result;
+{
+ int error;
+
+ error = DDI_SUCCESS;
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if (ppp_dip == NULL)
+ error = DDI_FAILURE;
+ else
+ *result = (void *) ppp_dip;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = NULL;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return error;
+}