From c5e4c2cfc092317c1e3ceb0f654f880f6a8573f4 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Tue, 16 Dec 2003 07:07:39 +0000 Subject: [PATCH] Initial revision --- .cvsignore | 5 + .cvsnotify | 1 + INSTALL | 72 + Makefile.in | 70 + arp.c | 56 + bounce.c | 90 + cli.c | 944 ++++++ cluster.c | 85 + cluster.h | 18 + cluster_master.c | 480 +++ cluster_slave.c | 249 ++ config.h.in | 213 ++ configure | 7102 ++++++++++++++++++++++++++++++++++++++++ configure.in | 48 + conform.cfg | 137 + constants.c | 136 + constants.h | 27 + control.c | 70 + control.h | 18 + etc/.cvsignore | 2 + etc/ip_pool.default | 2 + etc/l2tpns.cfg.default | 43 + etc/l2tpns.logrotate | 8 + etc/users.default | 1 + garden.c | 172 + install-sh | 238 ++ l2tpns.c | 2988 +++++++++++++++++ l2tpns.h | 393 +++ ll.c | 141 + ll.h | 28 + machines.cfg | 39 + md5.c | 349 ++ md5.h | 72 + nsctl.c | 138 + plugin.h | 116 + ppp.c | 725 ++++ radius.c | 614 ++++ rl.c | 158 + stamp-h | 1 + throttle.c | 73 + util.c | 16 + util.h | 1 + 42 files changed, 16139 insertions(+) create mode 100644 .cvsignore create mode 100644 .cvsnotify create mode 100644 INSTALL create mode 100644 Makefile.in create mode 100644 arp.c create mode 100644 bounce.c create mode 100644 cli.c create mode 100644 cluster.c create mode 100644 cluster.h create mode 100644 cluster_master.c create mode 100644 cluster_slave.c create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.in create mode 100644 conform.cfg create mode 100644 constants.c create mode 100644 constants.h create mode 100644 control.c create mode 100644 control.h create mode 100644 etc/.cvsignore create mode 100644 etc/ip_pool.default create mode 100644 etc/l2tpns.cfg.default create mode 100644 etc/l2tpns.logrotate create mode 100644 etc/users.default create mode 100644 garden.c create mode 100755 install-sh create mode 100644 l2tpns.c create mode 100644 l2tpns.h create mode 100644 ll.c create mode 100644 ll.h create mode 100644 machines.cfg create mode 100644 md5.c create mode 100644 md5.h create mode 100644 nsctl.c create mode 100644 plugin.h create mode 100644 ppp.c create mode 100644 radius.c create mode 100644 rl.c create mode 100644 stamp-h create mode 100644 throttle.c create mode 100644 util.c create mode 100644 util.h diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..9a84440 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,5 @@ +*.o +l2tpns +state.dump +*.swp +cluster_master diff --git a/.cvsnotify b/.cvsnotify new file mode 100644 index 0000000..589d7f1 --- /dev/null +++ b/.cvsnotify @@ -0,0 +1 @@ +david.parrish@optusnet.com.au diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..4ccf61f --- /dev/null +++ b/INSTALL @@ -0,0 +1,72 @@ +Brief Installation guide for L2TPNS + +1. Requirements + + * You must have libcli installed to enable the command-line + interface. You can get it from http://sourceforge.net/projects/libcli. + If you don't have it, command-line support will not be compiled in. + + * A kernel with iptables support + + * If you want to use throttling, you must have a kernel and a tc (iproute) which supports HTB. + + +2. Compile + +./configure --prefix=/usr --sysconfdir=/etc/l2tpns +make + + +3. Install + + * make install. This does: + * Install the binaries into /usr/bin (l2tpns, cluster_master and nsctl) + * Create config dir /etc/l2tpns and create default config files + * Ensures that /dev/net/tun exists + + * Modify config file. You probably need to change most of the config options. + + * Set up basic firewall rules. This should be done in an init script. + + iptables -t nat -N l2tpns + iptables -t nat -A PREROUTING -j l2tpns + iptables -t mangle -N l2tpns + iptables -t mangle -A PREROUTING -j l2tpns + + * Set up walled garden firewall rules. This should be done in an init + script. This is not required unless you are using the garden plugin. + + iptables -t nat -N garden >/dev/null 2>&1 + iptables -t nat -F garden + iptables -t nat -A garden -p tcp -m tcp --dport 25 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p udp -m udp --dport 53 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p tcp -m tcp --dport 53 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p tcp -m tcp --dport 80 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p tcp -m tcp --dport 110 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p tcp -m tcp --dport 443 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p icmp -m icmp --icmp-type echo-request -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p icmp -j ACCEPT + iptables -t nat -A garden -j DROP + + * Set up IP address pools in /etc/l2tpns/ip_pool + + * Set up clustering + + * Run cluster_master on a separate machine + * Set the "cluster master" and "bind address" parameters in /etc/l2tpns/l2tpns.cfg + + * Make l2tpns run on startup + + * Test it out + + + + +This software is quite stable and is being used in a production +environment at a quite large ISP. However, you may have problems +setting it up, and if so, I would appreciate it if you would file +useful bug reports on the Source Forge page: + +http://sourceforge.net/projects/l2tpns/ + +-- David Parrish diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..404a715 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,70 @@ +subdirs = @subdirs@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +infodir = @infodir@ +etcdir = @sysconfdir@ +libdir = @prefix@/lib/l2tpns + +CC = @CC@ +CFLAGS=-Wall @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +INSTALL = @INSTALL@ +DEFS = @DEFS@ + +OBJS= md5.o \ + cli.o \ + l2tpns.o \ + ppp.o \ + radius.o \ + throttle.o \ + rl.o \ + ll.o \ + cluster.o \ + cluster_slave.o \ + arp.o \ + constants.o \ + ll.o \ + control.o \ + util.o \ + +PLUGINS=garden.so + +all: l2tpns cluster_master nsctl $(PLUGINS) + +l2tpns: $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) $(DEFS) + +cluster_master: cluster_master.o ll.o cluster.o util.o + $(CC) $(CFLAGS) -o $@ $^ $(DEFS) + +nsctl: nsctl.o control.o + $(CC) $(CFLAGS) -o $@ $^ $(DEFS) + +clean: + /bin/rm -f *.o *.so l2tpns cluster_master nsctl + +distclean: + /bin/rm -f Makefile config.h config.status config.cache config.log + +install: all + $(INSTALL) -D -o root -g root -m 0755 l2tpns $(bindir)/l2tpns + $(INSTALL) -D -o root -g root -m 0755 cluster_master $(bindir)/cluster_master + $(INSTALL) -D -o root -g root -m 0755 nsctl $(bindir)/nsctl + $(INSTALL) -D -o root -g root -m 0600 etc/l2tpns.cfg.default $(etcdir)/l2tpns.cfg + $(INSTALL) -D -o root -g root -m 0644 etc/ip_pool.default $(etcdir)/l2tpns.ip_pool + $(INSTALL) -D -o root -g root -m 0600 etc/users.default $(etcdir)/l2tpns.users + for PLUGIN in $(PLUGINS); do + $(INSTALL) -o root -g root -m 0755 $(PLUGIN) $(libdir)/$(PLUGIN) + done + if [ ! -e /dev/net/tun ]; then + mkdir /dev/net + mknod /dev/net/tun c 10 200 + fi + +%.so: %.c + $(CC) -fPIC -shared -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBPATH) + diff --git a/arp.c b/arp.c new file mode 100644 index 0000000..122880a --- /dev/null +++ b/arp.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +#include "l2tpns.h" + +/* Most of this code is based on keepalived:vrrp_arp.c */ + +struct arp_buf { + struct ether_header eth; + struct arphdr arp; + + /* Data bit - variably sized, so not present in |struct arphdr| */ + unsigned char ar_sha[ETH_ALEN]; /* Sender hardware address */ + ipt ar_sip; /* Sender IP address. */ + unsigned char ar_tha[ETH_ALEN]; /* Target hardware address */ + ipt ar_tip; /* Target ip */ +} __attribute__((packed)); + +void sendarp(int ifr_idx, const unsigned char* mac, ipt ip) +{ + int fd; + struct sockaddr_ll sll; + struct arp_buf buf; + + /* Ethernet */ + memset(buf.eth.ether_dhost, 0xFF, ETH_ALEN); + memcpy(buf.eth.ether_shost, mac, ETH_ALEN); + buf.eth.ether_type = htons(ETHERTYPE_ARP); + + /* ARP */ + buf.arp.ar_hrd = htons(ARPHRD_ETHER); + buf.arp.ar_pro = htons(ETHERTYPE_IP); + buf.arp.ar_hln = ETH_ALEN; + buf.arp.ar_pln = 4; //IPPROTO_ADDR_LEN; + buf.arp.ar_op = htons(ARPOP_REQUEST); + + /* Data */ + memcpy(buf.ar_sha, mac, ETH_ALEN); + memcpy(&buf.ar_sip, &ip, sizeof(ip)); + memcpy(buf.ar_tha, mac, ETH_ALEN); + memcpy(&buf.ar_tip, &ip, sizeof(ip)); + + /* Now actually send the thing */ + fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_RARP)); + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + strncpy(sll.sll_addr, mac, sizeof(sll.sll_addr)); /* Null-pad */ + sll.sll_halen = ETH_ALEN; + sll.sll_ifindex = ifr_idx; + + sendto(fd, &buf, sizeof(buf), 0, (struct sockaddr*)&sll, sizeof(sll)); +} diff --git a/bounce.c b/bounce.c new file mode 100644 index 0000000..9d3f334 --- /dev/null +++ b/bounce.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 39000 + +void sigalarm(int junk); +unsigned long long recv_count = 0; +unsigned long pps = 0; +unsigned long bytes = 0; +unsigned port = PORT; + +int main(int argc, char *argv[]) +{ + int on = 1; + struct sockaddr_in addr; + int s; + char *packet; + + while ((s = getopt(argc, argv, "?p:")) > 0) + { + switch (s) + { + case 'p' : + port = atoi(optarg); + break; + case '?' : + printf("Options:\n"); + printf("\t-p port to listen on\n"); + return(0); + break; + } + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(s, (void *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + return -1; + } + + signal(SIGALRM, sigalarm); + alarm(1); + + printf("Waiting on port %d\n", port); + packet = (char *)malloc(65535); + while (1) + { + struct sockaddr_in addr; + int alen = sizeof(addr), l; + + l = recvfrom(s, packet, 65535, 0, (void *) &addr, &alen); + if (l < 0) continue; + recv_count++; + pps++; + bytes += l; + + sendto(s, packet, l, 0, (struct sockaddr *)&addr, alen); + } + + free(packet); +} + +void sigalarm(int junk) +{ + printf("Recv: %10llu %0.1fMbits/s (%lu pps)\n", recv_count, (bytes / 1024.0 / 1024.0 * 8), pps); + pps = bytes = 0; + alarm(1); +} + diff --git a/cli.c b/cli.c new file mode 100644 index 0000000..3aafb10 --- /dev/null +++ b/cli.c @@ -0,0 +1,944 @@ +// L2TPNS Command Line Interface +// $Id: cli.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// vim: sw=4 ts=8 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "util.h" +#include "config.h" + +#ifdef HAVE_LIBCLI + +extern tunnelt *tunnel; +extern sessiont *session; +extern radiust *radius; +extern ippoolt *ip_address_pool; +extern struct Tstats *_statistics; +extern int cli_pid; +struct cli_def *cli = NULL; +int cli_quit = 0; +extern int clifd, udpfd, tapfd, snoopfd, radfd, ifrfd, cluster_sockfd; +extern sessionidt *cli_session_kill; +extern tunnelidt *cli_tunnel_kill; +extern tbft *filter_buckets; +#ifdef RINGBUFFER +extern struct Tringbuffer *ringbuffer; +#endif + +char *rcs_id = "$Id: cli.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $"; + +char *debug_levels[] = { + "CRIT", + "ERROR", + "WARN", + "INFO", + "CALL", + "DATA", +}; + +struct +{ + char critical; + char error; + char warning; + char info; + char calls; + char data; +} debug_flags; + +int debug_session; +int debug_tunnel; +int debug_rb_tail; + +int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_show_tunnels(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_show_users(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_show_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_show_version(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_show_pool(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_show_banana(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_clear_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_no_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_no_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_no_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_watch_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int cmd_watch_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); +int regular_stuff(struct cli_def *cli, FILE *w); + +void init_cli() +{ + FILE *f; + char buf[4096]; + struct cli_command *c; + int on = 1; + struct sockaddr_in addr; + + cli = cli_init(); + + c = cli_register_command(cli, NULL, "show", NULL, NULL); + cli_register_command(cli, c, "session", cmd_show_session, "Show a list of sessions"); + cli_register_command(cli, c, "tunnels", cmd_show_tunnels, NULL); + cli_register_command(cli, c, "users", cmd_show_users, NULL); + cli_register_command(cli, c, "version", cmd_show_version, NULL); + cli_register_command(cli, c, "banana", cmd_show_banana, "Show a banana"); + cli_register_command(cli, c, "pool", cmd_show_pool, NULL); + +#ifdef STATISTICS + cli_register_command(cli, c, "counters", cmd_show_counters, NULL); + + c = cli_register_command(cli, NULL, "clear", NULL, NULL); + cli_register_command(cli, c, "counters", cmd_clear_counters, NULL); +#endif + + cli_register_command(cli, NULL, "snoop", cmd_snoop, NULL); + cli_register_command(cli, NULL, "throttle", cmd_throttle, NULL); + + c = cli_register_command(cli, NULL, "no", NULL, NULL); + cli_register_command(cli, c, "snoop", cmd_no_snoop, NULL); + cli_register_command(cli, c, "throttle", cmd_no_throttle, NULL); + cli_register_command(cli, c, "debug", cmd_no_debug, NULL); + + c = cli_register_command(cli, NULL, "drop", NULL, NULL); + cli_register_command(cli, c, "user", cmd_drop_user, NULL); + cli_register_command(cli, c, "tunnel", cmd_drop_tunnel, NULL); + cli_register_command(cli, c, "session", cmd_drop_session, NULL); + + cli_register_command(cli, NULL, "debug", cmd_debug, "Specify a debugging level"); + + c = cli_register_command(cli, NULL, "watch", NULL, NULL); + cli_register_command(cli, c, "session", cmd_watch_session, "Dump logs for a tunnel"); + cli_register_command(cli, c, "tunnel", cmd_watch_tunnel, "Dump logs for a tunnel"); + + // Enable regular processing + cli_regular(cli, regular_stuff); + + if (!(f = fopen(CLIUSERS, "r"))) + { + log(0, 0, 0, 0, "WARNING! No users specified. Command-line access is open to all\n"); + } + else + { + while (fgets(buf, 4096, f)) + { + char *p; + if (*buf == '#') continue; + if ((p = strchr(buf, '\r'))) *p = 0; + if ((p = strchr(buf, '\n'))) *p = 0; + if (!*buf) continue; + if (!(p = strchr((char *)buf, ':'))) continue; + *p++ = 0; + cli_allow_user(cli, buf, p); + log(3, 0, 0, 0, "Allowing user %s to connect to the CLI\n", buf); + } + fclose(f); + } + + memset(&addr, 0, sizeof(addr)); + clifd = socket(PF_INET, SOCK_STREAM, 6); + setsockopt(clifd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + { + int flags; + // Set cli fd as non-blocking + flags = fcntl(clifd, F_GETFL, 0); + fcntl(clifd, F_SETFL, flags | O_NONBLOCK); + } + addr.sin_family = AF_INET; + addr.sin_port = htons(23); + if (bind(clifd, (void *) &addr, sizeof(addr)) < 0) + { + log(0, 0, 0, 0, "Error listening on cli port 23: %s\n", strerror(errno)); + return; + } + listen(clifd, 10); +} + +void cli_do(int sockfd) +{ + if ((cli_pid = fork())) return; + + // Close sockets + if (udpfd) close(udpfd); udpfd = 0; + if (tapfd) close(tapfd); tapfd = 0; + if (snoopfd) close(snoopfd); snoopfd = 0; + if (radfd) close(radfd); radfd = 0; + if (ifrfd) close(ifrfd); ifrfd = 0; + if (cluster_sockfd) close(cluster_sockfd); cluster_sockfd = 0; + if (clifd) close(clifd); clifd = 0; + + signal(SIGPIPE, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGUSR1, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGKILL, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + log(3, 0, 0, 0, "Accepted connection to CLI\n"); + + debug_session = 0; + debug_tunnel = 0; +#ifdef RINGBUFFER + debug_rb_tail = ringbuffer->tail; +#endif + memset(&debug_flags, 0, sizeof(debug_flags)); + debug_flags.critical = 1; + + cli_loop(cli, sockfd, "l2tpns> "); + + close(sockfd); + log(3, 0, 0, 0, "Closed CLI connection\n"); + exit(0); +} + +int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + time_t time_now; + + time(&time_now); + if (argc > 0) + { + // Show individual session + for (i = 0; i < argc; i++) + { + unsigned int s; + s = atoi(argv[i]); + if (!s || s > MAXSESSION) + { + fprintf(w, "Invalid session id \"%s\"\r\n", argv[i]); + continue; + } + fprintf(w, "\r\nSession %d:\r\n", s); + fprintf(w, " User: %s\r\n", session[s].user[0] ? session[s].user : "none"); + fprintf(w, " Calling Num: %s\r\n", session[s].calling); + fprintf(w, " Called Num: %s\r\n", session[s].called); + fprintf(w, " Tunnel ID: %d\r\n", session[s].tunnel); + fprintf(w, " IP address: %s\r\n", inet_toa(htonl(session[s].ip))); + fprintf(w, " HSD sid: %lu\r\n", session[s].sid); + fprintf(w, " Idle time: %u seconds\r\n", abs(time_now - session[s].last_packet)); + fprintf(w, " Next Recv: %u\r\n", session[s].nr); + fprintf(w, " Next Send: %u\r\n", session[s].ns); + fprintf(w, " Bytes In/Out: %lu/%lu\r\n", (unsigned long)session[s].cin, (unsigned long)session[s].cout); + fprintf(w, " Pkts In/Out: %lu/%lu\r\n", (unsigned long)session[s].pin, (unsigned long)session[s].pout); + fprintf(w, " Radius Session: %u\r\n", session[s].radius); + fprintf(w, " Rx Speed: %lu\r\n", session[s].rx_connect_speed); + fprintf(w, " Tx Speed: %lu\r\n", session[s].tx_connect_speed); + fprintf(w, " Intercepted: %s\r\n", session[s].snoop ? "YES" : "no"); + fprintf(w, " Throttled: %s\r\n", session[s].throttle ? "YES" : "no"); + fprintf(w, " Walled Garden: %s\r\n", session[s].walled_garden ? "YES" : "no"); + fprintf(w, " Filter Bucket: %s\r\n", session[s].tbf ? filter_buckets[session[s].tbf].handle : "none"); + } + return CLI_OK; + } + + // Show Summary + fprintf(w, " %s %4s %-32s %-15s %s %s %s %10s %10s %10s %4s %-15s %s\r\n", + "SID", + "TID", + "Username", + "IP", + "I", + "T", + "S", + "opened", + "downloaded", + "uploaded", + "idle", + "LAC", + "CLI"); + for (i = 0; i < MAXSESSION; i++) + { + char *userip, *tunnelip; + if (!session[i].opened) continue; + userip = strdup(inet_toa(htonl(session[i].ip))); + tunnelip = strdup(inet_toa(htonl(tunnel[ session[i].tunnel ].ip))); + fprintf(w, "%5d %4d %-32s %-15s %s %s %s %10lu %10lu %10lu %4u %-15s %s\r\n", + i, + session[i].tunnel, + session[i].user[0] ? session[i].user : "*", + userip, + (session[i].snoop) ? "Y" : "N", + (session[i].throttle) ? "Y" : "N", + (session[i].walled_garden) ? "Y" : "N", + (unsigned long)session[i].opened, + (unsigned long)session[i].cout, + (unsigned long)session[i].cin, + abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)), + tunnelip, + session[i].calling[0] ? session[i].calling : "*"); + if (userip) free(userip); + if (tunnelip) free(tunnelip); + } + return CLI_OK; +} + +int cmd_show_tunnels(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i, x; + time_t time_now; + + time(&time_now); + if (argc > 0) + { + // Show individual tunnel + for (i = 0; i < argc; i++) + { + char s[65535] = {0}; + unsigned int t; + t = atoi(argv[i]); + if (!t || t > MAXTUNNEL) + { + fprintf(w, "Invalid tunnel id \"%s\"\r\n", argv[i]); + continue; + } + fprintf(w, "\r\nTunnel %d:\r\n", t); + fprintf(w, " Hostname: %s\r\n", tunnel[t].hostname[0] ? tunnel[t].hostname : "(none)"); + fprintf(w, " Remote IP: %s\r\n", inet_toa(htonl(tunnel[t].ip))); + fprintf(w, " Remote Port: %d\r\n", tunnel[t].port); + fprintf(w, " Rx Window: %u\r\n", tunnel[t].window); + fprintf(w, " Next Recv: %u\r\n", tunnel[t].nr); + fprintf(w, " Next Send: %u\r\n", tunnel[t].ns); + fprintf(w, " Queue Len: %u\r\n", tunnel[t].controlc); + fprintf(w, " Last Packet Age:%u\r\n", (unsigned)(time_now - tunnel[t].last)); + + for (x = 0; x < MAXSESSION; x++) + if (session[x].tunnel == t && session[x].opened && !session[x].die) + sprintf(s, "%s%u ", s, x); + fprintf(w, " Sessions: %s\r\n", s); + } + return CLI_OK; + } + + // Show tunnel summary + fprintf(w, "%s %s %s %s\r\n", + "TID", + "Hostname", + "IP", + "Sessions"); + for (i = 0; i < MAXTUNNEL; i++) + { + int sessions = 0; + if (!tunnel[i].ip || tunnel[i].die || !tunnel[i].hostname[0]) continue; + + for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == i && session[x].opened && !session[x].die) sessions++; + fprintf(w, "%d %s %s %d\r\n", + i, + tunnel[i].hostname, + inet_toa(htonl(tunnel[i].ip)), + sessions); + } + return CLI_OK; +} + +int cmd_show_users(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + for (i = 0; i < MAXSESSION; i++) + { + if (!session[i].opened) continue; + if (!session[i].user[0]) continue; + fprintf(w, "%s\r\n", + session[i].user); + } + return CLI_OK; +} + +int cmd_show_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + fprintf(w, "%-10s %-8s %-10s %-8s\r\n", "Ethernet", "Bytes", "Packets", "Errors"); + fprintf(w, "%-10s %8lu %8lu %8lu\r\n", "RX", + GET_STAT(tap_rx_bytes), + GET_STAT(tap_rx_packets), + GET_STAT(tap_rx_errors)); + fprintf(w, "%-10s %8lu %8lu %8lu\r\n", "TX", + GET_STAT(tap_tx_bytes), + GET_STAT(tap_tx_packets), + GET_STAT(tap_tx_errors)); + fprintf(w, "\r\n"); + + fprintf(w, "%-10s %-8s %-10s %-8s %-8s\r\n", "Tunnel", "Bytes", "Packets", "Errors", "Retries"); + fprintf(w, "%-10s %8lu %8lu %8lu %8lu\r\n", "RX", + GET_STAT(tunnel_rx_bytes), + GET_STAT(tunnel_rx_packets), + GET_STAT(tunnel_rx_errors), + 0L); + fprintf(w, "%-10s %8lu %8lu %8lu %8lu\r\n", "TX", + GET_STAT(tunnel_tx_bytes), + GET_STAT(tunnel_tx_packets), + GET_STAT(tunnel_rx_errors), + GET_STAT(tunnel_retries)); + fprintf(w, "\r\n"); + + fprintf(w, "%-30s%-10s\r\n", "Counter", "Value"); + fprintf(w, "-----------------------------------------\r\n"); + fprintf(w, "%-30s%lu\r\n", "radius_retries", GET_STAT(radius_retries)); + fprintf(w, "%-30s%lu\r\n", "arp_errors", GET_STAT(arp_errors)); + fprintf(w, "%-30s%lu\r\n", "arp_replies", GET_STAT(arp_replies)); + fprintf(w, "%-30s%lu\r\n", "arp_discarded", GET_STAT(arp_discarded)); + fprintf(w, "%-30s%lu\r\n", "arp_sent", GET_STAT(arp_sent)); + fprintf(w, "%-30s%lu\r\n", "arp_recv", GET_STAT(arp_recv)); + fprintf(w, "%-30s%lu\r\n", "packets_snooped", GET_STAT(packets_snooped)); + fprintf(w, "%-30s%lu\r\n", "tunnel_created", GET_STAT(tunnel_created)); + fprintf(w, "%-30s%lu\r\n", "session_created", GET_STAT(session_created)); + fprintf(w, "%-30s%lu\r\n", "tunnel_timeout", GET_STAT(tunnel_timeout)); + fprintf(w, "%-30s%lu\r\n", "session_timeout", GET_STAT(session_timeout)); + fprintf(w, "%-30s%lu\r\n", "radius_timeout", GET_STAT(radius_timeout)); + fprintf(w, "%-30s%lu\r\n", "radius_overflow", GET_STAT(radius_overflow)); + fprintf(w, "%-30s%lu\r\n", "tunnel_overflow", GET_STAT(tunnel_overflow)); + fprintf(w, "%-30s%lu\r\n", "session_overflow", GET_STAT(session_overflow)); + fprintf(w, "%-30s%lu\r\n", "ip_allocated", GET_STAT(ip_allocated)); + fprintf(w, "%-30s%lu\r\n", "ip_freed", GET_STAT(ip_freed)); + +#ifdef STAT_CALLS + fprintf(w, "\n%-30s%-10s\r\n", "Counter", "Value"); + fprintf(w, "-----------------------------------------\r\n"); + fprintf(w, "%-30s%lu\r\n", "call_processtap", GET_STAT(call_processtap)); + fprintf(w, "%-30s%lu\r\n", "call_processarp", GET_STAT(call_processarp)); + fprintf(w, "%-30s%lu\r\n", "call_processipout", GET_STAT(call_processipout)); + fprintf(w, "%-30s%lu\r\n", "call_processudp", GET_STAT(call_processudp)); + fprintf(w, "%-30s%lu\r\n", "call_processpap", GET_STAT(call_processpap)); + fprintf(w, "%-30s%lu\r\n", "call_processchap", GET_STAT(call_processchap)); + fprintf(w, "%-30s%lu\r\n", "call_processlcp", GET_STAT(call_processlcp)); + fprintf(w, "%-30s%lu\r\n", "call_processipcp", GET_STAT(call_processipcp)); + fprintf(w, "%-30s%lu\r\n", "call_processipin", GET_STAT(call_processipin)); + fprintf(w, "%-30s%lu\r\n", "call_processccp", GET_STAT(call_processccp)); + fprintf(w, "%-30s%lu\r\n", "call_processrad", GET_STAT(call_processrad)); + fprintf(w, "%-30s%lu\r\n", "call_sendarp", GET_STAT(call_sendarp)); + fprintf(w, "%-30s%lu\r\n", "call_sendipcp", GET_STAT(call_sendipcp)); + fprintf(w, "%-30s%lu\r\n", "call_sendchap", GET_STAT(call_sendchap)); + fprintf(w, "%-30s%lu\r\n", "call_sessionbyip", GET_STAT(call_sessionbyip)); + fprintf(w, "%-30s%lu\r\n", "call_sessionbyuser", GET_STAT(call_sessionbyuser)); + fprintf(w, "%-30s%lu\r\n", "call_tunnelsend", GET_STAT(call_tunnelsend)); + fprintf(w, "%-30s%lu\r\n", "call_tunnelkill", GET_STAT(call_tunnelkill)); + fprintf(w, "%-30s%lu\r\n", "call_tunnelshutdown", GET_STAT(call_tunnelshutdown)); + fprintf(w, "%-30s%lu\r\n", "call_sessionkill", GET_STAT(call_sessionkill)); + fprintf(w, "%-30s%lu\r\n", "call_sessionshutdown", GET_STAT(call_sessionshutdown)); + fprintf(w, "%-30s%lu\r\n", "call_sessionsetup", GET_STAT(call_sessionsetup)); + fprintf(w, "%-30s%lu\r\n", "call_assign_ip_address",GET_STAT(call_assign_ip_address)); + fprintf(w, "%-30s%lu\r\n", "call_free_ip_address", GET_STAT(call_free_ip_address)); + fprintf(w, "%-30s%lu\r\n", "call_dump_acct_info", GET_STAT(call_dump_acct_info)); + fprintf(w, "%-30s%lu\r\n", "call_radiussend", GET_STAT(call_radiussend)); + fprintf(w, "%-30s%lu\r\n", "call_radiusretry", GET_STAT(call_radiusretry)); +#endif + return CLI_OK; +} + +int cmd_show_version(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + fprintf(w, "L2TPNS %s\r\n", VERSION); + fprintf(w, "ID: %s\r\n", rcs_id); + return CLI_OK; +} +int cmd_show_pool(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + int used = 0, free = 0; + + fprintf(w, "%-15s %4s %8s %s\r\n", "IP Address", "Used", "Session", "User"); + for (i = 0; i < MAXIPPOOL; i++) + { + sessionidt s = 0; + + if (!ip_address_pool[i].address) continue; + if (ip_address_pool[i].assigned) + { + used++; + s = sessionbyip(ip_address_pool[i].address); + } + else + { + free++; + } + + fprintf(w, "%-15s %4s %8d %s\r\n", + inet_toa(ip_address_pool[i].address), + (s) ? "Y" : "N", + s, + session[s].user); + } + fprintf(w, "\r\nFree: %d\r\nUsed: %d\r\n", free, used); + return CLI_OK; +} +int cmd_show_banana(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + fputs(" _\r\n" + "//\\\r\n" + "V \\\r\n" + " \\ \\_\r\n" + " \\,'.`-.\r\n" + " |\\ `. `.\r\n" + " ( \\ `. `-. _,.-:\\\r\n" + " \\ \\ `. `-._ __..--' ,-';/\r\n" + " \\ `. `-. `-..___..---' _.--' ,'/\r\n" + " `. `. `-._ __..--' ,' /\r\n" + " `. `-_ ``--..'' _.-' ,'\r\n" + " `-_ `-.___ __,--' ,'\r\n" + " `-.__ `----\"\"\" __.-'\r\n" + "hh `--..____..--'\r\n", w); + + return CLI_OK; +} + +int cmd_clear_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + fprintf(w, "Counters cleared\r\n"); + SET_STAT(last_reset, time(NULL)); + return CLI_OK; +} + +int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user to drop\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + + if (session[s].ip && session[s].opened && !session[s].die) + { + int x; + + fprintf(w, "Dropping user %s\r\n", session[s].user); + for (x = 0; x < MAXSESSION; x++) + { + if (!cli_session_kill[x]) + { + cli_session_kill[x] = s; + break; + } + } + } + } + + return CLI_OK; +} + +int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + tunnelidt tid; + + if (!argc) + { + fprintf(w, "Specify a tunnel to drop\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "tunnel_id ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + int x; + + if ((tid = atol(argv[i])) <= 0 || (tid > MAXTUNNEL)) + { + fprintf(w, "Invalid tunnel ID (%d - %d)\r\n", 0, MAXTUNNEL); + continue; + } + + if (!tunnel[tid].ip) + { + fprintf(w, "Tunnel %d is not connected\r\n", tid); + continue; + } + + if (tunnel[tid].die) + { + fprintf(w, "Tunnel %d is already being shut down\r\n", tid); + continue; + } + + for (x = 0; x < MAXTUNNEL; x++) + { + if (!cli_tunnel_kill[x]) + { + cli_tunnel_kill[x] = tid; + fprintf(w, "Tunnel %d shut down (%s)\r\n", tid, tunnel[tid].hostname); + break; + } + } + } + + return CLI_OK; +} + +int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a session id to drop\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "session_id ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if ((s = atol(argv[i])) <= 0 || (s > MAXSESSION)) + { + fprintf(w, "Invalid session ID (%d - %d)\r\n", 0, MAXSESSION); + continue; + } + + if (session[s].ip && session[s].opened && !session[s].die) + { + int x; + for (x = 0; x < MAXSESSION; x++) + { + if (!cli_session_kill[x]) + { + cli_session_kill[x] = s; + break; + } + } + fprintf(w, "Dropping session %d\r\n", s); + } + else + { + fprintf(w, "Session %d is not active.\r\n", s); + } + } + + return CLI_OK; +} + +int cmd_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + session[s].snoop = 1; + + fprintf(w, "Snooping user %s\r\n", argv[i]); + } + return CLI_OK; +} + +int cmd_no_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + session[s].snoop = 0; + + fprintf(w, "Not snooping user %s\r\n", argv[i]); + } + return CLI_OK; +} + +int cmd_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + throttle_session(s, 1); + + fprintf(w, "throttling user %s\r\n", argv[i]); + } + return CLI_OK; +} + +int cmd_no_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + throttle_session(s, 0); + + fprintf(w, "unthrottling user %s\r\n", argv[i]); + } + return CLI_OK; +} + +int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + + if (!argc) + { + fprintf(w, "Currently debugging: "); + if (debug_flags.critical) fprintf(w, "critical "); + if (debug_flags.error) fprintf(w, "error "); + if (debug_flags.warning) fprintf(w, "warning "); + if (debug_flags.info) fprintf(w, "info "); + if (debug_flags.calls) fprintf(w, "calls "); + if (debug_flags.data) fprintf(w, "data "); + fprintf(w, "\r\n"); + return CLI_OK; + } + + for (i = 0; i < argc; i++) + { + if (*argv[i] == '?') + { + fprintf(w, "Possible debugging states are:\r\n"); + fprintf(w, " critical\r\n"); + fprintf(w, " error\r\n"); + fprintf(w, " warning\r\n"); + fprintf(w, " info\r\n"); + fprintf(w, " calls\r\n"); + fprintf(w, " data\r\n"); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (strcasecmp(argv[i], "critical") == 0) debug_flags.critical = 1; + if (strcasecmp(argv[i], "error") == 0) debug_flags.error = 1; + if (strcasecmp(argv[i], "warning") == 0) debug_flags.warning = 1; + if (strcasecmp(argv[i], "info") == 0) debug_flags.info = 1; + if (strcasecmp(argv[i], "calls") == 0) debug_flags.calls = 1; + if (strcasecmp(argv[i], "data") == 0) debug_flags.data = 1; + if (strcasecmp(argv[i], "all") == 0) + { + memset(&debug_flags, 1, sizeof(debug_flags)); + debug_flags.data = 0; + } + } + + return CLI_OK; +} + +int cmd_no_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + + for (i = 0; i < argc; i++) + { + if (strcasecmp(argv[i], "critical") == 0) debug_flags.critical = 0; + if (strcasecmp(argv[i], "error") == 0) debug_flags.error = 0; + if (strcasecmp(argv[i], "warning") == 0) debug_flags.warning = 0; + if (strcasecmp(argv[i], "info") == 0) debug_flags.info = 0; + if (strcasecmp(argv[i], "calls") == 0) debug_flags.calls = 0; + if (strcasecmp(argv[i], "data") == 0) debug_flags.data = 0; + if (strcasecmp(argv[i], "all") == 0) memset(&debug_flags, 0, sizeof(debug_flags)); + } + + return CLI_OK; +} + +int cmd_watch_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + sessionidt s; + + if (argc != 1) + { + fprintf(w, "Specify a single session to debug (0 to disable)\r\n"); + return CLI_OK; + } + s = atoi(argv[0]); + + if (debug_session) + fprintf(w, "No longer debugging session %d\r\n", debug_session); + + if (s) fprintf(w, "Debugging session %d.\r\n", s); + debug_session = s; + + return CLI_OK; +} + +int cmd_watch_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + tunnelidt s; + + if (argc != 1) + { + fprintf(w, "Specify a single tunnel to debug (0 to disable)\r\n"); + return CLI_OK; + } + s = atoi(argv[0]); + + if (debug_tunnel) + fprintf(w, "No longer debugging tunnel %d\r\n", debug_tunnel); + + if (s) fprintf(w, "Debugging tunnel %d.\r\n", s); + debug_tunnel = s; + + return CLI_OK; +} + +int regular_stuff(struct cli_def *cli, FILE *w) +{ + int i = debug_rb_tail; + +#ifdef RINGBUFFER + while (i != ringbuffer->tail) + { + int show_message = 0; + + if (*ringbuffer->buffer[i].message) + { + // Always show messages if we are doing general debug + if (ringbuffer->buffer[i].level == 0 && debug_flags.critical) show_message = 1; + if (ringbuffer->buffer[i].level == 1 && debug_flags.error) show_message = 1; + if (ringbuffer->buffer[i].level == 2 && debug_flags.warning) show_message = 1; + if (ringbuffer->buffer[i].level == 3 && debug_flags.info) show_message = 1; + if (ringbuffer->buffer[i].level == 4 && debug_flags.calls) show_message = 1; + if (ringbuffer->buffer[i].level == 5 && debug_flags.data) show_message = 1; + } + + if (show_message) + { + ipt address = ntohl(ringbuffer->buffer[i].address); + char *ipaddr; + struct in_addr addr; + + memcpy(&addr, &address, sizeof(ringbuffer->buffer[i].address)); + ipaddr = inet_ntoa(addr); + + fprintf(w, "%s-%s-%u-%u %s\r", + debug_levels[(int)ringbuffer->buffer[i].level], + ipaddr, + ringbuffer->buffer[i].tunnel, + ringbuffer->buffer[i].session, + ringbuffer->buffer[i].message); + } + + if (++i == ringbuffer->tail) break; + if (i == RINGBUFFER_SIZE) i = 0; + } + + debug_rb_tail = ringbuffer->tail; +#endif + return CLI_OK; +} + +#endif diff --git a/cluster.c b/cluster.c new file mode 100644 index 0000000..5ad3ff3 --- /dev/null +++ b/cluster.c @@ -0,0 +1,85 @@ +// L2TPNS Clustering Stuff +// $Id: cluster.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ #include #include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cluster.h" + +int cluster_sockfd = 0; +int cluster_server = 0; +uint32_t vip_address; +extern int debug; +void _log_hex(int level, const char *title, const char *data, int maxsize); +#define log_hex(a,b,c,d) +#ifndef log_hex +#define log_hex(a,b,c,d) do{if (a > debug) _log_hex(a,b,c,d);}while (0) +#endif + +// Create a listening socket +int cluster_init(uint32_t bind_address, int server) +{ + struct sockaddr_in addr; + + vip_address = bind_address; + cluster_server = !!server; + + cluster_sockfd = socket(AF_INET, SOCK_DGRAM, UDP); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(cluster_server ? CLUSTERPORT : CLUSTERCLIENTPORT); + addr.sin_addr.s_addr = INADDR_ANY; + setsockopt(cluster_sockfd, SOL_SOCKET, SO_REUSEADDR, &addr, sizeof(addr)); + if (bind(cluster_sockfd, (void *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + exit(-1); + } + + return cluster_sockfd; +} + +int cluster_send_message(unsigned long ip_address, uint32_t vip, char type, void *data, int datalen) +{ + size_t l = 1 + sizeof(uint32_t) + datalen; + char *buf = NULL; + struct sockaddr_in addr = {0}; + + if (!cluster_sockfd) return -1; + if (!ip_address) return 0; + + buf = calloc(l, 1); + *(uint32_t *)(buf) = htonl(vip); + *(char *)(buf+sizeof(uint32_t)) = type; + + if (data && datalen > 0) + memcpy((char *)(buf + sizeof(uint32_t) + 1), data, datalen); + + addr.sin_addr.s_addr = ip_address; + addr.sin_port = htons(cluster_server ? CLUSTERCLIENTPORT : CLUSTERPORT); + addr.sin_family = AF_INET; + + log_hex(4, "Cluster send", buf, l); + + if (sendto(cluster_sockfd, buf, l, MSG_NOSIGNAL, (void *) &addr, sizeof(addr)) < 0) + { + perror("sendto"); + free(buf); + return -1; + } + free(buf); + + return 0; +} diff --git a/cluster.h b/cluster.h new file mode 100644 index 0000000..57759fc --- /dev/null +++ b/cluster.h @@ -0,0 +1,18 @@ +// L2TPNS Clustering Stuff +// $Id: cluster.h,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#define C_HELLO 1 +#define C_HELLO_RESPONSE 2 +#define C_PING 3 +#define C_TUNNEL 4 +#define C_SESSION 5 + +#define CLUSTERPORT 32792 +#define CLUSTERCLIENTPORT 32793 +#define UDP 17 +#define TIMEOUT 20 +#define IL sizeof(int) + +int cluster_init(uint32_t bind_address, int server); +int cluster_send_message(unsigned long ip_address, uint32_t vip, char type, void *data, int datalen); +int processcluster(char *buf, int l); diff --git a/cluster_master.c b/cluster_master.c new file mode 100644 index 0000000..0cc43ee --- /dev/null +++ b/cluster_master.c @@ -0,0 +1,480 @@ +// L2TPNS Cluster Master +// $Id: cluster_master.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cluster.h" +#include "ll.h" +#include "util.h" +#include "config.h" + +#define L2TPNS BINDIR "/l2tpns" + +typedef struct +{ + char *hostname; + unsigned long last_message; + uint32_t ip_address; + uint32_t slave_address; + int remove_from_cluster; + int down; + int tunnel_len; + int session_len; + pid_t pid; + + int num_tunnels; + char *tunnels[1000]; + int num_sessions; + char *sessions[13000]; +} slave; + +uint32_t master_address; +linked_list *slaves; +extern int cluster_sockfd; +int debug = 4; + +int processmsg(char *buf, int l, struct sockaddr_in *src_addr); +int handle_hello(char *buf, int l, struct sockaddr_in *src_addr, uint32_t addr); +int handle_tunnel(char *buf, int l, uint32_t addr); +int handle_session(char *buf, int l, uint32_t addr); +int handle_ping(char *buf, int l, uint32_t addr); +int backup_up(slave *s); +int backup_down(slave *s); +int return_state(slave *s); +slave *find_slave(uint32_t address); +#define log _log +void _log(int level, const char *format, ...); +void log_hex(int level, const char *title, const char *data, int maxsize); + +/* Catch our forked processes exiting */ +void sigchild_handler(int signal) +{ + int status; + int pid; + + pid = wait(&status); + /* TODO: catch errors and respawn? */ +} + +int main(int argc, char *argv[]) +{ + slave *s; + char buf[4096]; + struct timeval to; + + if (argc != 2) { + log(0, "Usage: %s
\n", argv[0]); + exit(-1); + } + + master_address = inet_addr(argv[1]); + if (master_address == INADDR_NONE) { + log(0, "Invalid ip %s\n", argv[1]); + exit(-1); + } + + cluster_init(master_address, 1); + slaves = ll_init(); + + signal(SIGCHLD, sigchild_handler); + + log(0, "Cluster Manager $Id: cluster_master.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ starting\n"); + + to.tv_sec = 1; + to.tv_usec = 0; + while (1) + { + fd_set r; + int n; + + FD_ZERO(&r); + FD_SET(cluster_sockfd, &r); + n = select(cluster_sockfd + 1, &r, 0, 0, &to); + if (n < 0) + { + if (errno != EINTR) + { + perror("select"); + exit(-1); + } + continue; + } + else if (n) + { + struct sockaddr_in addr; + int alen = sizeof(addr); + + memset(buf, 0, sizeof(buf)); + if (FD_ISSET(cluster_sockfd, &r)) + processmsg(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr); + continue; + } + + // Handle slaves timing out + { + time_t now = time(NULL); + ll_reset(slaves); + while ((s = ll_next(slaves))) + { + if (s->down) continue; + if (s->last_message < (now - TIMEOUT)) + { + log(4, "Slave \"%s\" s->last_message is %lu (timeout is %lu)\n", s->hostname, s->last_message, (now - TIMEOUT)); + if (s->remove_from_cluster) + { + // Remove them from the cluster + ll_delete(slaves, s); + if (s->hostname) free(s->hostname); + free(s); + ll_reset(slaves); + continue; + } + backup_up(s); + } + } + } + + to.tv_sec = 1; + to.tv_usec = 0; + } + + return 0; +} + +int processmsg(char *buf, int l, struct sockaddr_in *src_addr) +{ + char mtype; + uint32_t addr; + + log_hex(4, "Received", buf, l); + if (!buf || l <= sizeof(uint32_t)) return 0; + + addr = ntohl(*(uint32_t*)buf); + buf += sizeof(uint32_t); + l -= sizeof(uint32_t); + + mtype = *buf; buf++; l--; + + switch (mtype) + { + case C_HELLO: + handle_hello(buf, l, src_addr, addr); + break; + case C_PING: + if (!find_slave(addr)) + handle_hello(buf, l, src_addr, addr); + else + handle_ping(buf, l, addr); + break; + case C_TUNNEL: + if (!find_slave(addr)) handle_hello((char *)(buf + 1), *(char *)buf, src_addr, addr); + handle_tunnel(buf, l, addr); + break; + case C_SESSION: + if (!find_slave(addr)) handle_hello((char *)(buf + 1), *(char *)buf, src_addr, addr); + handle_session(buf, l, addr); + break; + } + return mtype; +} + +int handle_hello(char *buf, int l, struct sockaddr_in *src_addr, uint32_t addr) +{ + slave *s; + char *hostname; + + hostname = calloc(l + 1, 1); + memcpy(hostname, buf, l); + + // Is this a slave we have state information for? + if ((s = find_slave(addr))) + { + if (src_addr->sin_addr.s_addr == master_address) + { + log(1, "Got hello from \"%s\", local backup for %s.\n", hostname, inet_toa(s->ip_address)); + } + else if (s->down) + { + log(1, "Slave \"%s\" (for %s) has come back.\n", hostname, inet_toa(s->ip_address)); + backup_down(s); + } + else + { + log(1, "Slave \"%s\" said hello and we didn't know it was down.\n", s->hostname); + } + + /* Reset the hostname if needed */ + free(s->hostname); + s->hostname = hostname; + } else { + // No state information, it's a new slave + s = calloc(sizeof(slave), 1); + s->ip_address = addr; + ll_push(slaves, s); + s->hostname = hostname; + log(1, "New slave added to cluster \"%s\"\n", s->hostname); + } + + s->slave_address = src_addr->sin_addr.s_addr; + + // Send state information back + return_state(s); + + s->last_message = time(NULL); + + return 0; +} + +int handle_tunnel(char *buf, int l, uint32_t addr) +{ + int tid; + slave *s; + if (!(s = find_slave(addr))) + { + log(0, "handle_tunnel() called with no valid slave\n"); + return 0; + } + s->last_message = time(NULL); + + // Skip hostname + tid = *(char *)buf; + buf += (tid + 1); + l -= (tid + 1); + + // Grab tunnel ID + tid = *(int *)buf; + buf += sizeof(int); + l -= sizeof(int); + + log(3, "Received tunnel %d from \"%s\" (%d bytes long)\n", tid, s->hostname, l); + + // Allocate memory for it if it's not already + if (!s->tunnels[tid]) + { + s->tunnels[tid] = malloc(l); + s->num_tunnels++; + s->tunnel_len = l; + } + + memcpy(s->tunnels[tid], buf, l); + return l; +} + +int handle_session(char *buf, int l, uint32_t addr) +{ + slave *s; + int sid; + char hostname[4096] = {0}; + if (!(s = find_slave(addr))) + { + log(0, "handle_session() called with no valid slave\n"); + return 0; + } + s->last_message = time(NULL); + + // Skip hostname + sid = *(char *)buf; + memcpy(hostname, (char *)(buf + 1), sid); + buf += (sid + 1); + l -= (sid + 1); + log(0, "Hostname is %s\n", hostname); + + // Grab session ID + sid = *(int *)buf; + buf += sizeof(int); + l -= sizeof(int); + + log(3, "Received session %d from \"%s\" (%d bytes long)\n", sid, s->hostname, l); + + // Allocate memory for it if it's not already + if (!s->sessions[sid]) + { + s->sessions[sid] = malloc(l); + s->num_sessions++; + s->session_len = l; + } + + memcpy(s->sessions[sid], buf, l); + return l; +} + +int handle_ping(char *buf, int l, uint32_t addr) +{ + slave *s; + if (!(s = find_slave(addr))) + { + log(0, "handle_ping() called with no valid slave\n"); + return 0; + } + s->last_message = time(NULL); + + return 0; +} + +int return_state(slave *s) +{ + char *packet; + int i; + int num_tunnels = 0, num_sessions = 0; + int pktlen; + + log(3, "Sending state information to \"%s\"\n", s->hostname); + + for (i = 0; i < 1000; i++) + if (s->tunnels[i]) num_tunnels++; + + for (i = 0; i < 13000; i++) + if (s->sessions[i]) num_sessions++; + + if (!num_sessions && !num_tunnels) return 0; + + packet = calloc(IL * 4, 1); + *(int *)(packet + IL * 0) = num_tunnels; + *(int *)(packet + IL * 1) = num_sessions; + *(int *)(packet + IL * 2) = s->tunnel_len; + *(int *)(packet + IL * 3) = s->session_len; + cluster_send_message(s->slave_address, s->ip_address, C_HELLO_RESPONSE, packet, IL * 4); + free(packet); + + // Send tunnels one-by-one, in order + log(0, "Sending %d tunnels of %d bytes each\n", num_tunnels, s->tunnel_len); + pktlen = s->tunnel_len + sizeof(int); + packet = malloc(pktlen); + for (i = 0; i < 1000; i++) + { + if (s->tunnels[i]) + { + *(int *)packet = i; + memcpy((char *)(packet + sizeof(int)), s->tunnels[i], s->tunnel_len); + log(0, "Sending tunnel %d\n", i); + cluster_send_message(s->slave_address, s->ip_address, C_TUNNEL, packet, pktlen); + } + } + free(packet); + + // Send sessions one-by-one, in order + log(0, "Sending %d sessions of %d bytes each\n", num_sessions, s->session_len); + pktlen = s->session_len + sizeof(int); + packet = malloc(pktlen); + for (i = 0; i < 13000; i++) + { + if (s->sessions[i]) + { + *(int *)packet = i; + memcpy((char *)(packet + sizeof(int)), s->sessions[i], s->session_len); + log(0, "Sending session %d\n", i); + cluster_send_message(s->slave_address, s->ip_address, C_SESSION, packet, pktlen); + } + } + free(packet); + + return 0; +} + +slave *find_slave(uint32_t address) +{ + slave *s; + + ll_reset(slaves); + while ((s = ll_next(slaves))) + { + if (s->ip_address == address) + { + return s; + } + } + return NULL; +} + +void _log(int level, const char *format, ...) +{ + va_list ap; + if (debug < level) return; + + va_start(ap, format); + vfprintf(stderr, format, ap); +} + +void log_hex(int level, const char *title, const char *data, int maxsize) +{ + unsigned int i, j; + unsigned const char *d = (unsigned const char *)data; + + if (debug < level) return; + log(level, "%s (%d bytes):\n", title, maxsize); + setvbuf(stderr, NULL, _IOFBF, 16384); + for (i = 0; i < maxsize; ) + { + fprintf(stderr, "%4X: ", i); + for (j = i; j < maxsize && j < (i + 16); j++) + { + fprintf(stderr, "%02X ", d[j]); + if (j == i + 7) + fputs(": ", stderr); + } + + for (; j < i + 16; j++) + { + fputs(" ", stderr); + if (j == i + 7) + fputs(": ", stderr); + } + + fputs(" ", stderr); + for (j = i; j < maxsize && j < (i + 16); j++) + { + if (d[j] >= 0x20 && d[j] < 0x7f && d[j] != 0x20) + fputc(d[j], stderr); + else + fputc('.', stderr); + + if (j == i + 7) + fputs(" ", stderr); + } + + i = j; + fputs("\n", stderr); + } + fflush(stderr); + setbuf(stderr, NULL); +} + +int backup_up(slave *s) +{ + log(2, "Becoming backup for \"%s\" (%s).\n", s->hostname, inet_toa(s->ip_address)); + s->pid = fork(); + if (!s->pid) + { + if (execl(L2TPNS, L2TPNS, "-a", inet_toa(s->ip_address), NULL) < 0) + log(0, "Error execing backup " L2TPNS ": %s\n", strerror(errno)); + exit(0); + } + s->down = 1; + return 0; +} + +int backup_down(slave *s) +{ + log(2, "Not being backup for \"%s\" (%s) anymore.\n", s->hostname, inet_toa(s->ip_address)); + s->down = 0; + if (s->pid) { + kill(s->pid, SIGTERM); + sleep(2); + kill(s->pid, SIGKILL); + } + return 0; +} + diff --git a/cluster_slave.c b/cluster_slave.c new file mode 100644 index 0000000..71e2eac --- /dev/null +++ b/cluster_slave.c @@ -0,0 +1,249 @@ +// L2TPNS Cluster Master +// $Id: cluster_slave.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "cluster.h" +#include "ll.h" +#include "util.h" + +extern int cluster_sockfd; +extern tunnelt *tunnel; +extern sessiont *session; +extern uint32_t cluster_address; +extern char hostname[1000]; +extern int debug; +extern ippoolt *ip_address_pool; +extern uint32_t vip_address; +extern tunnelidt tunnelfree; +extern sessionidt sessionfree; + +int handle_tunnel(char *buf, int l); +int handle_session(char *buf, int l); +int handle_hello_response(char *buf, int l); + +int processcluster(char *buf, int l) +{ + char mtype; + uint32_t addr; + + log_hex(4, "Cluster receive", buf, l); + if (!buf || l <= sizeof(uint32_t)) return 0; + + addr = ntohl(*(uint32_t*)buf); + buf += sizeof(uint32_t); + l -= sizeof(uint32_t); + + if (addr != vip_address) + { + log(0, 0, 0, 0, "Received cluster message for VIP %s, which isn't ours\n", inet_toa(addr)); + } + + mtype = *buf; buf++; l--; + + switch (mtype) + { + case C_HELLO: + break; + case C_HELLO_RESPONSE: + handle_hello_response(buf, l); + break; + case C_PING: + break; + case C_TUNNEL: + handle_tunnel(buf, l); + break; + case C_SESSION: + handle_session(buf, l); + break; + } + return mtype; + + return 0; +} + +int handle_tunnel(char *buf, int l) +{ + int t; + + t = *(int *)buf; + log(1, 0, 0, t, "Receiving tunnel %d from cluster master (%d bytes)\n", t, l); + buf += sizeof(int); l -= sizeof(int); + + if (t > MAXTUNNEL) + { + log(0, 0, 0, t, "Cluster master tried to send tunnel %d, which is bigger than MAXTUNNEL (%d)\n", t, MAXTUNNEL); + return 0; + } + + if (l != sizeof(tunnelt)) + { + log(1, 0, 0, t, "Discarding bogus tunnel message (%d bytes instead of %d).\n", l, sizeof(tunnelt)); + return 0; + } + + if (t > 1) + { + tunnel[t-1].next = tunnel[t].next; + } + + if (tunnelfree == t) + { + tunnelfree = tunnel[t].next; + } + + memcpy(&tunnel[t], buf, l); + log(3, 0, 0, t, "Cluster master sent tunnel for %s\n", tunnel[t].hostname); + + tunnel[t].controlc = 0; + tunnel[t].controls = NULL; + tunnel[t].controle = NULL; + return 0; +} + +int handle_session(char *buf, int l) +{ + int s; + + s = *(int *)buf; + log(1, 0, s, 0, "Receiving session %d from cluster master (%d bytes)\n", s, l); + buf += sizeof(int); l -= sizeof(int); + + if (s > MAXSESSION) + { + log(0, 0, s, 0, "Cluster master tried to send session %d, which is bigger than MAXSESSION (%d)\n", s, MAXSESSION); + return 0; + } + + if (l != sizeof(sessiont)) + { + log(1, 0, s, 0, "Discarding short session message (%d bytes instead of %d).\n", l, sizeof(sessiont)); + return 0; + } + + if (s > 1) + { + session[s-1].next = session[s].next; + } + + if (sessionfree == s) + { + sessionfree = session[s].next; + } + + memcpy(&session[s], buf, l); + session[s].tbf = 0; + session[s].throttle = 0; + if (session[s].opened) + { + log(2, 0, s, session[s].tunnel, "Cluster master sent active session for user %s\n", session[s].user); + sessionsetup(session[s].tunnel, s, 0); + if (session[s].ip && session[s].ip != 0xFFFFFFFE) + { + int x; + for (x = 0; x < MAXIPPOOL && ip_address_pool[x].address; x++) + { + if (ip_address_pool[x].address == session[s].ip) + { + ip_address_pool[x].assigned = 1; + break; + } + } + } + } + return 0; +} + +int handle_hello_response(char *buf, int l) +{ + int numtunnels, numsessions; + + /* The cluster master has downed the address, so send another garp */ + send_garp(vip_address); + + if (!l) return 0; + + if (l < (4 * IL)) + { + log(1, 0, 0, 0, "Cluster master sent invalid hello response: %d bytes instead of %d\n", l, (4 * IL)); + return 0; + } + numtunnels = *(int *)(buf + IL * 0); + numsessions = *(int *)(buf + IL * 1); + if (numtunnels == 0 && numsessions == 0) + { + log(2, 0, 0, 0, "Cluster master has no state information for us.\n"); + return 0; + } + log(2, 0, 0, 0, "The cluster master will send %d tunnels and %d sessions.\n", numtunnels, numsessions); + return 0; +} + +int cluster_send_session(int s) +{ + char *packet; + int len = 0; + + if (!cluster_sockfd) return 1; + + packet = malloc(4096); + + // Hostname + len = strlen(hostname); + *(char *)packet = len; + memcpy((char *)(packet + 1), hostname, len); + len++; + + // Session ID + *(int *)(packet + len) = s; + len += sizeof(int); + + // Session data + memcpy((char *)(packet + len), &session[s], sizeof(sessiont)); + len += sizeof(sessiont); + + cluster_send_message(cluster_address, vip_address, C_SESSION, packet, len); + free(packet); + + return 1; +} + +int cluster_send_tunnel(int t) +{ + char *packet; + int len = 0; + + packet = malloc(4096); + + // Hostname + len = strlen(hostname); + *(char *)packet = len; + memcpy((char *)(packet + 1), hostname, len); + len++; + + // Tunnel ID + *(int *)(packet + len) = t; + len += sizeof(int); + + // Tunnel data + memcpy((char *)(packet + len), &tunnel[t], sizeof(tunnelt)); + len += sizeof(tunnelt); + + cluster_send_message(cluster_address, vip_address, C_TUNNEL, packet, len); + free(packet); + + return 1; +} + diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..16b6b08 --- /dev/null +++ b/config.h.in @@ -0,0 +1,213 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the `alarm' function. */ +#undef HAVE_ALARM + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `gethostbyname' function. */ +#undef HAVE_GETHOSTBYNAME + +/* Define to 1 if you have the `gethostname' function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `inet_ntoa' function. */ +#undef HAVE_INET_NTOA + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the `pow' function. */ +#undef HAVE_POW + +/* Define to 1 if you have the `select' function. */ +#undef HAVE_SELECT + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +#undef HAVE_STAT_EMPTY_STRING_BUG + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strrchr' function. */ +#undef HAVE_STRRCHR + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if the C compiler supports function prototypes. */ +#undef PROTOTYPES + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define to the type of arg 1 for `select'. */ +#undef SELECT_TYPE_ARG1 + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#undef SELECT_TYPE_ARG234 + +/* Define to the type of arg 5 for `select'. */ +#undef SELECT_TYPE_ARG5 + +/* Define to 1 if the `setvbuf' function takes the buffering type as its + second argument and the buffer pointer as the third, as on System V before + release 3. */ +#undef SETVBUF_REVERSED + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define like PROTOTYPES; this can be used by system headers. */ +#undef __PROTOTYPES + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned' if does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork + +#undef HAVE_LIBCLI +#undef HAVE_LIBM +#undef HAVE_LIBDL + +#undef LIBDIR +#undef ETCDIR +#undef BINDIR + diff --git a/configure b/configure new file mode 100755 index 0000000..52c95f2 --- /dev/null +++ b/configure @@ -0,0 +1,7102 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.58 for l2tpns 1.0.0. +# +# Report bugs to . +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME='l2tpns' +PACKAGE_TARNAME='l2tpns' +PACKAGE_VERSION='1.0.0' +PACKAGE_STRING='l2tpns 1.0.0' +PACKAGE_BUGREPORT='fred_nerk@sourceforge.net' + +ac_unique_file="ll.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP EGREP LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures l2tpns 1.0.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of l2tpns 1.0.0:";; + esac + cat <<\_ACEOF + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd "$ac_popdir" + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF +l2tpns configure 1.0.0 +generated by GNU Autoconf 2.58 + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by l2tpns $as_me 1.0.0, which was +generated by GNU Autoconf 2.58. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ac_config_headers="$ac_config_headers config.h" + + +# Checks for programs. +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +# Checks for header files. + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 +if test "${ac_cv_header_sys_wait_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_sys_wait_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_sys_wait_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + + + + + + + + + +for ac_header in arpa/inet.h fcntl.h malloc.h memory.h netdb.h netinet/in.h stdlib.h string.h sys/file.h sys/ioctl.h sys/socket.h sys/time.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pid_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + + +# Checks for library functions. + + +for ac_header in unistd.h vfork.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_func in fork vfork +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + echo "$as_me:$LINENO: checking for working fork" >&5 +echo $ECHO_N "checking for working fork... $ECHO_C" >&6 +if test "${ac_cv_func_fork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_fork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* By Ruediger Kuhlmann. */ + #include + #if HAVE_UNISTD_H + # include + #endif + /* Some systems only have a dummy stub for fork() */ + int main () + { + if (fork() < 0) + exit (1); + exit (0); + } +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 +echo "${ECHO_T}$ac_cv_func_fork_works" >&6 + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + echo "$as_me:$LINENO: checking for working vfork" >&5 +echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 +if test "${ac_cv_func_vfork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_vfork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +# include +#endif +#if HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + exit( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_vfork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_vfork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 +echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_VFORK 1 +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define vfork fork +_ACEOF + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_FORK 1 +_ACEOF + +fi + +if test $ac_cv_c_compiler_gnu = yes; then + echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + + +for ac_header in stdlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 +echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6 +if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_malloc_0_nonnull=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if STDC_HEADERS || HAVE_STDLIB_H +# include +#else +char *malloc (); +#endif + +int +main () +{ +exit (malloc (0) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_malloc_0_nonnull=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_malloc_0_nonnull=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 +echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6 +if test $ac_cv_func_malloc_0_nonnull = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 1 +_ACEOF + +else + cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 0 +_ACEOF + + case $LIBOBJS in + "malloc.$ac_objext" | \ + *" malloc.$ac_objext" | \ + "malloc.$ac_objext "* | \ + *" malloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define malloc rpl_malloc +_ACEOF + +fi + + + +echo "$as_me:$LINENO: checking for working memcmp" >&5 +echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6 +if test "${ac_cv_func_memcmp_working+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = 0x40, c1 = 0x80, c2 = 0x81; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + exit (1); + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + exit (1); + } + exit (0); + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_memcmp_working=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 +echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6 +test $ac_cv_func_memcmp_working = no && case $LIBOBJS in + "memcmp.$ac_objext" | \ + *" memcmp.$ac_objext" | \ + "memcmp.$ac_objext "* | \ + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" ;; +esac + + + + +for ac_header in stdlib.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in getpagesize +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +echo "$as_me:$LINENO: checking for working mmap" >&5 +echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 +if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_mmap_fixed_mapped=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +/* malloc might have been renamed as rpl_malloc. */ +#undef malloc + +/* Thanks to Mike Haertel and Jim Avera for this test. + Here is a matrix of mmap possibilities: + mmap private not fixed + mmap private fixed at somewhere currently unmapped + mmap private fixed at somewhere already mapped + mmap shared not fixed + mmap shared fixed at somewhere currently unmapped + mmap shared fixed at somewhere already mapped + For private mappings, we should verify that changes cannot be read() + back from the file, nor mmap's back from the file at a different + address. (There have been systems where private was not correctly + implemented like the infamous i386 svr4.0, and systems where the + VM page cache was not coherent with the file system buffer cache + like early versions of FreeBSD and possibly contemporary NetBSD.) + For shared mappings, we should conversely verify that changes get + propagated back to all the places they're supposed to be. + + Grep wants private fixed already mapped. + The main things grep needs to know about mmap are: + * does it exist and is it safe to write into the mmap'd area + * how to use it (BSD variants) */ + +#include +#include + +#if !STDC_HEADERS && !HAVE_STDLIB_H +char *malloc (); +#endif + +/* This mess was copied from the GNU getpagesize.h. */ +#if !HAVE_GETPAGESIZE +/* Assume that all systems that can run configure have sys/param.h. */ +# if !HAVE_SYS_PARAM_H +# define HAVE_SYS_PARAM_H 1 +# endif + +# ifdef _SC_PAGESIZE +# define getpagesize() sysconf(_SC_PAGESIZE) +# else /* no _SC_PAGESIZE */ +# if HAVE_SYS_PARAM_H +# include +# ifdef EXEC_PAGESIZE +# define getpagesize() EXEC_PAGESIZE +# else /* no EXEC_PAGESIZE */ +# ifdef NBPG +# define getpagesize() NBPG * CLSIZE +# ifndef CLSIZE +# define CLSIZE 1 +# endif /* no CLSIZE */ +# else /* no NBPG */ +# ifdef NBPC +# define getpagesize() NBPC +# else /* no NBPC */ +# ifdef PAGESIZE +# define getpagesize() PAGESIZE +# endif /* PAGESIZE */ +# endif /* no NBPC */ +# endif /* no NBPG */ +# endif /* no EXEC_PAGESIZE */ +# else /* no HAVE_SYS_PARAM_H */ +# define getpagesize() 8192 /* punt totally */ +# endif /* no HAVE_SYS_PARAM_H */ +# endif /* no _SC_PAGESIZE */ + +#endif /* no HAVE_GETPAGESIZE */ + +int +main () +{ + char *data, *data2, *data3; + int i, pagesize; + int fd; + + pagesize = getpagesize (); + + /* First, make a file with some known garbage in it. */ + data = (char *) malloc (pagesize); + if (!data) + exit (1); + for (i = 0; i < pagesize; ++i) + *(data + i) = rand (); + umask (0); + fd = creat ("conftest.mmap", 0600); + if (fd < 0) + exit (1); + if (write (fd, data, pagesize) != pagesize) + exit (1); + close (fd); + + /* Next, try to mmap the file at a fixed address which already has + something else allocated at it. If we can, also make sure that + we see the same garbage. */ + fd = open ("conftest.mmap", O_RDWR); + if (fd < 0) + exit (1); + data2 = (char *) malloc (2 * pagesize); + if (!data2) + exit (1); + data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); + if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + exit (1); + + /* Finally, make sure that changes to the mapped area do not + percolate back to the file as seen by read(). (This is a bug on + some variants of i386 svr4.0.) */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = (char *) malloc (pagesize); + if (!data3) + exit (1); + if (read (fd, data3, pagesize) != pagesize) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + exit (1); + close (fd); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_mmap_fixed_mapped=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_mmap_fixed_mapped=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 +echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 +if test $ac_cv_func_mmap_fixed_mapped = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MMAP 1 +_ACEOF + +fi +rm -f conftest.mmap + + + +for ac_header in sys/select.h sys/socket.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking types of arguments for select" >&5 +echo $ECHO_N "checking types of arguments for select... $ECHO_C" >&6 +if test "${ac_cv_func_select_args+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + for ac_arg234 in 'fd_set *' 'int *' 'void *'; do + for ac_arg1 in 'int' 'size_t' 'unsigned long' 'unsigned'; do + for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#if HAVE_SYS_SELECT_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif + +int +main () +{ +extern int select ($ac_arg1, + $ac_arg234, $ac_arg234, $ac_arg234, + $ac_arg5); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done + done +done +# Provide a safe default value. +: ${ac_cv_func_select_args='int,int *,struct timeval *'} + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_select_args" >&5 +echo "${ECHO_T}$ac_cv_func_select_args" >&6 +ac_save_IFS=$IFS; IFS=',' +set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` +IFS=$ac_save_IFS +shift + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG1 $1 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG234 ($2) +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG5 ($3) +_ACEOF + +rm -f conftest* + +echo "$as_me:$LINENO: checking for function prototypes" >&5 +echo $ECHO_N "checking for function prototypes... $ECHO_C" >&6 +if test "$ac_cv_prog_cc_stdc" != no; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define PROTOTYPES 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define __PROTOTYPES 1 +_ACEOF + +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +echo "$as_me:$LINENO: checking whether setvbuf arguments are reversed" >&5 +echo $ECHO_N "checking whether setvbuf arguments are reversed... $ECHO_C" >&6 +if test "${ac_cv_func_setvbuf_reversed+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_func_setvbuf_reversed=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +# if PROTOTYPES + int (setvbuf) (FILE *, int, char *, size_t); +# endif +int +main () +{ +char buf; return setvbuf (stdout, _IOLBF, &buf, 1); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +# if PROTOTYPES + int (setvbuf) (FILE *, int, char *, size_t); +# endif +int +main () +{ +char buf; return setvbuf (stdout, &buf, _IOLBF, 1); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It compiles and links either way, so it must not be declared + # with a prototype and most likely this is a K&R C compiler. + # Try running it. + if test "$cross_compiling" = yes; then + : # Assume setvbuf is not reversed when cross-compiling. +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +/* This call has the arguments reversed. + A reversed system may check and see that the address of buf + is not _IOLBF, _IONBF, or _IOFBF, and return nonzero. */ + char buf; + if (setvbuf (stdout, _IOLBF, &buf, 1) != 0) + exit (1); + putchar ('\r'); + exit (0); /* Non-reversed systems SEGV here. */ + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_setvbuf_reversed=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +rm -f core *.core +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + ac_cv_func_setvbuf_reversed=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_setvbuf_reversed" >&5 +echo "${ECHO_T}$ac_cv_func_setvbuf_reversed" >&6 +if test $ac_cv_func_setvbuf_reversed = yes; then + +cat >>confdefs.h <<\_ACEOF +#define SETVBUF_REVERSED 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifdef signal +# undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_signal=void +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_signal=int +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + +echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 +echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6 +if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f conftest.sym conftest.file +echo >conftest.file +if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_dereferences_slashed_symlink=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + /* Linux will dereference the symlink and fail. + That is better in the sense that it means we will not + have to compile and use the lstat wrapper. */ + exit (lstat ("conftest.sym/", &sbuf) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_dereferences_slashed_symlink=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +else + # If the `ln -s' command failed, then we probably don't even + # have an lstat function. + ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f conftest.sym conftest.file + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6 + +test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && + +cat >>confdefs.h <<_ACEOF +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +_ACEOF + + +if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then + case $LIBOBJS in + "lstat.$ac_objext" | \ + *" lstat.$ac_objext" | \ + "lstat.$ac_objext "* | \ + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; +esac + +fi + +echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5 +echo $ECHO_N "checking whether stat accepts an empty string... $ECHO_C" >&6 +if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_stat_empty_string_bug=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + exit (stat ("", &sbuf) ? 1 : 0); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_stat_empty_string_bug=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_stat_empty_string_bug=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5 +echo "${ECHO_T}$ac_cv_func_stat_empty_string_bug" >&6 +if test $ac_cv_func_stat_empty_string_bug = yes; then + case $LIBOBJS in + "stat.$ac_objext" | \ + *" stat.$ac_objext" | \ + "stat.$ac_objext "* | \ + *" stat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS stat.$ac_objext" ;; +esac + + +cat >>confdefs.h <<_ACEOF +#define HAVE_STAT_EMPTY_STRING_BUG 1 +_ACEOF + +fi + + +for ac_func in strftime +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + # strftime is in -lintl on SCO UNIX. +echo "$as_me:$LINENO: checking for strftime in -lintl" >&5 +echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 +if test "${ac_cv_lib_intl_strftime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lintl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime (); +int +main () +{ +strftime (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_intl_strftime=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_intl_strftime=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5 +echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 +if test $ac_cv_lib_intl_strftime = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_STRFTIME 1 +_ACEOF + +LIBS="-lintl $LIBS" +fi + +fi +done + + +for ac_func in vprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +echo "$as_me:$LINENO: checking for _doprnt" >&5 +echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6 +if test "${ac_cv_func__doprnt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define _doprnt to an innocuous variant, in case declares _doprnt. + For example, HP-UX 11i declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef _doprnt + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +char (*f) () = _doprnt; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != _doprnt; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func__doprnt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func__doprnt=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 +echo "${ECHO_T}$ac_cv_func__doprnt" >&6 +if test $ac_cv_func__doprnt = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DOPRNT 1 +_ACEOF + +fi + +fi +done + + + + + + + + + + + + + + + + +for ac_func in alarm gethostbyname gethostname gettimeofday inet_ntoa memset pow select socket strcasecmp strchr strdup strerror strrchr +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# Checks for libraries. + +echo "$as_me:$LINENO: checking for cli_init in -lcli" >&5 +echo $ECHO_N "checking for cli_init in -lcli... $ECHO_C" >&6 +if test "${ac_cv_lib_cli_cli_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcli $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char cli_init (); +int +main () +{ +cli_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_cli_cli_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_cli_cli_init=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_cli_cli_init" >&5 +echo "${ECHO_T}$ac_cv_lib_cli_cli_init" >&6 +if test $ac_cv_lib_cli_cli_init = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCLI 1 +_ACEOF + + LIBS="-lcli $LIBS" + +fi + + +echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBDL 1 +_ACEOF + + LIBS="-ldl $LIBS" + +fi + + +echo "$as_me:$LINENO: checking for pow in -lm" >&5 +echo $ECHO_N "checking for pow in -lm... $ECHO_C" >&6 +if test "${ac_cv_lib_m_pow+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pow (); +int +main () +{ +pow (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_m_pow=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_m_pow=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_m_pow" >&5 +echo "${ECHO_T}$ac_cv_lib_m_pow" >&6 +if test $ac_cv_lib_m_pow = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBM 1 +_ACEOF + + LIBS="-lm $LIBS" + +fi + + +cat >>confdefs.h <<_ACEOF +#define LIBDIR "$prefix/lib/l2tpns" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define ETCDIR "$sysconfdir" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define BINDIR "$prefix/bin" +_ACEOF + + + ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by l2tpns $as_me 1.0.0, which was +generated by GNU Autoconf 2.58. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +l2tpns config.status 1.0.0 +configured by $0, generated by GNU Autoconf 2.58, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..f7b8b81 --- /dev/null +++ b/configure.in @@ -0,0 +1,48 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.58) +AC_INIT([l2tpns], [1.0.0], [fred_nerk@sourceforge.net]) +AC_CONFIG_SRCDIR([ll.c]) +AC_CONFIG_HEADER([config.h]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL + +# Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h memory.h netdb.h netinet/in.h stdlib.h string.h sys/file.h sys/ioctl.h sys/socket.h sys/time.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_HEADER_TIME + +# Checks for library functions. +AC_FUNC_FORK +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MALLOC +AC_FUNC_MEMCMP +AC_FUNC_MMAP +AC_FUNC_SELECT_ARGTYPES +AC_FUNC_SETVBUF_REVERSED +AC_TYPE_SIGNAL +AC_FUNC_STAT +AC_FUNC_STRFTIME +AC_FUNC_VPRINTF +AC_CHECK_FUNCS([alarm gethostbyname gethostname gettimeofday inet_ntoa memset pow select socket strcasecmp strchr strdup strerror strrchr]) + +# Checks for libraries. +AC_CHECK_LIB([cli], [cli_init]) +AC_CHECK_LIB([dl], [dlopen]) +AC_CHECK_LIB([m], [pow]) + +AC_DEFINE_UNQUOTED(LIBDIR, ["$prefix/lib/l2tpns"]) +AC_DEFINE_UNQUOTED(ETCDIR, ["$sysconfdir"]) +AC_DEFINE_UNQUOTED(BINDIR, ["$prefix/bin"]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/conform.cfg b/conform.cfg new file mode 100644 index 0000000..fabea05 --- /dev/null +++ b/conform.cfg @@ -0,0 +1,137 @@ +#!/usr/bin/perl -w +# vim:ft=perl + +die "l2tpns requires RedHat 7.3 or above" if i_isa("SOE_linux_rh6"); + +my $restart = 0; + +my %conf = (); +for my $c (i_isa_fetchall('L2tpns_config')) { + foreach my $opt (keys %$c) { + if (ref $conf{$opt} and ref $conf{$opt} eq 'ARRAY') { + $c->{$opt} = [ $c->{$opt} ] unless ref $c->{$opt}; + push @{$conf{$opt}}, @{$c->{$opt}}; + } elsif (ref $c->{$opt} and ref $c->{$opt} eq 'ARRAY') { + # Make sure to copy to avoid changing /etc/machine + $conf{$opt} = [ $conf{$opt} ] if $conf{$opt}; + $conf{$opt} ||= []; + push @{$conf{$opt}}, @{$c->{$opt}}; + } else { + $conf{$opt} = $c->{$opt}; + } + } +} + +$conf{Address_pool} ||= i_isa("Address_pool"); # backwards compat + +unless (i_isa("No_throttle")) { + chomp(my $kernel = `uname -r`); + print "WARN: l2tpns requires kernel 2.4.18-187OIE1. This is included in $_path/rpm/kernel-2.4.18-187OIE1.i386.rpm\n" + unless ($kernel eq '2.4.18-187OIE1' || $kernel =~ /^2\.4\.2\d/); +} + +# Recompile the server if needed +if ((stat("src/l2tpns.c"))[9] > (stat("src/l2tpns"))[9]) { + chdir("src"); + command("make clean"); + command("make"); + chdir(".."); + $restart++; +} + +command("mkdir /dev/net") and ++$restart unless -d "/dev/net"; +command("mknod /dev/net/tun c 10 200") and ++$restart unless -c "/dev/net/tun"; + +my $cluster = i_isa('Gateway_cluster'); +my $cluster_name = $cluster->{master} || $cluster->{slave} || die 'Not a master or a slave' if $cluster; +my $master = $cluster && $cluster->{master}; +my $command = $master ? "cluster_master" : "l2tpns"; +push @{$m{$_class}->{Monitor}->{process}->{tests}}, $command; + + +if ($cluster) { + $conf{'save state'} ||= 'no'; + if (!$master && !$cluster->{bind_address}) { + die 'No bind address for cluster slave'; + } + $conf{'bind address'} ||= $cluster->{bind_address} unless $master; + my $cluster_master; + my @cluster_slaves = (); + my @cluster_slave_addresses = (); + foreach my $host (type_list('Gateway_cluster')) { + my $host_conf = OIE::Conform::i_isa(\%m, $host, 'Gateway_cluster'); + if ($host_conf->{master} eq $cluster_name) { + $cluster_master = $host; + } elsif ($host_conf->{slave} eq $cluster_name) { + push @cluster_slaves, $host; + push @{$conf{Address_pool}}, map { "$host_conf->{bind_address}:$_" } @{$m{$host}->{L2tpns_config}->{Address_pool}} if $master; + push @cluster_slave_addresses, $m{$host}->{int_eth0}->{ip}; + } + } + + if ($master) { + push @{$m{$_class}->{inittab_include}}, + "$_path/src/cluster_master $m{$iam}->{int_eth0}->{ip}"; + push @{$m{$_class}->{inittab_disable}}, + "$_path/src/l2tpns"; + $m{$_class}->{Firewall}->{$_} = '32792:udp' + foreach @cluster_slave_addresses; + } + $conf{'cluster master'} ||= $m{$cluster_master}->{int_eth0}->{ip}; +} + +# Build up address pool +my $pool = $conf{Address_pool}; +if ($pool) { + my $address_pool = ""; + + foreach (@$pool) { + $address_pool .= "$_\n"; + } + + text_install("$_path/etc/ip_pool.txt", $address_pool) and $restart++; +} else { + print "WARN: No Address_pool defined in machines.\n"; +} +delete $conf{"Address_pool"}; # Don't add it to the conf file + +my $servicenet = $conf{"servicenet"}; +if ($servicenet) { + $conf{'servicenet'} = 'yes'; + push @{$conf{plugin}}, 'servicenet' unless grep /^servicenet$/, @{$conf{plugin}}; + file_install("/etc/rc.d/rc.firewall.INPUT.servicenet", "$_path/etc/rc.firewall.INPUT.servicenet", undef, undef, + "s/#SERVICENET#/$servicenet/g") + and queue_command("/etc/rc.d/rc.firewall"); +} else { + $conf{'servicenet'} = 'no'; + # Uninstall + if (-f "/etc/rc.d/rc.firewall.INPUT.servicenet") { + unlink "/etc/rc.d/rc.firewall.INPUT.servicenet"; + command("iptables -F snet"); + } +} + +# Note that we don't file_install the config file, but instead modify it +# in place + +my $config = slurp_file("$_path/etc/l2tpns.cfg"); + +# plugins need to go first, else they won't pick up params +foreach my $p (@{$conf{plugin}}) { + $config =~ s/^#?\s*plugin\s+=\s+\Q$p\E$/plugin = $p/mg or + $config = "plugin = $p\n\n$config"; +} +delete $conf{plugin}; + +foreach my $c (keys %conf) { + $config =~ s/^#?\s*\Q$c\E\s+=\s+.*$/$c = $conf{$c}/mg or + $config .= "$c = $conf{$c}\n\n"; +} + +file_install("/etc/rc.d/rc.firewall.INPUT.l2tpns", "$_path/etc/rc.firewall.INPUT.l2tpns") + and queue_command("/etc/rc.d/rc.firewall"); + +text_install("$_path/etc/l2tpns.cfg", $config) and $restart++; + +queue_command("killall $command") if $restart; + diff --git a/constants.c b/constants.c new file mode 100644 index 0000000..46258b1 --- /dev/null +++ b/constants.c @@ -0,0 +1,136 @@ +#include "constants.h" +#include + +const char *lcp_types[MAX_LCP_TYPE+1] = { + "Reserved", + "Maximum-Receive-Unit", + "Reserved 2", + "Authentication-Protocol", + "Quality-Protocol", + "Magic-Number", + "Reserved 6", + "Protocol-Field-Compression", + "Address-and-Control-Field-Compression", +}; + +const char *avpnames[MAX_AVPNAME+1] = { + "Message Type", // 0 + "Result Code", // 1 + "Protocol Version", // 2 + "Framing Capabilities", // 3 + "Bearer Capabilities", // 4 + "Tie Breaker", // 5 + "Firmware Revision", // 6 + "Host Name", // 7 + "Vendor Name", // 8 + "Assigned Tunnel ID", // 9 + "Receive Window Size", // 10 + "Challenge", // 11 + "Q.931 Cause Code", // 12 + "Challenge Response", // 13 + "Assigned Session ID", // 14 + "Call Serial Number", // 15 + "Minimum BPS", // 16 + "Maximum BPS", // 17 + "Bearer Type", // 18 (2 = Analog, 1 = Digital) + "Framing Type", // 19 (2 = Async, 1 = Sync) + "Reserved 20", // 20 + "Called Number", // 21 + "Calling Number", // 22 + "Sub Address", // 23 + "Tx Connect Speed", // 24 + "Physical Channel ID", // 25 + "Initial Received LCP CONFREQ", // 26 + "Last Sent LCP CONFREQ", // 27 + "Last Received LCP CONFREQ", // 28 + "Proxy Authen Type", // 29 + "Proxy Authen Name", // 30 + "Proxy Authen Challenge", // 31 + "Proxy Authen ID", // 32 + "Proxy Authen Response", // 33 + "Call Errors", // 34 + "ACCM", // 35 + "Random Vector", // 36 + "Private Group ID", // 37 + "Rx Connect Speed", // 38 + "Sequencing Required", // 39 +}; + +const char *stopccn_result_codes[MAX_STOPCCN_RESULT_CODE+1] = { + "Reserved", + "General request to clear control connection", + "General error--Error Code indicates the problem", + "Control channel already exists", + "Requester is not authorized to establish a control channel", + "The protocol version of the requester is not supported", + "Requester is being shut down", + "Finite State Machine error", +}; + +const char *cdn_result_codes[MAX_CDN_RESULT_CODE+1] = { + "Reserved", + "Call disconnected due to loss of carrier", + "Call disconnected for the reason indicated in error code", + "Call disconnected for administrative reasons", + "Call failed due to lack of appropriate facilities being available (temporary condition)", + "Call failed due to lack of appropriate facilities being available (permanent condition)", + "Invalid destination", + "Call failed due to no carrier detected", + "Call failed due to detection of a busy signal", + "Call failed due to lack of a dial tone", + "Call was not established within time allotted by LAC", + "Call was connected but no appropriate framing was detected", +}; + +const char *error_codes[MAX_ERROR_CODE+1] = { + "No general error", + "No control connection exists yet for this LAC-LNS pair", + "Length is wrong", + "One of the field values was out of range or reserved field was non-zero", + "Insufficient resources to handle this operation now", + "The Session ID is invalid in this context", + "A generic vendor-specific error occurred in the LAC", + "Try another LNS", + "Session or tunnel was shutdown due to receipt of an unknown AVP with the M-bit set", +}; + +const char *authtypes[MAX_AUTHTYPE+1] = { + "Reserved", + "Textual username/password exchange", + "PPP CHAP", + "PPP PAP", + "No Authentication", + "Microsoft CHAP Version 1 (MSCHAPv1)", +}; + +const char *radius_states[MAX_RADIUS_STATE+1] = { + "RADIUSNULL", + "RADIUSCHAP", + "RADIUSAUTH", + "RADIUSIPCP", + "RADIUSSTART", + "RADIUSSTOP", + "RADIUSWAIT", + NULL +}; + +const char *l2tp_message_types[MAX_L2TP_MESSAGE_TYPE+1] = { + "reserved", + "SCCRQ", + "SCCRP", + "SCCCN", + "StopCCN", // 4 + "reserved", + "HELLO", + "OCRQ", + "OCRP", + "OCCN", + "ICRQ", // 10 + "ICRP", + "ICCN", + "reserved", + "CDN", + "WEN", // 15 + "SLI", +}; + diff --git a/constants.h b/constants.h new file mode 100644 index 0000000..3ac8983 --- /dev/null +++ b/constants.h @@ -0,0 +1,27 @@ + +// enum these ? + +#define MAX_LCP_TYPE 8 +extern const char *lcp_types[MAX_LCP_TYPE+1]; + +#define MAX_AVPNAME 40 +extern const char *avpnames[MAX_AVPNAME+1]; + +#define MAX_STOPCCN_RESULT_CODE 7 +extern const char *stopccn_result_codes[MAX_STOPCCN_RESULT_CODE+1]; + +#define MAX_CDN_RESULT_CODE 11 +extern const char *cdn_result_codes[MAX_CDN_RESULT_CODE+1]; + +#define MAX_ERROR_CODE 8 +extern const char *error_codes[MAX_ERROR_CODE+1]; + +#define MAX_AUTHTYPE 5 +extern const char *authtypes[MAX_AUTHTYPE+1]; + +// Can remove the NULL end now +#define MAX_RADIUS_STATE 8 +extern const char *radius_states[MAX_RADIUS_STATE+1]; + +#define MAX_L2TP_MESSAGE_TYPE 16 +extern const char *l2tp_message_types[MAX_L2TP_MESSAGE_TYPE+1]; diff --git a/control.c b/control.c new file mode 100644 index 0000000..9d83522 --- /dev/null +++ b/control.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "control.h" + +int new_packet(short type, char *packet) +{ + int id = (time(NULL) ^ (rand() * 1024*1024)); + + *(short *)(packet + 0) = ntohs(0x9012); + *(short *)(packet + 2) = ntohs(type); + *(int *)(packet + 6) = ntohl(id); + + return 10; +} + +int send_packet(int sockfd, int dest_ip, int dest_port, char *packet, int len) +{ + struct sockaddr_in addr; + + *(short *)(packet + 4) = ntohs(len); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + *(int*)&addr.sin_addr = htonl(dest_ip); + addr.sin_port = htons(dest_port); + if (sendto(sockfd, packet, len, 0, (void *) &addr, sizeof(addr)) < 0) + { + perror("sendto"); + return 0; + } + return 1; +} + +int read_packet(int sockfd, char *packet) +{ + struct sockaddr_in addr; + int alen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + return recvfrom(sockfd, packet, 1400, 0, (void *) &addr, &alen); +} + +void dump_packet(char *packet, FILE *stream) +{ + if (htons(*(short *)(packet + 0)) != 0x9012) + { + fprintf(stream, "Invalid packet identifier %x\n", htons(*(short *)(packet + 0))); + return; + } + fprintf(stream, "Control packet:\n"); + fprintf(stream, " Type: %d\n", htons(*(short *)(packet + 2))); + fprintf(stream, " Length: %d\n", htons(*(short *)(packet + 4))); + fprintf(stream, " Identifier: %x\n", htonl(*(int *)(packet + 6))); + fprintf(stream, "\n"); +} + + diff --git a/control.h b/control.h new file mode 100644 index 0000000..041d3ce --- /dev/null +++ b/control.h @@ -0,0 +1,18 @@ +#ifndef __CONTROL_H__ +#define __CONTROL_H__ + +#define PKT_RESP_OK 1 +#define PKT_RESP_ERROR 2 + +#define PKT_LOAD_PLUGIN 5 +#define PKT_UNLOAD_PLUGIN 6 + +#define PKT_GARDEN 1000 +#define PKT_UNGARDEN 1001 + +int new_packet(short type, char *packet); +int send_packet(int sockfd, int dest_ip, int dest_port, char *packet, int len); +void dump_packet(char *packet, FILE *stream); +int read_packet(int sockfd, char *packet); + +#endif diff --git a/etc/.cvsignore b/etc/.cvsignore new file mode 100644 index 0000000..050550f --- /dev/null +++ b/etc/.cvsignore @@ -0,0 +1,2 @@ +ip_pool.txt +l2tpns.cfg diff --git a/etc/ip_pool.default b/etc/ip_pool.default new file mode 100644 index 0000000..9ae05e9 --- /dev/null +++ b/etc/ip_pool.default @@ -0,0 +1,2 @@ +10.10.10.0/24 +10.13.10.0/24 diff --git a/etc/l2tpns.cfg.default b/etc/l2tpns.cfg.default new file mode 100644 index 0000000..2620ce0 --- /dev/null +++ b/etc/l2tpns.cfg.default @@ -0,0 +1,43 @@ +# Comment out to log to stderr +log file = /var/log/l2tpns + +# This must be defined, or it won't work +# It's only used if the LAC requests tunnel authentication +l2tp secret = foobar + +# You can have multiple radius server entries, but ony one radius secret +radius server = radius.yourdomain.com.au +radius secret = barfoo + +# Turn on or off Radius Accounting (START and STOP records) +radius accounting = 0 + +# Only 2 dns server entries are allowed +dns server = 192.168.1.1 +dns server = 192.168.1.2 + +# Set this to 0 to disable throttling +throttle rate = 0 + +# This can be from 1 to 5 +# At 5, all packets are logged and your system will run +# very slowly +# 2 will show errors only +debug = 2 + +# Save / load state on restart +save state = no + +# Cluster Management +#cluster master = 192.168.1.15 + +# Where accounting information will be dumped. Comment out to disable. +accounting dir = /var/run/l2tpns/acct/ + +# You need to set this to the IP address of the tun interface +# if you want to use clustering +#bind address = 127.0.0.1 + +# Uncomment this if you wish to use the walled garden plugin +#plugin = garden + diff --git a/etc/l2tpns.logrotate b/etc/l2tpns.logrotate new file mode 100644 index 0000000..c6cf5e2 --- /dev/null +++ b/etc/l2tpns.logrotate @@ -0,0 +1,8 @@ +/var/log/l2tpns { + daily + missingok + rotate 14 + postrotate + /usr/bin/killall -HUP l2tpns + endscript +} diff --git a/etc/users.default b/etc/users.default new file mode 100644 index 0000000..dd67351 --- /dev/null +++ b/etc/users.default @@ -0,0 +1 @@ +# List username:password combinations here for cli users diff --git a/garden.c b/garden.c new file mode 100644 index 0000000..064bea2 --- /dev/null +++ b/garden.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "plugin.h" +#include "control.h" + +int __plugin_api_version = 1; +struct pluginfuncs p; + +int garden_session(sessiont *s, int flag); + +char *init_commands[] = { + // This is for incoming connections to a gardened user + "iptables -t nat -N garden_users 2>&1 >/dev/null", + "iptables -t nat -F garden_users 2>&1 >/dev/null", + "iptables -t nat -N garden 2>&1 >/dev/null", + "iptables -t nat -A l2tpns -j garden_users", + NULL +}; + +char *done_commands[] = { + "iptables -t nat -F garden_users 2>&1 >/dev/null", + "iptables -t nat -D l2tpns -j garden_users 2>&1 >/dev/null", + NULL +}; + +int plugin_post_auth(struct param_post_auth *data) +{ + // Ignore if user authentication was successful + if (data->auth_allowed) return PLUGIN_RET_OK; + + p.log(3, 0, 0, 0, "User allowed into walled garden\n"); + data->auth_allowed = 1; + data->s->walled_garden = 1; + return PLUGIN_RET_OK; +} + +int plugin_new_session(struct param_new_session *data) +{ + if (data->s->walled_garden) garden_session(data->s, 1); + return PLUGIN_RET_OK; +} + +int plugin_kill_session(struct param_new_session *data) +{ + if (data->s->walled_garden) garden_session(data->s, 0); + return PLUGIN_RET_OK; +} + +int plugin_control(struct param_control *data) +{ + sessiont *s; + sessionidt session; + + if (data->type != PKT_GARDEN && data->type != PKT_UNGARDEN) return PLUGIN_RET_OK; + if (!data->data && data->data_length) return PLUGIN_RET_OK; + session = atoi((char*)(data->data)); + + if (!session) + return PLUGIN_RET_OK; + + data->send_response = 1; + s = p.get_session_by_id(session); + if (!s || !s->ip) + { + char *errormsg = "Session not connected"; + *(short *)(data->response + 2) = ntohs(PKT_RESP_ERROR); + sprintf((data->response + data->response_length), "%s", errormsg); + data->response_length += strlen(errormsg) + 1; + + p.log(3, 0, 0, 0, "Unknown session %s\n", session); + return PLUGIN_RET_STOP; + } + *(short *)(data->response + 2) = ntohs(PKT_RESP_OK); + + if (!(garden_session(s, (data->type == PKT_GARDEN)))) + { + char *errormsg = "User not connected"; + *(short *)(data->response + 2) = ntohs(PKT_RESP_ERROR); + sprintf((data->response + data->response_length), "%s", errormsg); + data->response_length += strlen(errormsg) + 1; + } + return PLUGIN_RET_STOP; +} + +int plugin_config(struct param_config *data) +{ + return PLUGIN_RET_OK; +} + +int garden_session(sessiont *s, int flag) +{ + char cmd[2048]; + + if (!s) return 0; + if (!s->opened) return 0; + + if (flag == 1) + { + p.log(2, 0, 0, s->tunnel, "Trap user %s (%s) in walled garden\n", s->user, p.inet_toa(ntohl(s->ip))); + snprintf(cmd, 2048, "iptables -t nat -A garden_users -s %s -j garden", p.inet_toa(ntohl(s->ip))); + p.log(3, 0, 0, s->tunnel, "%s\n", cmd); + system(cmd); + s->walled_garden = 1; + } + else + { + sessionidt other; + int count = 10; + + // Normal User + p.log(2, 0, 0, s->tunnel, "Release user %s (%s) from walled garden\n", s->user, p.inet_toa(ntohl(s->ip))); + // Kick off any duplicate usernames + // but make sure not to kick off ourself + if (s->ip && !s->die && (other = p.get_session_by_username(s->user)) && s != p.get_session_by_id(other)) { + p.sessionkill(other, "Duplicate session when user ungardened"); + } + /* Clean up counters */ + s->cin = s->cout = 0; + s->pin = s->pout = 0; + + snprintf(cmd, 2048, "iptables -t nat -D garden_users -s %s -j garden", p.inet_toa(ntohl(s->ip))); + p.log(3, 0, 0, s->tunnel, "%s\n", cmd); + while (--count) + { + int status = system(cmd); + if (WEXITSTATUS(status) != 0) break; + } + + s->walled_garden = 0; + + if (!s->die) { + /* OK, we're up! */ + u8 r = p.radiusnew(p.get_id_by_session(s)); + p.radiussend(r, RADIUSSTART); + } + } + s->walled_garden = flag; + return 1; +} + +int plugin_init(struct pluginfuncs *funcs) +{ + int i; + + if (!funcs) return 0; + memcpy(&p, funcs, sizeof(p)); + + p.log(1, 0, 0, 0, "Enabling walled garden service\n"); + + for (i = 0; init_commands[i] && *init_commands[i]; i++) + { + p.log(4, 0, 0, 0, "Running %s\n", init_commands[i]); + system(init_commands[i]); + } + + return 1; +} + +void plugin_done() +{ + int i; + for (i = 0; done_commands[i] && *done_commands[i]; i++) + { + p.log(4, 0, 0, 0, "Running %s\n", done_commands[i]); + system(done_commands[i]); + } +} + diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..ab74c88 --- /dev/null +++ b/install-sh @@ -0,0 +1,238 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +tranformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/l2tpns.c b/l2tpns.c new file mode 100644 index 0000000..40f4eed --- /dev/null +++ b/l2tpns.c @@ -0,0 +1,2988 @@ +// L2TP Network Server +// Adrian Kennard 2002 +// (c) Copyrigth 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) +// vim: sw=8 ts=8 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBCLI +#include +#endif +#include "md5.h" +#include "l2tpns.h" +#include "cluster.h" +#include "plugin.h" +#include "ll.h" +#include "constants.h" +#include "control.h" +#include "util.h" + +ipt radiusserver[MAXRADSERVER]; // radius servers +u8 numradiusservers = 0; // how many radius servers + +// Globals +char tapdevice[10] = ""; // tap device name +int tapfd = -1; // tap interface file handle +int udpfd = -1; // UDP file handle +int controlfd = -1; // Control signal handle +int snoopfd = -1; // UDP file handle for sending out intercept data +int radfd = -1; // RADIUS requests file handle +int ifrfd = -1; // File descriptor for routing, etc +char debug = 0; // debug leveL +time_t basetime = 0; // base clock +char hostname[1000] = ""; // us. +ipt myip = 0; // MY IP +u16 tapmac[3]; // MAC of tap interface +int tapidx; // ifr_ifindex of tap device +char *radiussecret = 0; // RADIUS secret +char *l2tpsecret = 0; // L2TP secret +u32 sessionid = 0; // session id for radius accounting +char *snoop_destination_host = NULL; +u16 snoop_destination_port = 0; +char *log_filename = NULL; +char *config_file = CONFIGFILE; +FILE *log_stream = NULL; +unsigned long default_dns1 = 0, default_dns2 = 0; +struct sockaddr_in snoop_addr = {0}; +extern unsigned long rl_rate; +extern int cluster_sockfd; +unsigned long last_sid = 0; +int config_save_state = 0; +int radius_accounting = 0; +char *accounting_dir = NULL; +uint32_t cluster_address = 0; +uint32_t bind_address = INADDR_ANY; +int handle_interface = 0; +#ifdef HAVE_LIBCLI +pid_t cli_pid = 0; +int clifd = 0; +sessionidt *cli_session_kill = NULL; +tunnelidt *cli_tunnel_kill = NULL; +#endif +static void *ip_hash[256]; +unsigned long udp_tx = 0, udp_rx = 0, udp_rx_pkt = 0; +unsigned long eth_tx = 0, eth_rx = 0, eth_rx_pkt = 0; +unsigned int ip_pool_index = 0; +unsigned int ip_pool_size = 0; +time_t time_now; +char time_now_string[64] = {0}; +char main_quit = 0; +int dump_speed = 0; +int target_uid = 500; +char *_program_name = NULL; +linked_list *loaded_plugins; +linked_list *plugins[MAX_PLUGIN_TYPES]; + +char *plugin_functions[] = { + NULL, + "plugin_pre_auth", + "plugin_post_auth", + "plugin_packet_rx", + "plugin_packet_tx", + "plugin_timer", + "plugin_config", + "plugin_new_session", + "plugin_kill_session", + "plugin_control", + "plugin_radius_response", +}; +#define max_plugin_functions (sizeof(plugin_functions) / sizeof(char *)) + +tunnelt *tunnel = NULL; // 1000 * 45 = 45000 = 45k +sessiont *session = NULL; // 5000 * 213 = 1065000 = 1 Mb +radiust *radius = NULL; +ippoolt *ip_address_pool = NULL; +tunnelidt tunnelfree; // free list link heads +sessionidt sessionfree = 0; +u8 radiusfree; +controlt *controlfree = 0; +struct Tstats *_statistics = NULL; +#ifdef RINGBUFFER +struct Tringbuffer *ringbuffer = NULL; +#endif +tbft *filter_buckets = NULL; + +void sigalrm_handler(int); +void sighup_handler(int); +void sigterm_handler(int); +void sigquit_handler(int); +void sigchild_handler(int); +void sigsegv_handler(int); +void read_config_file(); +void read_state(); +void dump_state(); + +// return internal time (10ths since run) +clockt now(void) +{ + struct timeval t; + gettimeofday(&t, 0); + return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1; +} + +// work out a retry time based on try number +clockt backoff(u8 try) +{ + if (try > 5) try = 5; // max backoff + return now() + 10 * (1 << try); +} + +void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...) +{ + va_list ap; + +#ifdef RINGBUFFER + if (ringbuffer) + { + if (++ringbuffer->tail >= RINGBUFFER_SIZE) + ringbuffer->tail = 0; + if (ringbuffer->tail == ringbuffer->head) + if (++ringbuffer->head >= RINGBUFFER_SIZE) + ringbuffer->head = 0; + + ringbuffer->buffer[ringbuffer->tail].level = level; + ringbuffer->buffer[ringbuffer->tail].address = address; + ringbuffer->buffer[ringbuffer->tail].session = s; + ringbuffer->buffer[ringbuffer->tail].tunnel = t; + va_start(ap, format); + vsnprintf(ringbuffer->buffer[ringbuffer->tail].message, 4095, format, ap); + va_end(ap); + } +#endif + + if (debug < level) return; + + if (!log_stream && log_filename) + { + if ((log_stream = fopen(log_filename, "a"))) + fseek(log_stream, 0, SEEK_END); + setbuf(log_stream, NULL); + } + if (!log_stream) + { + log_stream = stderr; + setbuf(log_stream, NULL); + } + + va_start(ap, format); + fprintf(log_stream, "%s %02d/%02d ", time_now_string, t, s); + vfprintf(log_stream, format, ap); + va_end(ap); +} + +void _log_hex(int level, ipt address, sessionidt s, tunnelidt t, const char *title, const char *data, int maxsize) +{ + unsigned int i, j; + unsigned const char *d = (unsigned const char *)data; + + if (debug < level) return; + log(level, address, s, t, "%s (%d bytes):\n", title, maxsize); + setvbuf(log_stream, NULL, _IOFBF, 16384); + for (i = 0; i < maxsize; ) + { + fprintf(log_stream, "%4X: ", i); + for (j = i; j < maxsize && j < (i + 16); j++) + { + fprintf(log_stream, "%02X ", d[j]); + if (j == i + 7) + fputs(": ", log_stream); + } + + for (; j < i + 16; j++) + { + fputs(" ", log_stream); + if (j == i + 7) + fputs(": ", log_stream); + } + + fputs(" ", log_stream); + for (j = i; j < maxsize && j < (i + 16); j++) + { + if (d[j] >= 0x20 && d[j] < 0x7f && d[j] != 0x20) + fputc(d[j], log_stream); + else + fputc('.', log_stream); + + if (j == i + 7) + fputs(" ", log_stream); + } + + i = j; + fputs("\n", log_stream); + } + fflush(log_stream); + setbuf(log_stream, NULL); +} + + +// Add a route +void routeset(ipt ip, ipt mask, ipt gw, u8 add) +{ + struct rtentry r; + memset(&r, 0, sizeof(r)); + r.rt_dev = tapdevice; + r.rt_dst.sa_family = AF_INET; + *(u32 *) & (((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr) = htonl(ip); + r.rt_gateway.sa_family = AF_INET; + *(u32 *) & (((struct sockaddr_in *) &r.rt_gateway)->sin_addr.s_addr) = htonl(gw); + r.rt_genmask.sa_family = AF_INET; + *(u32 *) & (((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr) = htonl(mask ? : 0xFFFFFFF); + r.rt_flags = (RTF_UP | RTF_STATIC); + if (gw) + r.rt_flags |= RTF_GATEWAY; + else + r.rt_flags |= RTF_HOST; + if (ioctl(ifrfd, add ? SIOCADDRT : SIOCDELRT, (void *) &r) < 0) perror("routeset"); + log(1, ip, 0, 0, "Route %s %u.%u.%u.%u/%u.%u.%u.%u %u.%u.%u.%u\n", add ? "Add" : "Del", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255, mask >> 24, mask >> 16 & 255, mask >> 8 & 255, mask & 255, gw >> 24, gw >> 16 & 255, gw >> 8 & 255, gw & 255); +} + +// Set up TAP interface +void inittap(void) +{ + struct ifreq ifr; + struct sockaddr_in sin = {0}; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN; + + tapfd = open(TAPDEVICE, O_RDWR); + if (tapfd < 0) + { // fatal + log(0, 0, 0, 0, "Can't open %s: %s\n", TAPDEVICE, strerror(errno)); + exit(-1); + } + if (ioctl(tapfd, TUNSETIFF, (void *) &ifr) < 0) + { + log(0, 0, 0, 0, "Can't set tap interface: %s\n", strerror(errno)); + exit(-1); + } + assert(strlen(ifr.ifr_name) < sizeof(tapdevice)); + strcpy(tapdevice, ifr.ifr_name); + ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = handle_interface ? bind_address : 0x01010101; // 1.1.1.1 + memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); + + if (ioctl(ifrfd, SIOCSIFADDR, (void *) &ifr) < 0) + { + perror("set tap addr"); + exit( -1); + } + ifr.ifr_flags = IFF_UP; + if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0) + { + perror("set tap flags"); + exit( -1); + } + if (ioctl(ifrfd, SIOCGIFHWADDR, (void *) &ifr) < 0) + { + perror("get tap hwaddr"); + exit( -1); + } + memcpy(&tapmac, 2 + (u8 *) & ifr.ifr_hwaddr, 6); + if (ioctl(ifrfd, SIOCGIFINDEX, (void *) &ifr) < 0) + { + perror("get tap ifindex"); + exit( -1); + } + tapidx = ifr.ifr_ifindex; +} + +// set up UDP port +void initudp(void) +{ + int on = 1; + struct sockaddr_in addr; + + // Tunnel + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(L2TPPORT); + addr.sin_addr.s_addr = bind_address; + udpfd = socket(AF_INET, SOCK_DGRAM, UDP); + setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + exit( -1); + } + snoopfd = socket(AF_INET, SOCK_DGRAM, UDP); + + // Control + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(1702); + controlfd = socket(AF_INET, SOCK_DGRAM, 17); + setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + exit(-1); + } +} + +// Find session by IP, 0 for not found +sessionidt sessionbyip(ipt ip) +{ + unsigned char *a = (char *)&ip; + char **d = (char **) ip_hash; + +#ifdef STAT_CALLS + STAT(call_sessionbyip); +#endif + + if (!(d = (char **) d[(size_t) *a++])) return 0; + if (!(d = (char **) d[(size_t) *a++])) return 0; + if (!(d = (char **) d[(size_t) *a++])) return 0; + + return (ipt) d[(size_t) *a]; +} + +void cache_sessionid(ipt ip, sessionidt s) +{ + unsigned char *a = (char *) &ip; + char **d = (char **) ip_hash; + int i; + + for (i = 0; i < 3; i++) + { + if (!d[(size_t) a[i]]) + { + if (!(d[(size_t) a[i]] = calloc(256, sizeof (void *)))) + return; + } + + d = (char **) d[(size_t) a[i]]; + } + + log(4, ip, s, session[s].tunnel, "Caching session ID %d for ip address\n", s); + d[(size_t) a[3]] = (char *)((int)s); +} + +void uncache_sessionid(ipt ip) +{ + unsigned char *a = (char *) &ip; + char **d = (char **) ip_hash; + int i; + + for (i = 0; i < 3; i++) + { + if (!d[(size_t) a[i]]) return; + d = (char **) d[(size_t) a[i]]; + } + d[(size_t) a[3]] = NULL; +} + +// Find session by username, 0 for not found +// walled garden'd users aren't authenticated, so the username is +// reasonably useless. Ignore them to avoid incorrect actions +sessionidt sessionbyuser(char *username) +{ + int s; +#ifdef STAT_CALLS + STAT(call_sessionbyuser); +#endif + for (s = 1; s < MAXSESSION && (session[s].walled_garden || strncmp(session[s].user, username, 128)); s++); + if (s < MAXSESSION) + return s; + return 0; +} + +void send_garp(ipt ip) +{ + int s; + struct ifreq ifr; + unsigned char mac[6]; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + perror("socket"); + exit(-1); + } + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, "eth0"); + if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) + { + perror("get eth0 hwaddr"); + exit(-1); + } + memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6*sizeof(char)); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) + { + perror("get eth0 ifindex"); + exit(-1); + } + close(s); + sendarp(ifr.ifr_ifindex, mac, ip); +} + +// Find session by username, 0 for not found +sessiont *sessiontbysessionidt(sessionidt s) +{ + if (!s || s > MAXSESSION) return NULL; + return &session[s]; +} + +sessionidt sessionidtbysessiont(sessiont *s) +{ + sessionidt val = s-session; + if (s < session || val > MAXSESSION) return 0; + return val; +} + +// send gratuitous ARP to set ARP table for newly allocated IP +void sessionsendarp(sessionidt s) +{ + unsigned char mac[6]; +#ifdef STAT_CALLS + STAT(call_sendarp); +#endif + *(u16 *) (mac + 0) = htons(tapmac[0]); // set source address + *(u16 *) (mac + 2) = htons(tapmac[1]); + *(u16 *) (mac + 4) = htons(tapmac[2]); + sendarp(tapidx, mac, session[s].ip); + STAT(arp_sent); +} + +// Handle ARP requests +void processarp(u8 * buf, int len) +{ + ipt ip; + sessionidt s; + +#ifdef STAT_CALLS + STAT(call_processarp); +#endif + STAT(arp_recv); + if (len != 46) + { + log(0, 0, 0, 0, "Unexpected length ARP %d bytes\n", len); + STAT(arp_errors); + return; + } + if (*(u16 *) (buf + 16) != htons(PKTARP)) + { + log(0, 0, 0, 0, "Unexpected ARP type %04X\n", ntohs(*(u16 *) (buf + 16))); + STAT(arp_errors); + return; + } + if (*(u16 *) (buf + 18) != htons(0x0001)) + { + log(0, 0, 0, 0, "Unexpected ARP hard type %04X\n", ntohs(*(u16 *) (buf + 18))); + STAT(arp_errors); + return; + } + if (*(u16 *) (buf + 20) != htons(PKTIP)) + { + log(0, 0, 0, 0, "Unexpected ARP prot type %04X\n", ntohs(*(u16 *) (buf + 20))); + STAT(arp_errors); + return; + } + if (buf[22] != 6) + { + log(0, 0, 0, 0, "Unexpected ARP hard len %d\n", buf[22]); + STAT(arp_errors); + return; + } + if (buf[23] != 4) + { + log(0, 0, 0, 0, "Unexpected ARP prot len %d\n", buf[23]); + STAT(arp_errors); + return; + } + if (*(u16 *) (buf + 24) != htons(0x0001)) + { + log(0, 0, 0, 0, "Unexpected ARP op %04X\n", ntohs(*(u16 *) (buf + 24))); + STAT(arp_errors); + return; + } + ip = ntohl(*(u32 *) (buf + 42)); + // look up session + s = sessionbyip(htonl(ip)); + if (s) + { + log(3, ip, s, session[s].tunnel, "ARP reply for %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255, session[s].tunnel, s); + memcpy(buf + 4, buf + 10, 6); // set destination as source + *(u16 *) (buf + 10) = htons(tapmac[0]); // set soucre address + *(u16 *) (buf + 12) = htons(tapmac[1]); + *(u16 *) (buf + 14) = htons(tapmac[2]); + *(u16 *) (buf + 24) = htons(0x0002); // ARP reply + memcpy(buf + 26, buf + 10, 6); // sender ethernet + memcpy(buf + 36, buf + 4, 6); // target ethernet + *(u32 *) (buf + 42) = *(u32 *) (buf + 32); // target IP + *(u32 *) (buf + 32) = htonl(ip); // sender IP + write(tapfd, buf, len); + STAT(arp_replies); + } + else + { + log(3, ip, 0, 0, "ARP request for unknown IP %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255); + STAT(arp_discarded); + } +} + +// actually send a control message for a specific tunnel +void tunnelsend(u8 * buf, u16 l, tunnelidt t) +{ + struct sockaddr_in addr; + +#ifdef STAT_CALLS + STAT(call_tunnelsend); +#endif + if (!tunnel[t].ip) + { + log(1, 0, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n"); + STAT(tunnel_tx_errors); + return; + } + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + *(u32 *) & addr.sin_addr = htonl(tunnel[t].ip); + addr.sin_port = htons(tunnel[t].port); + + // sequence expected, if sequence in message + if (*buf & 0x08) *(u16 *) (buf + ((*buf & 0x40) ? 10 : 8)) = htons(tunnel[t].nr); + + // If this is a control message, deal with retries + if (*buf & 0x80) + { + tunnel[t].last = time_now; // control message sent + tunnel[t].retry = backoff(tunnel[t].try); // when to resend + if (tunnel[t].try > 1) + { + STAT(tunnel_retries); + log(3, tunnel[t].ip, 0, t, "Control message resend try %d\n", tunnel[t].try); + } + } + + if (sendto(udpfd, buf, l, 0, (void *) &addr, sizeof(addr)) < 0) + { + log(0, tunnel[t].ip, ntohs((*(u16 *) (buf + 6))), t, "Error sending data out tunnel: %s (udpfd=%d, buf=%p, len=%d, dest=%s)\n", + strerror(errno), udpfd, buf, l, inet_ntoa(addr.sin_addr)); + STAT(tunnel_tx_errors); + return; + } + + log_hex(5, "Send Tunnel Data", buf, l); + STAT(tunnel_tx_packets); + INC_STAT(tunnel_tx_bytes, l); +} + +// process outgoing (to tunnel) IP +void processipout(u8 * buf, int len) +{ + sessionidt s; + sessiont *sp; + tunnelidt t; + ipt ip; + u8 b[MAXETHER]; +#ifdef STAT_CALLS + STAT(call_processipout); +#endif + if (len < 38) + { + log(1, 0, 0, 0, "Short IP, %d bytes\n", len); + STAT(tunnel_tx_errors); + return; + } + + // Skip the tun header + buf += 4; + len -= 4; + + // Got an IP header now + if (*(u8 *)(buf) >> 4 != 4) + { + log(1, 0, 0, 0, "IP: Don't understand anything except IPv4\n"); + return; + } + + ip = *(u32 *)(buf + 16); + if (!(s = sessionbyip(ip))) + { +// log(4, 0, 0, 0, "IP: Can't find session for IP %s\n", inet_toa(ip)); + return; + } + t = session[s].tunnel; + sp = &session[s]; + + // Snooping this session, send it to ASIO + if (sp->snoop) snoop_send_packet(buf, len); + + log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); + + // Plugin hook + { + struct param_packet_rx packet = { &tunnel[t], &session[s], buf, len }; + run_plugins(PLUGIN_PACKET_RX, &packet); + } + + // Add on L2TP header + { + u8 *p = makeppp(b, buf, len, t, s, PPPIP); + tunnelsend(b, len + (p-b), t); // send it... + sp->cout += len; // byte count + sp->pout++; + udp_tx += len; + } +} + +// add an AVP (16 bit) +void control16(controlt * c, u16 avp, u16 val, u8 m) +{ + u16 l = (m ? 0x8008 : 0x0008); + *(u16 *) (c->buf + c->length + 0) = htons(l); + *(u16 *) (c->buf + c->length + 2) = htons(0); + *(u16 *) (c->buf + c->length + 4) = htons(avp); + *(u16 *) (c->buf + c->length + 6) = htons(val); + c->length += 8; +} + +// add an AVP (32 bit) +void control32(controlt * c, u16 avp, u32 val, u8 m) +{ + u16 l = (m ? 0x800A : 0x000A); + *(u16 *) (c->buf + c->length + 0) = htons(l); + *(u16 *) (c->buf + c->length + 2) = htons(0); + *(u16 *) (c->buf + c->length + 4) = htons(avp); + *(u32 *) (c->buf + c->length + 6) = htonl(val); + c->length += 10; +} + +// add an AVP (32 bit) +void controls(controlt * c, u16 avp, char *val, u8 m) +{ + u16 l = ((m ? 0x8000 : 0) + strlen(val) + 6); + *(u16 *) (c->buf + c->length + 0) = htons(l); + *(u16 *) (c->buf + c->length + 2) = htons(0); + *(u16 *) (c->buf + c->length + 4) = htons(avp); + memcpy(c->buf + c->length + 6, val, strlen(val)); + c->length += 6 + strlen(val); +} + +// add a binary AVP +void controlb(controlt * c, u16 avp, char *val, unsigned int len, u8 m) +{ + u16 l = ((m ? 0x8000 : 0) + len + 6); + *(u16 *) (c->buf + c->length + 0) = htons(l); + *(u16 *) (c->buf + c->length + 2) = htons(0); + *(u16 *) (c->buf + c->length + 4) = htons(avp); + memcpy(c->buf + c->length + 6, val, len); + c->length += 6 + len; +} + +// new control connection +controlt *controlnew(u16 mtype) +{ + controlt *c; + if (!controlfree) + c = malloc(sizeof(controlt)); + else + { + c = controlfree; + controlfree = c->next; + } + assert(c); + c->next = 0; + *(u16 *) (c->buf + 0) = htons(0xC802); // flags/ver + c->length = 12; + control16(c, 0, mtype, 1); + return c; +} + +// send zero block if nothing is waiting +void controlnull(tunnelidt t) +{ + u8 buf[12]; + if (tunnel[t].controlc) + return; + *(u16 *) (buf + 0) = htons(0xC802); // flags/ver + *(u16 *) (buf + 2) = htons(12); // length + *(u16 *) (buf + 4) = htons(tunnel[t].far); // tunnel + *(u16 *) (buf + 6) = htons(0); // session + *(u16 *) (buf + 8) = htons(tunnel[t].ns); // sequence + *(u16 *) (buf + 10) = htons(tunnel[t].nr); // sequence + tunnelsend(buf, 12, t); +} + +// add a control message to a tunnel, and send if within window +void controladd(controlt * c, tunnelidt t, sessionidt s) +{ + *(u16 *) (c->buf + 2) = htons(c->length); // length + *(u16 *) (c->buf + 4) = htons(tunnel[t].far); // tunnel + *(u16 *) (c->buf + 6) = htons(s ? session[s].far : 0); // session + *(u16 *) (c->buf + 8) = htons(tunnel[t].ns); // sequence + tunnel[t].ns++; // advance sequence + // link in message in to queue + if (tunnel[t].controlc) + tunnel[t].controle->next = c; + else + tunnel[t].controls = c; + tunnel[t].controle = c; + tunnel[t].controlc++; + // send now if space in window + if (tunnel[t].controlc <= tunnel[t].window) + { + tunnel[t].try = 0; // first send + tunnelsend(c->buf, c->length, t); + } +} + +// start tidy shutdown of session +void sessionshutdown(sessionidt s, char *reason) +{ + int dead = session[s].die; + int walled_garden = session[s].walled_garden; + +#ifdef STAT_CALLS + STAT(call_sessionshutdown); +#endif + if (!session[s].tunnel) + return; // not a live session + + if (!session[s].die) + log(2, 0, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason); + + session[s].die = now() + 150; // Clean up in 15 seconds + + { + struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] }; + run_plugins(PLUGIN_KILL_SESSION, &data); + } + + // RADIUS Stop message + if (session[s].opened && !walled_garden && !dead) { + u8 r = session[s].radius; + if (!r) + { + if (!radiusfree) + { + log(1, 0, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n"); + STAT(radius_overflow); + } else { + int n; + r = radiusnew(s); + for (n = 0; n < 15; n++) + radius[r].auth[n] = rand(); + } + } + if (r && radius[r].state != RADIUSSTOP) + radiussend(r, RADIUSSTOP); // stop, if not already trying + } + + if (session[s].ip) + { // IP allocated, clear and unroute + u8 r; + if (session[s].route[0].ip) + { + routeset(session[s].ip, 0, 0, 0); + for (r = 0; r < MAXROUTE; r++) + { + if (session[s].route[r].ip) + { + routeset(session[s].route[r].ip, session[s].route[r].mask, session[s].ip, 0); + session[s].route[r].ip = 0; + } + } + } + if (session[s].throttle) throttle_session(s, 0); session[s].throttle = 0; + free_ip_address(session[s].ip); + session[s].ip = 0; + } + { // Send CDN + controlt *c = controlnew(14); // sending CDN + control16(c, 1, 3, 1); // result code (admin reasons - TBA make error, general error, add message + control16(c, 14, s, 1); // assigned session (our end) + controladd(c, session[s].tunnel, s); // send the message + } + cluster_send_session(s); +} + +void sendipcp(tunnelidt t, sessionidt s) +{ + u8 buf[MAXCONTROL]; + u8 r = session[s].radius; + u8 *q; +#ifdef STAT_CALLS + STAT(call_sendipcp); +#endif + if (!r) + r = radiusnew(s); + if (radius[r].state != RADIUSIPCP) + { + radius[r].state = RADIUSIPCP; + radius[r].try = 0; + } + radius[r].retry = backoff(radius[r].try++); + if (radius[r].try > 10) + { + sessionshutdown(s, "No reply on IPCP"); + return; + } + q = makeppp(buf, 0, 0, t, s, PPPIPCP); + *q = ConfigReq; + q[1] = r; // ID, dont care, we only send one type of request + *(u16 *) (q + 2) = htons(10); + q[4] = 3; + q[5] = 6; + *(u32 *) (q + 6) = htonl(myip ? : session[s].ip); // send my IP (use theirs if I dont have one) + tunnelsend(buf, 10 + (q - buf), t); // send it +} + +// kill a session now +void sessionkill(sessionidt s, char *reason) +{ +#ifdef STAT_CALLS + STAT(call_sessionkill); +#endif + sessionshutdown(s, reason); // close radius/routes, etc. + if (session[s].radius) + radius[session[s].radius].session = 0; // cant send clean accounting data, session is killed + memset(&session[s], 0, sizeof(session[s])); + session[s].next = sessionfree; + sessionfree = s; + log(2, 0, s, session[s].tunnel, "Kill session %d: %s\n", s, reason); + cluster_send_session(s); +} + +// kill a tunnel now +void tunnelkill(tunnelidt t, char *reason) +{ + sessionidt s; + controlt *c; +#ifdef STAT_CALLS + STAT(call_tunnelkill); +#endif + // free control messages + while ((c = tunnel[t].controls)) + { + controlt * n = c->next; + tunnel[t].controls = n; + tunnel[t].controlc--; + c->next = controlfree; + controlfree = c; + } + // kill sessions + for (s = 0; s < MAXSESSION; s++) + if (session[s].tunnel == t) + sessionkill(s, reason); + // free tunnel + memset(&tunnel[t], 0, sizeof(tunnel[t])); + tunnel[t].next = tunnelfree; + cluster_send_tunnel(t); + log(1, 0, 0, t, "Kill tunnel %d: %s\n", t, reason); + tunnelfree = t; +} + +// shut down a tunnel +void tunnelshutdown(tunnelidt t, char *reason) +{ + sessionidt s; +#ifdef STAT_CALLS + STAT(call_tunnelshutdown); +#endif + if (!tunnel[t].last || !tunnel[t].far) + { // never set up, can immediately kill + tunnelkill(t, reason); + return; + } + log(1, 0, 0, t, "Shutting down tunnel %d (%s)\n", t, reason); + // close session + for (s = 0; s < MAXSESSION; s++) + if (session[s].tunnel == t) + sessionkill(s, reason); + tunnel[t].die = now() + 700; // Clean up in 70 seconds + cluster_send_tunnel(t); + // TBA - should we wait for sessions to stop? + { // Send StopCCN + controlt *c = controlnew(4); // sending StopCCN + control16(c, 1, 1, 1); // result code (admin reasons - TBA make error, general error, add message + control16(c, 9, t, 1); // assigned tunnel (our end) + controladd(c, t, 0); // send the message + } +} + +// read and process packet on tunnel (UDP) +void processudp(u8 * buf, int len, struct sockaddr_in *addr) +{ + char *chapresponse = NULL; + u16 l = len, t = 0, s = 0, ns = 0, nr = 0; + u8 *p = buf + 2; + +#ifdef STAT_CALLS + STAT(call_processudp); +#endif + udp_rx += len; + udp_rx_pkt++; + log_hex(5, "UDP Data", buf, len); + STAT(tunnel_rx_packets); + INC_STAT(tunnel_rx_bytes, len); + if (len < 6) + { + log(1, ntohl(addr->sin_addr.s_addr), 0, 0, "Short UDP, %d bytes\n", len); + STAT(tunnel_rx_errors); + return; + } + if ((buf[1] & 0x0F) != 2) + { + log(1, ntohl(addr->sin_addr.s_addr), 0, 0, "Bad L2TP ver %d\n", (buf[1] & 0x0F) != 2); + STAT(tunnel_rx_errors); + return; + } + if (*buf & 0x40) + { // length + l = ntohs(*(u16 *) p); + p += 2; + } + t = ntohs(*(u16 *) p); + p += 2; + s = ntohs(*(u16 *) p); + p += 2; + if (s >= MAXSESSION) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Received UDP packet with invalid session ID\n"); + STAT(tunnel_rx_errors); + return; + } + if (t >= MAXTUNNEL) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Received UDP packet with invalid tunnel ID\n"); + STAT(tunnel_rx_errors); + return; + } + if (s && !session[s].tunnel) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "UDP packet contains session %d but no session[%d].tunnel exists (LAC said tunnel = %d). Dropping packet.\n", s, s, t); + STAT(tunnel_rx_errors); + return; + } + if (*buf & 0x08) + { // ns/nr + ns = ntohs(*(u16 *) p); + p += 2; + nr = ntohs(*(u16 *) p); + p += 2; + } + if (*buf & 0x02) + { // offset + u16 o = ntohs(*(u16 *) p); + p += o + 2; + } + if ((p - buf) > l) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Bad length %d>%d\n", (p - buf), l); + STAT(tunnel_rx_errors); + return; + } + l -= (p - buf); + if (t) tunnel[t].last = time_now; + if (*buf & 0x80) + { // control + u16 message = 0xFFFF; // message type + u8 fatal = 0; + u8 mandatorymessage = 0; + u8 chap = 0; // if CHAP being used + u16 asession = 0; // assigned session + u32 amagic = 0; // magic number + u8 aflags = 0; // flags from last LCF + u16 version = 0x0100; // protocol version (we handle 0.0 as well and send that back just in case) + int requestchap = 0; // do we request PAP instead of original CHAP request? + char called[MAXTEL] = ""; // called number + char calling[MAXTEL] = ""; // calling number + if ((*buf & 0xCA) != 0xC8) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Bad control header %02X\n", *buf); + STAT(tunnel_rx_errors); + return; + } + log(3, ntohl(addr->sin_addr.s_addr), s, t, "Control message (%d bytes): %d ns %d nr %d ns %d nr %d\n", + l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr); + // if no tunnel specified, assign one + if (!t) + { + /* + ipt ip = ntohl(*(ipt *) & addr->sin_addr); + portt port = ntohs(addr->sin_port); + + // find existing tunnel that was not fully set up + for (t = 0; t < MAXTUNNEL; t++) + { + if ((tunnel[t].ip == ip && tunnel[t].port == port) && + (!tunnel[t].die || !tunnel[t].hostname[0])) + { + char buf[600] = {0}; + snprintf(buf, 600, "Duplicate tunnel with %d. ip=%u port=%d die=%d hostname=%s", + t, tunnel[t].ip, tunnel[t].port, tunnel[t].die, tunnel[t].hostname); + tunnelshutdown(t, buf); + break; + } + } + */ + + t = tunnelfree; + if (!t) + { + log(1, ntohl(addr->sin_addr.s_addr), 0, 0, "No more tunnels\n"); + STAT(tunnel_overflow); + return; + } + tunnelfree = tunnel[t].next; + memset(&tunnel[t], 0, sizeof(tunnelt)); + tunnel[t].ip = ntohl(*(ipt *) & addr->sin_addr); + tunnel[t].port = ntohs(addr->sin_port); + tunnel[t].window = 4; // default window + log(1, ntohl(addr->sin_addr.s_addr), 0, t, " New tunnel from %u.%u.%u.%u/%u ID %d\n", tunnel[t].ip >> 24, tunnel[t].ip >> 16 & 255, tunnel[t].ip >> 8 & 255, tunnel[t].ip & 255, tunnel[t].port, t); + STAT(tunnel_created); + } + { // check sequence of this message + int skip = tunnel[t].window; // track how many in-window packets are still in queue + if (tunnel[t].controlc) + { // some to clear maybe + while (tunnel[t].controlc && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000)) + { + controlt *c = tunnel[t].controls; + tunnel[t].controls = c->next; + tunnel[t].controlc--; + c->next = controlfree; + controlfree = c; + skip--; + tunnel[t].try = 0; // we have progress + } + } + if (tunnel[t].nr < ns && tunnel[t].nr != 0) + { + // is this the sequence we were expecting? + log(1, ntohl(addr->sin_addr.s_addr), 0, t, " Out of sequence tunnel %d, (%d not %d)\n", t, ns, tunnel[t].nr); + STAT(tunnel_rx_errors); +// controlnull(t); + return; + } + if (l) + tunnel[t].nr++; // receiver advance (do here so quoted correctly in any sends below) + if (skip < 0) + skip = 0; + if (skip < tunnel[t].controlc) + { // some control packets can now be sent that were previous stuck out of window + int tosend = tunnel[t].window - skip; + controlt *c = tunnel[t].controls; + while (c && skip) + { + c = c->next; + skip--; + } + while (c && tosend) + { + tunnel[t].try = 0; // first send + tunnelsend(c->buf, c->length, t); + c = c->next; + tosend--; + } + } + if (!tunnel[t].controlc) + tunnel[t].retry = 0; // caught up + } + if (l) + { // if not a null message + // process AVPs + while (l && !(fatal & 0x80)) + { + u16 n = (ntohs(*(u16 *) p) & 0x3FF); + u8 *b = p; + u8 flags = *p; + u16 mtype; + p += n; // next + if (l < n) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Invalid length in AVP\n"); + STAT(tunnel_rx_errors); + fatal = flags; + return; + } + l -= n; + if (flags & 0x40) + { + // handle hidden AVPs + if (!l2tpsecret) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP requested, but no L2TP secret.\n"); + fatal = flags; + continue; + } + if (!session[s].random_vector_length) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP requested, but no random vector.\n"); + fatal = flags; + continue; + } + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP\n"); + } + if (*b & 0x3C) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Unrecognised AVP flags %02X\n", *b); + fatal = flags; + continue; // next + } + b += 2; + if (*(u16 *) (b)) + { + log(2, ntohl(addr->sin_addr.s_addr), s, t, "Unknown AVP vendor %d\n", ntohs(*(u16 *) (b))); + fatal = flags; + continue; // next + } + b += 2; + mtype = ntohs(*(u16 *) (b)); + b += 2; + n -= 6; + + log(4, ntohl(addr->sin_addr.s_addr), s, t, " AVP %d (%s) len %d\n", mtype, avpnames[mtype], n); + switch (mtype) + { + case 0: // message type + message = ntohs(*(u16 *) b); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Message type = %d (%s)\n", *b, + l2tp_message_types[message]); + mandatorymessage = flags; + break; + case 1: // result code + { + u16 rescode = ntohs(*(u16 *)(b)); + const char* resdesc = "(unknown)"; + if (message == 4) { /* StopCCN */ + if (rescode <= MAX_STOPCCN_RESULT_CODE) + resdesc = stopccn_result_codes[rescode]; + } else if (message == 14) { /* CDN */ + if (rescode <= MAX_CDN_RESULT_CODE) + resdesc = cdn_result_codes[rescode]; + } + + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Result Code %d: %s\n", + rescode, resdesc); + if (n >= 4) { + u16 errcode = ntohs(*(u16 *)(b + 2)); + const char* errdesc = "(unknown)"; + if (errcode <= MAX_ERROR_CODE) + errdesc = error_codes[errcode]; + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Error Code %d: %s\n", + errcode, errdesc); + } + if (n > 4) { + /* %*s doesn't work?? */ + char buf[n-4+2]; + memcpy(buf, b+4, n-4); + buf[n-4+1] = '\0'; + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Error String: %s\n", + buf); + } + break; + } + break; + case 2: // protocol version + { + version = ntohs(*(u16 *) (b)); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Protocol version = %d\n", version); + if (version && version != 0x0100) + { // allow 0.0 and 1.0 + log(1, ntohl(addr->sin_addr.s_addr), s, t, " Bad protocol version %04X\n", + version); + fatal = flags; + continue; // next + } + } + break; + case 3: // framing capabilities +// log(4, ntohl(addr->sin_addr.s_addr), s, t, "Framing capabilities\n"); + break; + case 4: // bearer capabilities +// log(4, ntohl(addr->sin_addr.s_addr), s, t, "Bearer capabilities\n"); + break; + case 5: // tie breaker + // We never open tunnels, so we don't + // care about tie breakers +// log(4, ntohl(addr->sin_addr.s_addr), s, t, "Tie breaker\n"); + continue; + case 6: // firmware revision +// log(4, ntohl(addr->sin_addr.s_addr), s, t, "Firmware revision\n"); + break; + case 7: // host name + memset(tunnel[t].hostname, 0, 128); + memcpy(tunnel[t].hostname, b, (n >= 127) ? 127 : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Tunnel hostname = \"%s\"\n", tunnel[t].hostname); + // TBA - to send to RADIUS + break; + case 8: // vendor name + memset(tunnel[t].vendor, 0, 128); + memcpy(tunnel[t].vendor, b, (n >= 127) ? 127 : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Vendor name = \"%s\"\n", tunnel[t].vendor); + break; + case 9: // assigned tunnel + tunnel[t].far = ntohs(*(u16 *) (b)); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Remote tunnel id = %d\n", tunnel[t].far); + break; + case 10: // rx window + tunnel[t].window = ntohs(*(u16 *) (b)); + if (!tunnel[t].window) + tunnel[t].window = 1; // window of 0 is silly + log(4, ntohl(addr->sin_addr.s_addr), s, t, " rx window = %d\n", tunnel[t].window); + break; + case 11: // Challenge + { + log(4, ntohl(addr->sin_addr.s_addr), s, t, " LAC requested CHAP authentication for tunnel\n"); + build_chap_response(b, 2, n, &chapresponse); + } + break; + case 14: // assigned session + asession = session[s].far = ntohs(*(u16 *) (b)); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " assigned session = %d\n", asession); + break; + case 15: // call serial number + log(4, ntohl(addr->sin_addr.s_addr), s, t, " call serial number = %d\n", ntohl(*(u32 *)b)); + break; + case 18: // bearer type + log(4, ntohl(addr->sin_addr.s_addr), s, t, " bearer type = %d\n", ntohl(*(u32 *)b)); + // TBA - for RADIUS + break; + case 19: // framing type + log(4, ntohl(addr->sin_addr.s_addr), s, t, " framing type = %d\n", ntohl(*(u32 *)b)); + // TBA + break; + case 21: // called number + memset(called, 0, MAXTEL); + memcpy(called, b, (n >= MAXTEL) ? (MAXTEL-1) : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Called <%s>\n", called); + break; + case 22: // calling number + memset(calling, 0, MAXTEL); + memcpy(calling, b, (n >= MAXTEL) ? (MAXTEL-1) : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Calling <%s>\n", calling); + break; + case 24: // tx connect speed + if (n == 4) + { + session[s].tx_connect_speed = ntohl(*(u32 *)b); + } + else + { + // AS5300s send connect speed as a string + char tmp[30] = {0}; + memcpy(tmp, b, (n >= 30) ? 30 : n); + session[s].tx_connect_speed = atol(tmp); + } + log(4, ntohl(addr->sin_addr.s_addr), s, t, " TX connect speed <%d>\n", + session[s].tx_connect_speed); + break; + case 38: // rx connect speed + if (n == 4) + { + session[s].rx_connect_speed = ntohl(*(u32 *)b); + } + else + { + // AS5300s send connect speed as a string + char tmp[30] = {0}; + memcpy(tmp, b, (n >= 30) ? 30 : n); + session[s].rx_connect_speed = atol(tmp); + } + log(4, ntohl(addr->sin_addr.s_addr), s, t, " RX connect speed <%d>\n", + session[s].rx_connect_speed); + break; + case 25: // Physical Channel ID + { + u32 tmp = ntohl(*(u32 *)b); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Physical Channel ID <%X>\n", tmp); + break; + } + case 29: // Proxy Authentication Type + { + u16 authtype = ntohs(*(u16 *)b); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Type %d (%s)\n", + authtype, authtypes[authtype]); + requestchap = (authtype == 2); + break; + } + case 30: // Proxy Authentication Name + { + char authname[64] = {0}; + memcpy(authname, b, (n > 63) ? 63 : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Name (%s)\n", + authname); + break; + } + case 31: // Proxy Authentication Challenge + { + memcpy(radius[session[s].radius].auth, b, 16); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Challenge (%X)\n", radius[session[s].radius].auth); + break; + } + case 32: // Proxy Authentication ID + { + u16 authid = ntohs(*(u16 *)(b)); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth ID (%d)\n", + authid); + if (session[s].radius) + radius[session[s].radius].id = authid; + break; + } + case 33: // Proxy Authentication Response + { + char authresp[64] = {0}; + memcpy(authresp, b, (n > 63) ? 63 : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Response\n"); + break; + } + case 27: // last send lcp + { // find magic number + u8 *p = b, *e = p + n; + while (p < e && p[1]) + { + if (*p == 5 && p[1] == 6) + amagic = ntohl(*(u32 *) (p + 2)); + else if (*p == 3 && p[1] == 5 && *(u16 *) (p + 2) == htons(PPPCHAP) && p[4] == 5) + chap = 1; + else if (*p == 7) + aflags |= SESSIONPFC; + else if (*p == 8) + aflags |= SESSIONACFC; + p += p[1]; + } + + { + char tmp[500] = {0}; + tmp[0] = ConfigReq; + memcpy((tmp + 1), b, n); + } + } + break; + case 28: // last recv lcp confreq + { + char tmp[500] = {0}; + tmp[0] = ConfigReq; + memcpy((tmp + 1), b, n); + break; + } + case 26: // Initial Received LCP CONFREQ + { + char tmp[500] = {0}; + tmp[0] = ConfigReq; + memcpy((tmp + 1), b, n); + } + break; + case 39: // seq required - we control it as an LNS anyway... + break; + case 36: // Random Vector + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Random Vector received. Enabled AVP Hiding.\n"); + memset(session[s].random_vector, 0, sizeof(session[s].random_vector)); + memcpy(session[s].random_vector, b, n); + session[s].random_vector_length = n; + break; + default: + log(2, ntohl(addr->sin_addr.s_addr), s, t, " Unknown AVP type %d\n", mtype); + fatal = flags; + continue; // next + } + } + // process message + if (fatal & 0x80) + tunnelshutdown(t, "Unknown Mandatory AVP"); + else + switch (message) + { + case 1: // SCCRQ - Start Control Connection Request + { + controlt *c = controlnew(2); // sending SCCRP + control16(c, 2, version, 1); // protocol version + control32(c, 3, 3, 1); // framing + controls(c, 7, tunnel[t].hostname, 1); // host name (TBA) + if (chapresponse) controlb(c, 13, chapresponse, 16, 1); // Challenge response + control16(c, 9, t, 1); // assigned tunnel + controladd(c, t, s); // send the resply + } + break; + case 2: // SCCRP + // TBA + break; + case 3: // SCCN + controlnull(t); // ack + break; + case 4: // StopCCN + controlnull(t); // ack + tunnelshutdown(t, "Stopped"); // Shut down cleanly + tunnelkill(t, "Stopped"); // Immediately force everything dead + break; + case 6: // HELLO + controlnull(t); // simply ACK + break; + case 7: // OCRQ + // TBA + break; + case 8: // OCRO + // TBA + break; + case 9: // OCCN + // TBA + break; + case 10: // ICRQ + if (!sessionfree) + { + STAT(session_overflow); + tunnelshutdown(t, "No free sessions"); + } + else + { + u8 r; + controlt *c; + + // make a RADIUS session + if (!radiusfree) + { + STAT(radius_overflow); + log(1, ntohl(addr->sin_addr.s_addr), s, t, "No free RADIUS sessions for ICRQ\n"); + return; + } + + c = controlnew(11); // sending ICRP + s = sessionfree; + sessionfree = session[s].next; + memset(&session[s], 0, sizeof(session[s])); + session[s].id = sessionid++; + session[s].opened = time(NULL); + session[s].tunnel = t; + session[s].far = asession; + log(3, ntohl(addr->sin_addr.s_addr), s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far); + control16(c, 14, s, 1); // assigned session + controladd(c, t, s); // send the reply + r = radiusfree; + radiusfree = radius[r].next; + memset(&radius[r], 0, sizeof(radius[r])); + session[s].radius = r; + radius[r].session = s; + { + // Generate a random challenge + int n; + for (n = 0; n < 15; n++) + radius[r].auth[n] = rand(); + } + strcpy(radius[r].calling, calling); + strcpy(session[s].called, called); + strcpy(session[s].calling, calling); + STAT(session_created); + } + break; + case 11: // ICRP + // TBA + break; + case 12: // ICCN + session[s].magic = amagic; // set magic number + session[s].flags = aflags; // set flags received + log(3, ntohl(addr->sin_addr.s_addr), s, t, "Magic %X Flags %X\n", amagic, aflags); + controlnull(t); // ack + // In CHAP state, request PAP instead + if (requestchap) + initlcp(t, s); + break; + case 14: // CDN + controlnull(t); // ack + sessionshutdown(s, "Closed (Received CDN)"); + break; + case 0xFFFF: + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Missing message type\n"); + break; + default: + STAT(tunnel_rx_errors); + if (mandatorymessage & 0x80) + tunnelshutdown(t, "Unknown message"); + else + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Unknown message type %d\n", message); + break; + } + if (chapresponse) free(chapresponse); + cluster_send_tunnel(t); + } + else + { + log(4, 0, s, t, " Got a ZLB ack\n"); + } + } + else + { // data + u16 prot; + + log_hex(5, "Receive Tunnel Data", p, l); + if (session[s].die) + { + log(3, ntohl(addr->sin_addr.s_addr), s, t, "Session %d is closing. Don't process PPP packets\n", s); + return; // closing session, PPP not processed + } + if (l > 2 && p[0] == 0xFF && p[1] == 0x03) + { // HDLC address header, discard + p += 2; + l -= 2; + } + if (l < 2) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Short ppp length %d\n", l); + STAT(tunnel_rx_errors); + return; + } + if (*p & 1) + { + prot = *p++; + l--; + } + else + { + prot = ntohs(*(u16 *) p); + p += 2; + l -= 2; + } + if (prot == PPPPAP) + { + session[s].last_packet = time_now; + processpap(t, s, p, l); + } + else if (prot == PPPCHAP) + { + session[s].last_packet = time_now; + processchap(t, s, p, l); + } + else if (prot == PPPLCP) + { + session[s].last_packet = time_now; + processlcp(t, s, p, l); + } + else if (prot == PPPIPCP) + { + session[s].last_packet = time_now; + processipcp(t, s, p, l); + } + else if (prot == PPPCCP) + { + session[s].last_packet = time_now; + processccp(t, s, p, l); + } + else if (prot == PPPIP) + { + session[s].last_packet = time_now; + processipin(t, s, p, l); + } + else + { + STAT(tunnel_rx_errors); + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Unknown PPP protocol %04X\n", prot); + } + } +} + +// read and process packet on tap +void processtap(u8 * buf, int len) +{ + log_hex(5, "Receive TAP Data", buf, len); + STAT(tap_rx_packets); + INC_STAT(tap_rx_bytes, len); +#ifdef STAT_CALLS + STAT(call_processtap); +#endif + eth_rx_pkt++; + eth_rx += len; + if (len < 22) + { + log(1, 0, 0, 0, "Short tap packet %d bytes\n", len); + STAT(tap_rx_errors); + return; + } + if (*(u16 *) (buf + 2) == htons(PKTARP)) // ARP + processarp(buf, len); + else if (*(u16 *) (buf + 2) == htons(PKTIP)) // ARP + processipout(buf, len); + else + { + log(1, 0, 0, 0, "Unexpected tap packet %04X, %d bytes\n", ntohs(*(u16 *) (buf + 2)), len); + } +} + +// main loop - gets packets on tap or udp and processes them +void mainloop(void) +{ + fd_set cr; + int cn; + u8 buf[65536]; + struct timeval to; + clockt slow = now(); // occasional functions like session/tunnel expiry, tunnel hello, etc + clockt next_acct = slow + ACCT_TIME; + clockt next_cluster_ping = slow + 50; + to.tv_sec = 1; + to.tv_usec = 0; + log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tapfd=%d, radfd=%d, cluster_sockfd=%d, controlfd=%d\n", + udpfd, tapfd, radfd, cluster_sockfd, controlfd); + + FD_ZERO(&cr); + FD_SET(udpfd, &cr); + FD_SET(tapfd, &cr); + FD_SET(radfd, &cr); + FD_SET(controlfd, &cr); +#ifdef HAVE_LIBCLI + FD_SET(clifd, &cr); +#endif + if (cluster_sockfd) FD_SET(cluster_sockfd, &cr); + cn = udpfd; + if (cn < radfd) cn = radfd; + if (cn < tapfd) cn = tapfd; + if (cn < controlfd) cn = controlfd; +#ifdef HAVE_LIBCLI + if (cn < clifd) cn = clifd; +#endif + if (cn < cluster_sockfd) cn = cluster_sockfd; + + while (!main_quit) + { + fd_set r; + int n = cn; + memcpy(&r, &cr, sizeof(fd_set)); + n = select(n + 1, &r, 0, 0, &to); + if (n < 0) + { + if (errno != EINTR) + { + perror("select"); + exit( -1); + } + } + else if (n) + { + struct sockaddr_in addr; + int alen = sizeof(addr); + if (FD_ISSET(udpfd, &r)) + processudp(buf, recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen), &addr); + else if (FD_ISSET(tapfd, &r)) + processtap(buf, read(tapfd, buf, sizeof(buf))); + else if (FD_ISSET(radfd, &r)) + processrad(buf, recv(radfd, buf, sizeof(buf), 0)); + else if (FD_ISSET(cluster_sockfd, &r)) + processcluster(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)); + else if (FD_ISSET(controlfd, &r)) + processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr); +#ifdef HAVE_LIBCLI + else if (FD_ISSET(clifd, &r)) + { + struct sockaddr_in addr; + int sockfd; + int len = sizeof(addr); + + if ((sockfd = accept(clifd, (struct sockaddr *)&addr, &len)) <= 0) + { + log(0, 0, 0, 0, "accept error: %s\n", strerror(errno)); + continue; + } + else + { + cli_do(sockfd); + close(sockfd); + } + } +#endif + else + { + log(1, 0, 0, 0, "Main select() loop returned %d, but no fds have data waiting\n", n); + continue; + } + } + else if (n == 0) { // handle timeouts + clockt when = now(); + clockt best = when + 100; // default timeout + sessionidt s; + tunnelidt t; + u8 r; + for (r = 0; r < MAXRADIUS; r++) + if (radius[r].state && radius[r].retry) + { + if (radius[r].retry <= when) + radiusretry(r); + if (radius[r].retry && radius[r].retry < best) + best = radius[r].retry; + } + for (t = 0; t < MAXTUNNEL; t++) + { + // check for expired tunnels + if (tunnel[t].die && tunnel[t].die <= when) + { + STAT(tunnel_timeout); + tunnelkill(t, "Expired"); + continue; + } + // check for message resend + if (tunnel[t].retry && tunnel[t].controlc) + { + // resend pending messages as timeout on reply + if (tunnel[t].retry <= when) + { + controlt *c = tunnel[t].controls; + u8 w = tunnel[t].window; + tunnel[t].try++; // another try + if (tunnel[t].try > 5) + tunnelkill(t, "Timeout on control message"); // game over + else + while (c && w--) + { + tunnelsend(c->buf, c->length, t); + c = c->next; + } + } + if (tunnel[t].retry && tunnel[t].retry < best) + best = tunnel[t].retry; + } + // Send hello + if (tunnel[t].ip && !tunnel[t].die && tunnel[t].last < when + 600 && !tunnel[t].controlc) + { + controlt *c = controlnew(6); // sending HELLO + controladd(c, t, 0); // send the message + log(3, tunnel[t].ip, 0, t, "Sending HELLO message\n"); + } + } + +#ifdef HAVE_LIBCLI + // Check for sessions that have been killed from the CLI + if (cli_session_kill[0]) + { + int i; + for (i = 0; i < MAXSESSION && cli_session_kill[i]; i++) + { + log(2, 0, cli_session_kill[i], 0, "Dropping session by CLI\n"); + sessionshutdown(cli_session_kill[i], "Requested by CLI"); + cli_session_kill[i] = 0; + } + } + // Check for tunnels that have been killed from the CLI + if (cli_tunnel_kill[0]) + { + int i; + for (i = 0; i < MAXTUNNEL && cli_tunnel_kill[i]; i++) + { + log(2, 0, cli_tunnel_kill[i], 0, "Dropping tunnel by CLI\n"); + tunnelshutdown(cli_tunnel_kill[i], "Requested by CLI"); + cli_tunnel_kill[i] = 0; + } + } +#endif + + for (s = 0; s < MAXSESSION; s++) + { + // check for expired sessions + if (session[s].die && session[s].die <= when) + { + STAT(session_timeout); + sessionkill(s, "Expired"); + continue; + } + + // Drop sessions who have not responded within IDLE_TIMEOUT seconds + if (session[s].user[0] && (time_now - session[s].last_packet >= IDLE_TIMEOUT)) + { + sessionkill(s, "No response to LCP ECHO requests"); + continue; + } + + // No data in IDLE_TIMEOUT seconds, send LCP ECHO + if (session[s].user[0] && (time_now - session[s].last_packet >= ECHO_TIMEOUT)) + { + u8 b[MAXCONTROL] = {0}; + u8 *q = makeppp(b, 0, 0, session[s].tunnel, s, PPPLCP); + + *q = EchoReq; + *(u8 *)(q + 1) = (time_now % 255); // ID + *(u16 *)(q + 2) = htons(8); // Length + *(u32 *)(q + 4) = 0; // Magic Number (not supported) + + log(4, session[s].ip, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n", + time_now - session[s].last_packet); + tunnelsend(b, 24, session[s].tunnel); // send it + continue; + } + } + if (accounting_dir && next_acct <= when) + { + // Dump accounting data + next_acct = when + ACCT_TIME; + dump_acct_info(); + } + + if (cluster_sockfd && next_cluster_ping <= when) + { + // Dump accounting data + next_cluster_ping = when + 50; + cluster_send_message(cluster_address, bind_address, C_PING, hostname, strlen(hostname)); + } + + if (best <= when) + best = when + 1; // should not really happen + to.tv_sec = (best - when) / 10; + to.tv_usec = 100000 * ((best - when) % 10); + log(5, 0, 0, 0, "Next time check in %d.%d seconds\n", (best - when) / 10, ((best - when) % 10)); + } + } +} + +// Init data structures +void initdata(void) +{ + int i; + + _statistics = mmap(NULL, sizeof(struct Tstats), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (_statistics <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for _statistics: %s\n", strerror(errno)); + exit(1); + } + tunnel = mmap(NULL, sizeof(tunnelt) * MAXTUNNEL, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (tunnel <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for tunnels: %s\n", strerror(errno)); + exit(1); + } + session = mmap(NULL, sizeof(sessiont) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (session <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for sessions: %s\n", strerror(errno)); + exit(1); + } + radius = mmap(NULL, sizeof(radiust) * MAXRADIUS, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (radius <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno)); + exit(1); + } + ip_address_pool = mmap(NULL, sizeof(ippoolt) * MAXIPPOOL, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (ip_address_pool <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno)); + exit(1); + } +#ifdef RINGBUFFER + ringbuffer = mmap(NULL, sizeof(struct Tringbuffer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (ringbuffer <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno)); + exit(1); + } + memset(ringbuffer, 0, sizeof(struct Tringbuffer)); +#endif + +#ifdef HAVE_LIBCLI + cli_session_kill = mmap(NULL, sizeof(sessionidt) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (cli_session_kill <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for cli session kill: %s\n", strerror(errno)); + exit(1); + } + memset(cli_session_kill, 0, sizeof(sessionidt) * MAXSESSION); + cli_tunnel_kill = mmap(NULL, sizeof(tunnelidt) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (cli_tunnel_kill <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for cli tunnel kill: %s\n", strerror(errno)); + exit(1); + } + memset(cli_tunnel_kill, 0, sizeof(tunnelidt) * MAXSESSION); + + filter_buckets = mmap(NULL, sizeof(tbft) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (filter_buckets <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for filter buckets: %s\n", strerror(errno)); + exit(1); + } + memset(filter_buckets, 0, sizeof(tbft) * MAXSESSION); + +#endif + + memset(tunnel, 0, sizeof(tunnelt) * MAXTUNNEL); + memset(session, 0, sizeof(sessiont) * MAXSESSION); + memset(radius, 0, sizeof(radiust) * MAXRADIUS); + memset(ip_address_pool, 0, sizeof(ippoolt) * MAXIPPOOL); + for (i = 1; i < MAXTUNNEL - 1; i++) + tunnel[i].next = i + 1; + tunnel[MAXTUNNEL - 1].next = 0; + tunnelfree = 1; + for (i = 1; i < MAXSESSION - 1; i++) + session[i].next = i + 1; + session[MAXSESSION - 1].next = 0; + sessionfree = 1; + for (i = 1; i < MAXRADIUS - 1; i++) + radius[i].next = i + 1; + radius[MAXRADIUS - 1].next = 0; + radiusfree = 1; + if (!*hostname) + { + // Grab my hostname unless it's been specified + gethostname(hostname, sizeof(hostname)); + { + struct hostent *h = gethostbyname(hostname); + if (h) + myip = ntohl(*(u32 *) h->h_addr); + } + } + _statistics->start_time = _statistics->last_reset = time(NULL); + + // Start the timer routine off + time(&time_now); + strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now)); +} + +void initiptables(void) +{ + /* Flush the tables here so that we have a clean slate */ + system("iptables -t nat -F l2tpns"); + system("iptables -t mangle -F l2tpns"); +} + +ipt assign_ip_address() +{ + int c = 0; +#ifdef STAT_CALLS + STAT(call_assign_ip_address); +#endif + ip_pool_index++; + while (1) + { + if (ip_pool_index >= ip_pool_size) + { + if (++c == 2) + return 0; + ip_pool_index = 0; + } + if (!ip_address_pool[ip_pool_index].assigned && ip_address_pool[ip_pool_index].address) + { + ip_address_pool[ip_pool_index].assigned = 1; + log(4, ip_address_pool[ip_pool_index].address, 0, 0, "assign_ip_address(): Allocating ip address %lu from pool\n", ip_pool_index); + STAT(ip_allocated); + return ntohl(ip_address_pool[ip_pool_index].address); + } + ip_pool_index++; + } + return 0; +} + +void free_ip_address(ipt address) +{ + int i; + ipt a; +#ifdef STAT_CALLS + STAT(call_free_ip_address); +#endif + + a = ntohl(address); + for (i = 0; i <= ip_pool_size; i++) + { + if (ip_address_pool[i].address == a) + { + STAT(ip_freed); + ip_address_pool[i].assigned = 0; + } + } + uncache_sessionid(htonl(address)); +} + +// Initialize the IP address pool +void initippool() +{ + FILE *f; + char *buf, *p; + int pi = 0; + memset(ip_address_pool, 0, sizeof(ip_address_pool)); + + if (!(f = fopen(IPPOOLFILE, "r"))) + { + log(0, 0, 0, 0, "Can't load pool file " IPPOOLFILE ": %s\n", strerror(errno)); + exit(-1); + } + + buf = (char *)malloc(4096); + + while (pi < MAXIPPOOL && fgets(buf, 4096, f)) + { + char* pool = buf; + if (*buf == '#' || *buf == '\n') + continue; // Skip comments / blank lines + if ((p = (char *)strrchr(buf, '\n'))) *p = 0; + if ((p = (char *)strchr(buf, ':'))) + { + ipt src; + *p = '\0'; + src = inet_addr(buf); + if (src == INADDR_NONE) + { + log(0, 0, 0, 0, "Invalid address pool IP %s", buf); + exit(-1); + } + // This entry is for a specific IP only + if (src != bind_address) + continue; + *p = ':'; + pool = p+1; + } + if ((p = (char *)strchr(pool, '/'))) + { + // It's a range + int numbits = 0; + unsigned long start = 0, end = 0, mask = 0, ip; + struct rtentry r; + + log(2, 0, 0, 0, "Adding IP address range %s\n", buf); + *p++ = 0; + if (!*p || !(numbits = atoi(p))) + { + log(0, 0, 0, 0, "Invalid pool range %s/\n", buf, p); + continue; + } + start = end = ntohl(inet_addr(pool)); + mask = (unsigned long)(pow(2, numbits) - 1) << (32 - numbits); + start &= mask; + end = start + (int)(pow(2, (32 - numbits))) - 1; + for (ip = (start + 1); ip < end && pi < MAXIPPOOL; ip++) + { + if ((ip & 0xFF) == 0 || (ip & 0xFF) == 255) + continue; + ip_address_pool[pi++].address = htonl(ip); + } + + // Add a static route for this pool + log(5, 0, 0, 0, "Adding route for address pool %s/%d\n", inet_toa(htonl(start)), 32+mask); + memset(&r, 0, sizeof(r)); + r.rt_dev = tapdevice; + r.rt_dst.sa_family = AF_INET; + *(u32 *) & (((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr) = htonl(start); + r.rt_genmask.sa_family = AF_INET; + *(u32 *) & (((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr) = htonl(mask); + r.rt_flags = (RTF_UP | RTF_STATIC); + if (ioctl(ifrfd, SIOCADDRT, (void *) &r) < 0) + { + log(0, 0, 0, 0, "Error adding ip address pool route %s/%d: %s\n", inet_toa(start), mask, strerror(errno)); + } + } + else + { + // It's a single ip address + ip_address_pool[pi++].address = inet_addr(pool); + } + } + + free(buf); + fclose(f); + log(1, 0, 0, 0, "IP address pool is %d addresses\n", pi); + ip_pool_size = pi; +} + +void snoop_send_packet(char *packet, u16 size) +{ + if (!snoop_addr.sin_port || snoopfd <= 0 || size <= 0 || !packet) + return; + + if (sendto(snoopfd, packet, size, MSG_DONTWAIT | MSG_NOSIGNAL, (void *) &snoop_addr, sizeof(snoop_addr)) < 0) + log(0, 0, 0, 0, "Error sending intercept packet: %s\n", strerror(errno)); + STAT(packets_snooped); +} + +void dump_acct_info() +{ + char filename[1024]; + char timestr[64]; + time_t t = time(NULL); + int i; + FILE *f = NULL; + +#ifdef STAT_CALLS + STAT(call_dump_acct_info); +#endif + strftime(timestr, 64, "%Y%m%d%H%M%S", localtime(&t)); + snprintf(filename, 1024, "%s/%s", accounting_dir, timestr); + + for (i = 0; i < MAXSESSION; i++) + { + if (!session[i].opened || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden) + continue; + if (!f) + { + time_t now = time(NULL); + if (!(f = fopen(filename, "w"))) + { + log(0, 0, 0, 0, "Can't write accounting info to %s: %s\n", filename, strerror(errno)); + return; + } + log(3, 0, 0, 0, "Dumping accounting information to %s\n", filename); + fprintf(f, "# dslwatch.pl dump file V1.01\n" + "# host: %s\n" + "# time: %ld\n" + "# uptime: %ld\n" + "# format: username ip qos uptxoctets downrxoctets\n", + hostname, + now, + now - basetime); + } + + log(4, 0, 0, 0, "Dumping accounting information for %s\n", session[i].user); + fprintf(f, "%s %s %d %lu %lu\n", + session[i].user, // username + inet_toa(htonl(session[i].ip)), // ip + (session[i].throttle) ? 2 : 1, // qos + (unsigned long)session[i].cin, // uptxoctets + (unsigned long)session[i].cout); // downrxoctets + + session[i].pin = session[i].cin = 0; + session[i].pout = session[i].cout = 0; + } + + if (f) fclose(f); +} + +// Main program +int main(int argc, char *argv[]) +{ + int o; + + _program_name = strdup(argv[0]); + + { + struct rlimit rlim; + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + // Remove the maximum core size + setrlimit(RLIMIT_CORE, &rlim); + // Make core dumps go to /tmp + chdir("/tmp"); + } + + time(&basetime); // start clock + // scan args + + while ((o = getopt(argc, argv, "vc:f:h:a:")) >= 0) + { + switch (o) + { + case 'v': + debug++; + break; + case 'c': + config_file = strdup(optarg); + break; + case 'f': + log_filename = strdup(optarg); + break; + case 'h': + strncpy(hostname, optarg, 1000); + break; + case 'a': + myip = inet_addr(optarg); + if (myip == INADDR_NONE) { + log(0, 0, 0, 0, "Invalid ip %s\n", optarg); + exit(-1); + } + bind_address = myip; + handle_interface = 1; + break; + case '?': + default: + printf("Args are:\n\t-c \tConfig file\n\t-h \tForce hostname\n\t-a
\tUse specific address\n\t-f \tLog File\n\t-v\t\tDebug\n"); + return (0); + break; + } + } + + initiptables(); + initplugins(); + read_config_file(); + initdata(); + log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); + + /* Start up the cluster first, so that we don't have two machines with + * the same IP at once. + * This is still racy, but the second GARP should fix that + */ + cluster_init(bind_address, 0); + cluster_send_message(cluster_address, bind_address, C_HELLO, hostname, strlen(hostname)); + + inittap(); + log(1, 0, 0, 0, "Set up on interface %s\n", tapdevice); + + initudp(); + initrad(); + initippool(); + init_rl(); + if (handle_interface) { + send_garp(bind_address); + } + read_state(); + +#ifdef HAVE_LIBCLI + init_cli(); +#endif + + signal(SIGALRM, sigalrm_handler); + signal(SIGHUP, sighup_handler); + signal(SIGTERM, sigterm_handler); + signal(SIGINT, sigterm_handler); + signal(SIGQUIT, sigquit_handler); + signal(SIGCHLD, sigchild_handler); + signal(SIGSEGV, sigsegv_handler); + if (debug) + { + int n; + for (n = 0; n < numradiusservers; n++) + log(1, 0, 0, 0, "RADIUS to %s\n", inet_toa(htonl(radiusserver[n]))); + } + + alarm(1); + + // Drop privileges here + if (target_uid > 0 && geteuid() == 0) + setuid(target_uid); + + mainloop(); + + if (l2tpsecret) free(l2tpsecret); + if (log_filename) free(log_filename); + if (snoop_destination_host) free(snoop_destination_host); + if (radiussecret) free(radiussecret); + + return 0; +} + +void sighup_handler(int junk) +{ + if (log_stream != stderr) + fclose(log_stream); + + log_stream = NULL; + read_config_file(); +} + +void sigalrm_handler(int junk) +{ + // Log current traffic stats + if (dump_speed) + { + printf("UDP-ETH:%1.0f/%1.0f ETH-UDP:%1.0f/%1.0f TOTAL:%0.1f IN:%lu OUT:%lu\n", + (udp_rx / 1024.0 / 1024.0 * 8), + (eth_tx / 1024.0 / 1024.0 * 8), + (eth_rx / 1024.0 / 1024.0 * 8), + (udp_tx / 1024.0 / 1024.0 * 8), + ((udp_tx + udp_rx + eth_tx + eth_rx) / 1024.0 / 1024.0 * 8), + udp_rx_pkt, eth_rx_pkt); + + udp_tx = udp_rx = 0; + udp_rx_pkt = eth_rx_pkt = 0; + eth_tx = eth_rx = 0; + } + + // Update the internal time counter + time(&time_now); + strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now)); + alarm(1); + + { + // Run timer hooks + struct param_timer p = { time_now }; + run_plugins(PLUGIN_TIMER, &p); + } + +} + +void sigterm_handler(int junk) +{ + log(1, 0, 0, 0, "Shutting down cleanly\n"); + if (config_save_state) + dump_state(); + main_quit++; +} + +void sigquit_handler(int junk) +{ + int i; + log(1, 0, 0, 0, "Shutting down without saving sessions\n"); + for (i = 0; i < MAXSESSION; i++) + { + if (session[i].opened) + sessionkill(i, "L2TPNS Closing"); + } + for (i = 0; i < MAXTUNNEL; i++) + { + if (tunnel[i].ip) + tunnelshutdown(i, "L2TPNS Closing"); + } + main_quit++; +} + +void sigchild_handler(int signal) +{ + int status; + int pid; + + pid = wait(&status); +#ifdef HAVE_LIBCLI + status = (WIFEXITED(status)) ? WEXITSTATUS(status) : 0; + if (pid == cli_pid) + { + if (status == 0) + log(3, 0, 0, 0, "CLI client closed connection\n"); + else + log(2, 0, 0, 0, "CLI child died with rc %d!\n", status); + } +#endif +} + +void *backtrace_buffer[30] = {0}; + +void sigsegv_handler(int signal) +{ + log(0, 0, 0, 0, "----------------------------------------------\n"); + log(0, 0, 0, 0, "- SEGFAULT! -\n"); + log(0, 0, 0, 0, "----------------------------------------------\n"); + _exit(0); +} + +void read_state() +{ + struct stat sb; + FILE *f; + + if (!config_save_state) return; + + if (stat(STATEFILE, &sb) < 0) + return; + + if (sb.st_mtime < (time(NULL) - 60)) + { + log(0, 0, 0, 0, "State file is too old to read\n"); + unlink(STATEFILE); + return; + } + + if (!(f = fopen(STATEFILE, "r"))) + { + log(0, 0, 0, 0, "Can't read state file: %s\n", strerror(errno)); + unlink(STATEFILE); + return; + } + fseek(f, 0, 0); + + log(1, 0, 0, 0, "Reading state information\n"); + { + u32 i, numtunnels; + if (fread(&numtunnels, sizeof(numtunnels), 1, f) <= 0) + { + log(0, 0, 0, 0, "Error reading saved state (tunnel count): %s\n", strerror(errno)); + fclose(f); + unlink(STATEFILE); + return; + } + log(2, 0, 0, 0, "Reading %lu tunnels\n", numtunnels); + fread(tunnel, sizeof(tunnelt), numtunnels, f); + tunnelfree = 0; + for (i = 0; i < numtunnels; i++) + { + tunnel[i].controlc = 0; + tunnel[i].controls = NULL; + tunnel[i].controle = NULL; + if (*tunnel[i].hostname) + { + log(3, 0, 0, 0, "Created tunnel for %s\n", tunnel[i].hostname); + tunnelfree = i; + } + } + tunnelfree++; + } + { + u32 i, numsessions; + if (fread(&numsessions, sizeof(numsessions), 1, f) <= 0) + { + log(0, 0, 0, 0, "Error reading saved state (session count): %s\n", strerror(errno)); + fclose(f); + unlink(STATEFILE); + return; + } + log(2, 0, 0, 0, "Reading %lu sessions\n", numsessions); + if (fread(session, sizeof(sessiont), numsessions, f) < numsessions) + { + log(0, 0, 0, 0, "Error reading saved state (%d sessions): %s\n", numsessions, strerror(errno)); + fclose(f); + unlink(STATEFILE); + return; + } + for (i = 0; i < numsessions; i++) + { + session[i].tbf = 0; + session[i].throttle = 0; + if (session[i].opened) + { + log(2, 0, i, 0, "Loaded active session for user %s\n", session[i].user); + if (session[i].ip && session[i].ip != 0xFFFFFFFE) + { + int x; + sessionsetup(session[i].tunnel, i, 0); + for (x = 0; x < MAXIPPOOL && ip_address_pool[x].address; x++) + { + if (ip_address_pool[x].address == session[i].ip) + { + ip_address_pool[x].assigned = 1; + break; + } + } + } + else + { + log(2, 0, i, 0, "No IP for session\n"); + } + } + } + for (i = 0; i < numsessions && session[i].opened; i++) + sessionfree = session[i].next; + } + fclose(f); + log(0, 0, 0, 0, "Loaded saved state information\n"); + unlink(STATEFILE); +} + +void dump_state() +{ + FILE *f; + + if (!config_save_state) return; + + if ((f = fopen(STATEFILE, "w"))) + { + u32 i; + log(1, 0, 0, 0, "Dumping state information\n"); + + i = MAXTUNNEL; + fwrite(&i, sizeof(i), 1, f); // Number of tunnels + + log(2, 0, 0, 0, "Dumping %lu tunnels\n", i); + fwrite(tunnel, sizeof(tunnelt), MAXTUNNEL, f); + + i = MAXSESSION; + fwrite(&i, sizeof(i), 1, f); // Number of sessions + log(2, 0, 0, 0, "Dumping %lu sessions\n", i); + fwrite(session, sizeof(sessiont), MAXSESSION, f); + + fclose(f); + } + else + { + log(0, 0, 0, 0, "Can't write state information: %s\n", strerror(errno)); + } + return; +} + +void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response) +{ + MD5_CTX ctx; + *challenge_response = NULL; + + if (!l2tpsecret || !*l2tpsecret) + { + log(0, 0, 0, 0, "LNS requested CHAP authentication, but no l2tp secret is defined\n"); + return; + } + + /* + if (challenge_length != 16) + { + log(0, 0, 0, 0, "Challenge length != 16.\n"); + return; + } + */ + + log(4, 0, 0, 0, " Building challenge response for CHAP request\n"); + + *challenge_response = (char *)calloc(17, 1); + + MD5Init(&ctx); + MD5Update(&ctx, &id, 1); + MD5Update(&ctx, l2tpsecret, strlen(l2tpsecret)); + MD5Update(&ctx, challenge, challenge_length); + MD5Final(*challenge_response, &ctx); + + return; +} + +void read_config_file() +{ + FILE *f; + char *buf; + + if (!config_file) return; + if (!(f = fopen(config_file, "r"))) { + fprintf(stderr, "Can't open config file %s: %s\n", config_file, strerror(errno)); + return; + } + + if (radiussecret) + { + free(radiussecret); + radiussecret = NULL; + } + + if (l2tpsecret) + { + free(l2tpsecret); + l2tpsecret = NULL; + } + + if (log_filename) + { + free(log_filename); + log_filename = NULL; + } + + if (snoop_destination_host) + { + free(snoop_destination_host); + snoop_destination_host = NULL; + } + + if (numradiusservers) { + int n; + for (n = 0; n < numradiusservers; n++) + radiusserver[n] = 0; + numradiusservers = 0; + } + + snoop_destination_port = 0L; + config_save_state = 0; + rl_rate = 0L; + debug = 1; + default_dns1 = default_dns2 = 0; + radius_accounting = 0; + + buf = (char *)malloc(4096); + + while (fgets(buf, 4096, f)) { + char *p, *t; + + if (*buf == '#') continue; + if ((p = strchr(buf, '\n'))) *p = 0; + p = t = strchr(buf, '='); + if (!p) continue; + *p = 0; p++; + t--; + while (*p && *p == ' ') p++; + while (*t && *t == ' ') *t-- = 0; + + if (strcmp(buf, "log file") == 0) { + if (!log_filename) + log_filename = strdup(p); + } else if (strcmp(buf, "l2tp secret") == 0) { + if (!l2tpsecret) + l2tpsecret = strdup(p); + log(0, 0, 0, 0, "L2TP Secret is \"%s\"\n", l2tpsecret); + } else if (strcmp(buf, "radius secret") == 0) { + if (!radiussecret) + radiussecret = strdup(p); + log(4, 0, 0, 0, "Radius Secret is \"%s\"\n", radiussecret); + } else if (strcmp(buf, "radius accounting") == 0) { + radius_accounting = atoi(p); + log(4, 0, 0, 0, "Radius Account is %s\n", radius_accounting ? "on" : "off"); + } else if (strcmp(buf, "throttle rate") == 0) { + rl_rate = atol(p); + if (rl_rate == 0) + { + log(1, 0, 0, 0, "Disabled throttling.\n"); + } + else + { + log(1, 0, 0, 0, "Enabled throttling (rate is %lu kbits/s)\n", rl_rate); + } + } else if (strcmp(buf, "debug") == 0) { + debug = atoi(p); + log(debug, 0, 0, 0, "Set debugging level to %d\n", debug); + } else if (strcmp(buf, "accounting dir") == 0) { + accounting_dir = strdup(p); + log(debug, 0, 0, 0, "Will dump accounting information to %s\n", accounting_dir); + } else if (strcmp(buf, "dns server") == 0) { + unsigned long addr = 0; + if (inet_aton(p, (struct in_addr *)&addr) < 0) { + printf("Invalid DNS server %s\n", p); + continue; + } + if (default_dns1 == 0) + default_dns1 = addr; + else if (default_dns2 == 0) + default_dns2 = addr; + } else if (strcmp(buf, "radius server") == 0) { + struct hostent *h = gethostbyname(p); + if (h) + { + while (*h->h_addr_list) + { + ipt ip = ntohl(*(u32 *) * h->h_addr_list); + if (numradiusservers < MAXRADSERVER) + radiusserver[numradiusservers++] = ip; + else + log(0, 0, 0, 0, "Too many RADIUS IPs\n"); + h->h_addr_list++; + } + } + else + { // may be IP? + ipt ip = ntohl(inet_addr(p)); + if (ip && ip != 0xFFFFFFFF) + { + if (numradiusservers < MAXRADSERVER) + radiusserver[numradiusservers++] = ip; + else + log(0, 0, 0, 0, "Too many RADIUS IPs\n"); + } + else + log(0, 0, 0, 0, "Unknown server %s\n", p); + } + } else if (strcmp(buf, "snoop host") == 0) { + snoop_destination_host = strdup(p); + } else if (strcmp(buf, "snoop port") == 0) { + snoop_destination_port = atol(p); + } else if (strcmp(buf, "bind address") == 0) { + if (!bind_address) + { + // Already overridden on the command line + bind_address = inet_addr(p); + handle_interface = 1; + } + } else if (strcmp(buf, "dump speed") == 0) { + dump_speed = atoi(p); + } else if (strcmp(buf, "setuid") == 0) { + target_uid = atoi(p); + } else if (strcmp(buf, "cluster master") == 0) { + struct hostent *h = gethostbyname(p); + if (h) + { + if (*h->h_addr_list) + { + cluster_address = *(u32 *) *h->h_addr_list; + } + } + else + { // may be IP? + cluster_address = inet_addr(p); + } + } else if (strcmp(buf, "save state") == 0) { + if (strcasecmp(p, "no") == 0) { + config_save_state = 0; + } else { + config_save_state = 1; + } + } else if (strcmp(buf, "plugin") == 0) { + add_plugin(p); + } else { + struct param_config cp = { buf, p }; + int rc = run_plugins(PLUGIN_CONFIG, &cp); + if (rc == 0) log(0, 0, 0, 0, "Unknown config directive \"%s\"\n", buf); + } + } + + if (snoop_destination_host) + { + if (inet_aton(snoop_destination_host, &snoop_addr.sin_addr)) + { + snoop_addr.sin_port = htons(snoop_destination_port); + snoop_addr.sin_family = AF_INET; + } + else + { + log(0, 0, 0, 0, "Can't find address for snoop host %s\n", snoop_destination_host); + } + } + + free(buf); + fclose(f); + log(2, 0, 0, 0, "Done reading config file\n"); +} + +int sessionsetup(tunnelidt t, sessionidt s, u8 routes) +{ + // A session now exists, set it up + ipt ip; + char *user; + sessionidt i; +#ifdef STAT_CALLS + STAT(call_sessionsetup); +#endif + log(3, session[s].ip, s, t, "Doing session setup for session\n"); + if (!session[s].ip) { + log(0, session[s].ip, s, t, "VERY VERY BAD! sessionsetup() called with no session[s].ip\n"); + return 1; + } + if (session[s].ip == 0xFFFFFFFE) + { + session[s].ip = assign_ip_address(); // Assign one from the pool; + + log(2, session[s].ip, s, t, "IP assigned is a magic token. Assign address from pool: %s\n", + inet_toa(htonl(session[s].ip))); + } + + // Make sure this is right + session[s].tunnel = t; + // zap old sessions with same IP and/or username + // Don't kill walled_garden sessions - doing so leads to a DoS + // from someone who doesn't need to know the password + ip = session[s].ip; + user = session[s].user; + for (i = 0; i < MAXSESSION; i++) + { + if (i == s) continue; + if (ip == session[i].ip) sessionkill(i, "Duplicate IP address"); + if (!session[s].walled_garden && !session[i].walled_garden && strcasecmp(user, session[i].user) == 0) + sessionkill(i, "Duplicate session for user"); + } + + if (routes) + { + if (session[s].route[routes].ip && session[s].route[routes].mask) + { + log(2, session[s].ip, s, t, "Routing session\n"); + routeset(session[s].ip, 0, 0, 1); + while (routes--) + routeset(session[s].route[routes].ip, session[s].route[routes].mask, + session[s].ip, 1); + } + } + sessionsendarp(s); + if (!session[s].sid) + sendipcp(t, s); + + // Force throttling on or off + // This has the advantage of cleaning up after another throttled user who may have left + // firewall rules lying around + throttle_session(s, session[s].throttle); + + { + struct param_new_session data = { &tunnel[t], &session[s] }; + run_plugins(PLUGIN_NEW_SESSION, &data); + } + + session[s].sid = ++last_sid; + cache_sessionid(htonl(session[s].ip), s); + + cluster_send_session(s); + session[s].last_packet = time_now; + { + char *sessionip, *tunnelip; + sessionip = strdup(inet_toa(ntohl(session[s].ip))); + tunnelip = strdup(inet_toa(ntohl(tunnel[t].ip))); + log(2, session[s].ip, s, t, "Login by %s at %s from %s (%s)\n", + session[s].user, sessionip, tunnelip, tunnel[t].hostname); + if (sessionip) free(sessionip); + if (tunnelip) free(tunnelip); + } + + return 1; // RADIUS OK and IP allocated, done... +} + +#ifdef RINGBUFFER +void ringbuffer_dump(FILE *stream) +{ + int i = ringbuffer->head; + + while (i != ringbuffer->tail) + { + if (*ringbuffer->buffer[i].message) + fprintf(stream, "%d-%s", ringbuffer->buffer[i].level, ringbuffer->buffer[i].message); + if (++i == ringbuffer->tail) break; + if (i == RINGBUFFER_SIZE) i = 0; + } +} +#endif + +void initplugins() +{ + int i; + + loaded_plugins = ll_init(); + // Initialize the plugins to nothing + for (i = 0; i < MAX_PLUGIN_TYPES; i++) + plugins[i] = ll_init(); +} + +void add_plugin(char *plugin_name) +{ + void *p; + int (*initfunc)(struct pluginfuncs *); + char path[256] = {0}; + int i; + struct pluginfuncs funcs; + + funcs._log = _log; + funcs._log_hex = _log_hex; + funcs.inet_toa = inet_toa; + funcs.get_session_by_username = sessionbyuser; + funcs.get_session_by_id = sessiontbysessionidt; + funcs.get_id_by_session = sessionidtbysessiont; + funcs.sessionkill = sessionkill; + funcs.radiusnew = radiusnew; + funcs.radiussend = radiussend; + + snprintf(path, 256, "%s/%s.so", LIBDIR, plugin_name); + + log(2, 0, 0, 0, "Loading plugin from %s\n", path); + p = dlopen(path, RTLD_NOW); + if (!p) + { + log(1, 0, 0, 0, " Plugin load failed: %s\n", dlerror()); + return; + } + + if (ll_contains(loaded_plugins, p)) + { + dlclose(p); + return; + } + + { + int *v = dlsym(p, "__plugin_api_version"); + if (!v || *v != PLUGIN_API_VERSION) + { + log(1, 0, 0, 0, " Plugin load failed: API version mismatch\n", dlerror()); + dlclose(p); + return; + } + } + + initfunc = dlsym(p, "plugin_init"); + if (!initfunc) + { + log(1, 0, 0, 0, " Plugin load failed: function plugin_init() does not exist.\n", dlerror()); + dlclose(p); + return; + } + + if (!initfunc(&funcs)) + { + log(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE.\n", dlerror()); + dlclose(p); + return; + } + + for (i = 0; i < max_plugin_functions; i++) + { + void *x; + if (!plugin_functions[i]) continue; + if ((x = dlsym(p, plugin_functions[i]))) + { + log(3, 0, 0, 0, " Supports function \"%s\"\n", plugin_functions[i]); + ll_push(plugins[i], x); + } + } + log(2, 0, 0, 0, " Loaded plugin %s\n", plugin_name); +} + +void remove_plugin(char *plugin_name) +{ + void *p; + int (*donefunc)(); + char path[256] = {0}; + int i; + + snprintf(path, 256, "%s/%s.so", LIBDIR, plugin_name); + + log(2, 0, 0, 0, "Removing plugin %s\n", plugin_name); + // Get the existing pointer + p = dlopen(path, RTLD_LAZY); + if (!p) return; + + for (i = 0; i < max_plugin_functions; i++) + { + void *x; + if (!plugin_functions[i]) continue; + if ((x = dlsym(p, plugin_functions[i]))) ll_delete(plugins[i], x); + } + + if (ll_contains(loaded_plugins, p)) + { + ll_delete(loaded_plugins, p); + + donefunc = dlsym(p, "plugin_done"); + if (donefunc) donefunc(); + } + + dlclose(p); + dlclose(p); + log(2, 0, 0, 0, "Removed plugin %s\n", plugin_name); +} + +int run_plugins(int plugin_type, void *data) +{ + int (*func)(void *data); + if (!plugins[plugin_type] || plugin_type > max_plugin_functions) return 1; + + ll_reset(plugins[plugin_type]); + while ((func = ll_next(plugins[plugin_type]))) + { + int rc; + rc = func(data); + if (rc == PLUGIN_RET_STOP) return 1; + if (rc == PLUGIN_RET_ERROR) return 0; + } + return 1; +} + +void processcontrol(u8 * buf, int len, struct sockaddr_in *addr) +{ + char *resp; + int l; + struct param_control param = { buf, len, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), NULL, 0, 0 }; + + log(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received "); + dump_packet(buf, log_stream); + + resp = calloc(1400, 1); + l = new_packet(PKT_RESP_ERROR, resp); + *(int *)(resp + 6) = *(int *)(buf + 6); + + param.type = ntohs(*(short *)(buf + 2)); + param.id = ntohl(*(int *)(buf + 6)); + param.data_length = ntohs(*(short *)(buf + 4)) - 10; + param.data = (param.data_length > 0) ? (char *)(buf + 10) : NULL; + param.response = resp; + param.response_length = l; + + if (param.type == PKT_LOAD_PLUGIN && param.data_length) + { + add_plugin(param.data); + } + else if (param.type == PKT_UNLOAD_PLUGIN && param.data_length) + { + remove_plugin(param.data); + } + else + { + run_plugins(PLUGIN_CONTROL, ¶m); + } + + if (param.send_response) + { + send_packet(controlfd, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), param.response, param.response_length); + log(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Sent Control packet response\n"); + } + + free(resp); +} + diff --git a/l2tpns.h b/l2tpns.h new file mode 100644 index 0000000..afbfe3f --- /dev/null +++ b/l2tpns.h @@ -0,0 +1,393 @@ +// L2TPNS Global Stuff +// $Id: l2tpns.h,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include + +#include "config.h" + +#define VERSION "1.0" + +// Limits +#define MAXTUNNEL 500 // could be up to 65535 +#define MAXSESSION 50000 // could be up to 65535 +#define MAXRADIUS 255 +#define MAXCONTROL 1000 // max length control message we ever send... +#define MAXETHER (1500+18) // max packet we try sending to tap +#define MAXTEL 96 // telephone number +#define MAXRADSERVER 10 // max radius servers +#define MAXROUTE 10 // max static routes per session +#define MAXIPPOOL 131072 // max number of ip addresses in pool +#define RINGBUFFER_SIZE 10000 // Number of ringbuffer entries to allocate +#define MAX_LOG_LENGTH 512 // Maximum size of log message +#define ECHO_TIMEOUT 60 // Time between last packet sent and LCP ECHO generation +#define IDLE_TIMEOUT 240 // Time between last packet sent and LCP ECHO generation + +// Constants +#define STATISTICS +#define STAT_CALLS +#define RINGBUFFER +#define UDP 17 +#define TAPDEVICE "/dev/net/tun" +#define CLIUSERS ETCDIR "l2tpns.users" // CLI Users file +#define CONFIGFILE ETCDIR "l2tpns.cfg" // Configuration file +#define IPPOOLFILE ETCDIR "l2tpns.ip_pool" // Address pool configuration +#define STATEFILE "/tmp/l2tpns.dump" // State dump file + +#ifndef LIBDIR +#define LIBDIR "/usr/lib/l2tpns" +#endif + +#define ACCT_TIME 3000 // 5 minute accounting interval +#define L2TPPORT 1701 // L2TP port +#define RADPORT 1645 // old radius port... +#define RADAPORT 1646 // old radius accounting port +#define PKTARP 0x0806 // ARP packet type +#define PKTIP 0x0800 // IP packet type +#define PSEUDOMAC 0x0200 // pseudo MAC prefix (local significant MAC) +#define PPPPAP 0xC023 +#define PPPCHAP 0xC223 +#define PPPLCP 0xC021 +#define PPPIPCP 0x8021 +#define PPPCCP 0x80FD +#define PPPIP 0x0021 +#define PPPMP 0x003D +#define ConfigReq 1 +#define ConfigAck 2 +#define ConfigNak 3 +#define ConfigRej 4 +#define TerminateReq 5 +#define TerminateAck 6 +#define CodeRej 7 +#define ProtocolRej 8 +#define EchoReq 9 +#define EchoReply 10 +#define DiscardRequest 11 + +#undef TC_TBF +#define TC_HTB + +// Types +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned char u8; +typedef u32 ipt; +typedef u16 portt; +typedef u16 sessionidt; +typedef u16 tunnelidt; +typedef u32 clockt; +typedef u8 hasht[16]; + +// structures +typedef struct routes // route +{ + ipt ip; + ipt mask; +} +routet; + +typedef struct controls // control message +{ + struct controls *next; // next in queue + u16 length; // length + u8 buf[MAXCONTROL]; +} +controlt; + +typedef struct stbft +{ + struct stbft *next; + char handle[10]; + char in_use; + int mark; +} tbft; + + +// 336 bytes per session +typedef struct sessions +{ + sessionidt next; // next session in linked list + sessionidt far; // far end session ID + tunnelidt tunnel; // tunnel ID + ipt ip; // IP of session set by RADIUS response + unsigned long sid; // session id for hsddb + u16 nr; // next receive + u16 ns; // next send + u32 magic; // ppp magic number + u32 cin, cout; // byte counts + u32 pin, pout; // packet counts + u32 id; // session id + clockt opened; // when started + clockt die; // being closed, when to finally free + time_t last_packet; // Last packet from the user (used for idle timeouts) + ipt dns1, dns2; // DNS servers + routet route[MAXROUTE]; // static routes + u8 radius; // which radius session is being used (0 for not waiting on authentication) + u8 flags; // various bit flags + u8 snoop; // are we snooping this session? + u8 throttle; // is this session throttled? + u8 walled_garden; // is this session stuck in the walled garden? + u16 mru; // maximum receive unit + u16 tbf; // filter bucket for throttling + char random_vector[MAXTEL]; + int random_vector_length; + char user[129]; // user (needed in seesion for radius stop messages) + char called[MAXTEL]; // called number + char calling[MAXTEL]; // calling number + unsigned long tx_connect_speed; + unsigned long rx_connect_speed; +} +sessiont; + +#define SESSIONPFC 1 // PFC negotiated flags +#define SESSIONACFC 2 // ACFC negotiated flags + +// 168 bytes per tunnel +typedef struct tunnels +{ + tunnelidt next; // next tunnel in linked list + tunnelidt far; // far end tunnel ID + ipt ip; // Ip for far end + portt port; // port for far end + u16 window; // Rx window + u16 nr; // next receive + u16 ns; // next send + clockt last; // when last control message sent (used for resend timeout) + clockt retry; // when to try resenting pending control + clockt die; // being closed, when to finally free + char hostname[128]; // tunnel hostname + char vendor[128]; // LAC vendor + u8 try; // number of retrys on a control message + u16 controlc; // outstaind messages in queue + controlt *controls; // oldest message + controlt *controle; // newest message +} +tunnelt; + +// 180 bytes per radius session +typedef struct radiuss // outstanding RADIUS requests +{ + u8 next; // next in free list + sessionidt session; // which session this applies to + hasht auth; // request authenticator + clockt retry; // ehwne to try next + char calling[MAXTEL]; // calling number + char pass[129]; // password + u8 id; // ID for PPP response + u8 try; // which try we are on + u8 state; // state of radius requests + u8 chap; // set if CHAP used (is CHAP identifier) +} +radiust; + +typedef struct +{ + ipt address; + char assigned; // 1 if assigned, 0 if free +} +ippoolt; + +#ifdef RINGBUFFER +struct Tringbuffer +{ + struct { + char level; + sessionidt session; + tunnelidt tunnel; + ipt address; + char message[MAX_LOG_LENGTH]; + } buffer[RINGBUFFER_SIZE]; + int head; + int tail; +}; +#endif + +enum +{ + RADIUSNULL, // Not in use + RADIUSCHAP, // sending CHAP down PPP + RADIUSAUTH, // sending auth to RADIUS server + RADIUSIPCP, // sending IPCP to end user + RADIUSSTART, // sending start accounting to RADIUS server + RADIUSSTOP, // sending stop accounting to RADIUS server + RADIUSWAIT // waiting timeout before available, in case delayed replies +}; + +struct Tstats +{ + time_t start_time; + time_t last_reset; + + unsigned long tap_rx_packets; + unsigned long tap_tx_packets; + unsigned long tap_rx_bytes; + unsigned long tap_tx_bytes; + unsigned long tap_rx_errors; + unsigned long tap_tx_errors; + + unsigned long tunnel_rx_packets; + unsigned long tunnel_tx_packets; + unsigned long tunnel_rx_bytes; + unsigned long tunnel_tx_bytes; + unsigned long tunnel_rx_errors; + unsigned long tunnel_tx_errors; + + unsigned long tunnel_retries; + unsigned long radius_retries; + + unsigned long arp_errors; + unsigned long arp_replies; + unsigned long arp_discarded; + unsigned long arp_sent; + unsigned long arp_recv; + + unsigned long packets_snooped; + + unsigned long tunnel_created; + unsigned long session_created; + unsigned long tunnel_timeout; + unsigned long session_timeout; + unsigned long radius_timeout; + unsigned long radius_overflow; + unsigned long tunnel_overflow; + unsigned long session_overflow; + + unsigned long ip_allocated; + unsigned long ip_freed; +#ifdef STAT_CALLS + unsigned long call_processtap; + unsigned long call_processarp; + unsigned long call_processipout; + unsigned long call_processudp; + unsigned long call_sessionbyip; + unsigned long call_sessionbyuser; + unsigned long call_sendarp; + unsigned long call_sendipcp; + unsigned long call_tunnelsend; + unsigned long call_sessionkill; + unsigned long call_sessionshutdown; + unsigned long call_tunnelkill; + unsigned long call_tunnelshutdown; + unsigned long call_assign_ip_address; + unsigned long call_free_ip_address; + unsigned long call_dump_acct_info; + unsigned long call_sessionsetup; + unsigned long call_processpap; + unsigned long call_processchap; + unsigned long call_processlcp; + unsigned long call_processipcp; + unsigned long call_processipin; + unsigned long call_processccp; + unsigned long call_sendchap; + unsigned long call_processrad; + unsigned long call_radiussend; + unsigned long call_radiusretry; +#endif +}; + +#ifdef STATISTICS +#define STAT(x) _statistics->x++ +#define INC_STAT(x,y) _statistics->x += y +#define GET_STAT(x) _statistics->x +#define SET_STAT(x, y) _statistics->x = y +#else +#define STAT(x) +#define INC_STAT(x,y) +#define GET_STAT(x) 0 +#define SET_STAT(x, y) +#endif + +// arp.c +void sendarp(int ifr_idx, const unsigned char* mac, ipt ip); + + +// ppp.c +void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processccp(tunnelidt t, sessionidt s, u8 * p, u16 l); +void sendchap(tunnelidt t, sessionidt s); +u8 *makeppp(u8 * b, u8 * p, int l, tunnelidt t, sessionidt s, u16 mtype); +u8 *findppp(u8 * b, u8 mtype); +void initlcp(tunnelidt t, sessionidt s); +void dumplcp(char *p, int l); + + +// radius.c +void initrad(void); +void radiussend(u8 r, u8 state); +void processrad(u8 *buf, int len); +void radiusretry(u8 r); +u8 radiusnew(sessionidt s); + +// throttle.c +int throttle_session(sessionidt s, int throttle); + + +// rl.c +void init_rl(); +u16 rl_create_tbf(); +u16 rl_get_tbf(); +void rl_done_tbf(u16 t); +void rl_destroy_tbf(u16 t); + + +// l2tpns.c +clockt now(void); +clockt backoff(u8 try); +void routeset(ipt ip, ipt mask, ipt gw, u8 add); +void inittap(void); +void initudp(void); +void initdata(void); +void initippool(); +sessionidt sessionbyip(ipt ip); +/* NB - sessionbyuser ignores walled garden'd sessions */ +sessionidt sessionbyuser(char *username); +void sessionshutdown(sessionidt s, char *reason); +void sessionsendarp(sessionidt s); +void send_garp(ipt ip); +void sessionkill(sessionidt s, char *reason); +void control16(controlt * c, u16 avp, u16 val, u8 m); +void control32(controlt * c, u16 avp, u32 val, u8 m); +void controls(controlt * c, u16 avp, char *val, u8 m); +void controlb(controlt * c, u16 avp, char *val, unsigned int len, u8 m); +controlt *controlnew(u16 mtype); +void controlnull(tunnelidt t); +void controladd(controlt * c, tunnelidt t, sessionidt s); +void tunnelsend(u8 * buf, u16 l, tunnelidt t); +void tunnelkill(tunnelidt t, char *reason); +void tunnelshutdown(tunnelidt t, char *reason); +void sendipcp(tunnelidt t, sessionidt s); +void processipout(u8 * buf, int len); +void processarp(u8 * buf, int len); +void processudp(u8 * buf, int len, struct sockaddr_in *addr); +void processtap(u8 * buf, int len); +void processcontrol(u8 * buf, int len, struct sockaddr_in *addr); +ipt assign_ip_address(); +void free_ip_address(ipt address); +void snoop_send_packet(char *packet, u16 size); +void dump_acct_info(); +void mainloop(void); +#define log _log +#ifndef log_hex +#define log_hex(a,b,c,d) do{if (a <= debug) _log_hex(a,0,0,0,b,c,d);}while (0) +#endif +void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...); +void _log_hex(int level, ipt address, sessionidt s, tunnelidt t, const char *title, const char *data, int maxsize); +void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response); +int sessionsetup(tunnelidt t, sessionidt s, u8 routes); +int cluster_send_session(int s); +int cluster_send_tunnel(int t); +#ifdef HAVE_LIBCLI +void init_cli(); +void cli_do(int sockfd); +#endif +#ifdef RINGBUFFER +void ringbuffer_dump(FILE *stream); +#endif +void initplugins(); +int run_plugins(int plugin_type, void *data); +void add_plugin(char *plugin_name); +void remove_plugin(char *plugin_name); diff --git a/ll.c b/ll.c new file mode 100644 index 0000000..a4aad8b --- /dev/null +++ b/ll.c @@ -0,0 +1,141 @@ +// L2TPNS Linked List Stuff +// $Id: ll.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ll.h" + +linked_list *ll_init() +{ + return (linked_list *)calloc(sizeof(linked_list), 1); +} + +void ll_done(linked_list *l) +{ + li *i = l->head, *n; + + while (i) + { + n = i->next; + free(i); + i = n; + } + + free(l); +} + +li *ll_push(linked_list *l, void *data) +{ + li *i; + + if (!l) return NULL; + if (!(i = (li *)calloc(sizeof(li), 1))) return NULL; + + i->data = data; + i->next = NULL; + if (l->end) + l->end->next = i; + else + l->head = i; + l->end = i; + + return i; +} + +void *ll_pop(linked_list *l) +{ + li *i; + void *data; + + if (!l) return NULL; + if (!l->head) + return NULL; + + data = l->head->data; + i = l->head->next; + free(l->head); + l->head = i; + return data; +} + +void ll_iterate(linked_list *l, int(*func)(void *)) +{ + li *i; + if (!l || !func) return; + + for (i = l->head; i; i = i->next) + { + if (i->data) + if (!func(i)) + break; + } +} + +void ll_reset(linked_list *l) +{ + if (!l) return; + l->current = NULL; +} + +void *ll_next(linked_list *l) +{ + if (!l) return NULL; + if (!l->current) + l->current = l->head; + else + l->current = l->current->next; + if (!l->current) + return NULL; + return l->current->data; +} + +void ll_delete(linked_list *l, void *data) +{ + li *i = l->head, *p = NULL; + + while (i) + { + if (i->data == data) + { + if (l->head == i) l->head = i->next; + if (l->end == i) l->end = i->next; + if (p) p->next = i->next; + free(i); + l->current = NULL; + return; + } + p = i; + i = i->next; + } +} + +int ll_size(linked_list *l) +{ + int count = 0; + li *i; + + if (!l) return 0; + + for (i = l->head; i; i = i->next) + if (i->data) count++; + + return count; +} + +int ll_contains(linked_list *l, void *search) +{ + li *i; + for (i = l->head; i; i = i->next) + if (i->data == search) + return 1; + return 0; +} + diff --git a/ll.h b/ll.h new file mode 100644 index 0000000..ad4d30c --- /dev/null +++ b/ll.h @@ -0,0 +1,28 @@ +#ifndef __LL_H__ +#define __LL_H__ + +typedef struct s_li +{ + void *data; + struct s_li *next; +} li; + +typedef struct s_ll +{ + li *head; + li *end; + li *current; +} linked_list; + +linked_list *ll_init(); +void ll_done(linked_list *l); +li *ll_push(linked_list *l, void *data); +void ll_delete(linked_list *l, void *data); +void *ll_pop(linked_list *l); +void ll_iterate(linked_list *l, int(*func)(void *)); +void ll_reset(linked_list *l); +void *ll_next(linked_list *l); +int ll_size(linked_list *l); +int ll_contains(linked_list *l, void *search); + +#endif diff --git a/machines.cfg b/machines.cfg new file mode 100644 index 0000000..a10a694 --- /dev/null +++ b/machines.cfg @@ -0,0 +1,39 @@ +#!/usr/bin/perl -w +# vim:ft=perl + +$m{$_class} = { + IPfilter => "iptables", + + File_append => [ + [ "/etc/modules.conf", "alias char-major-10-200 tun\n", "char-major-10-200", "depmod -a" ], + ], + inittab_include => [ + "$_path/src/l2tpns", + ], + rpm_check => [ + "$_path/rpm/libcli-1.2.0-1.i386.rpm", + 'iproute', + 'perl-Compress-Zlib', + 'perl-MLDBM', + 'perl-Storable', + ], + Firewall => { + 'all' => '1701:udp', + }, + F_Firewall => { + all => 'all', + }, + Sysctl => { + 'net.ipv4.ip_forward' => 1, + 'net.ipv4.conf.all.proxy_arp' => 1, + 'net.core.rmem_max' => 8388608, + 'net.core.wmem_max' => 8388608, + 'net.core.rmem_default' => 8388608, + 'net.core.wmem_default' => 8388608, + 'net.ipv4.tcp_rmem' => '4096 65530 128388607', + 'net.ipv4.tcp_wmem' => '4096 65530 128388607', + }, + File_install => [ + [ "/etc/logrotate.d/l2tpns", "$_path/etc/l2tpns.logrotate", undef, { mode => 0755 } ], + ], +}; diff --git a/md5.c b/md5.c new file mode 100644 index 0000000..e0691c8 --- /dev/null +++ b/md5.c @@ -0,0 +1,349 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. 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" + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST((UINT4[4], unsigned char[64])); +static void Encode PROTO_LIST((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST((UINT4 *, unsigned char *, unsigned int)); +static void MD5_memcpy PROTO_LIST((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST((POINTER, int, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* 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); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void +MD5Init(context) + MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void +MD5Update(context, input, inputLen) + MD5_CTX *context; /* context */ + unsigned char *input; /* input block */ + unsigned int inputLen; /* length of input block */ +{ + unsigned int i, + index, + partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4) inputLen << 3)) < ((UINT4) inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4) inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. + */ + if (inputLen >= partLen) + { + MD5_memcpy((POINTER) & context->buffer[index], (POINTER) input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy((POINTER) & context->buffer[index], (POINTER) & input[i], inputLen - i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void +MD5Final(digest, context) + unsigned char digest[16]; /* message digest */ + MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, + padLen; + + /* Save number of bits */ + Encode(bits, context->count, 8); + + /* Pad out to 56 mod 64. + */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. + */ + MD5_memset((POINTER) context, 0, sizeof(*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void +MD5Transform(state, block) + UINT4 state[4]; + unsigned char block[64]; +{ + UINT4 a = state[0], + b = state[1], + c = state[2], + d = state[3], + x[16]; + + Decode(x, block, 64); + + /* Round 1 */ + FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ + + GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + + */ + MD5_memset((POINTER) x, 0, sizeof(x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void +Encode(output, input, len) + unsigned char *output; + UINT4 *input; + unsigned int len; +{ + unsigned int i, + j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[j] = (unsigned char) (input[i] & 0xff); + output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff); + output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff); + output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void +Decode(output, input, len) + UINT4 *output; + unsigned char *input; + unsigned int len; +{ + unsigned int i, + j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) | (((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void +MD5_memcpy(output, input, len) + POINTER output; + POINTER input; + unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void +MD5_memset(output, value, len) + POINTER output; + int value; + unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *) output)[i] = (char) value; +} diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..53ecf51 --- /dev/null +++ b/md5.h @@ -0,0 +1,72 @@ +/* GLOBAL.H - RSAREF types and constants + */ + +#include "config.h" + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. +The following makes PROTOTYPES default to 0 if it has not already + + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. +If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + + +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. 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. + */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST + ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + diff --git a/nsctl.c b/nsctl.c new file mode 100644 index 0000000..8a8aee0 --- /dev/null +++ b/nsctl.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "control.h" + +struct { char *command; int pkt_type; int params; } commands[] = { + { "load_plugin", PKT_LOAD_PLUGIN, 1 }, + { "unload_plugin", PKT_UNLOAD_PLUGIN, 1 }, + { "garden", PKT_GARDEN, 1 }, + { "ungarden", PKT_UNGARDEN, 1 }, +}; + +char *dest_host = NULL; +unsigned int dest_port = 1702; +int udpfd; + +int main(int argc, char *argv[]) +{ + int len = 0; + int dest_ip = 0; + int pkt_type = 0; + char *packet = NULL; + int i; + + setbuf(stdout, NULL); + + if (argc < 3) + { + printf("Usage: %s [args...]\n", argv[0]); + return 1; + } + + dest_host = strdup(argv[1]); + + { + // Init socket + int on = 1; + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(1703); + udpfd = socket(AF_INET, SOCK_DGRAM, 17); + setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + return(1); + } + } + + { + struct hostent *h = gethostbyname(dest_host); + if (h) dest_ip = ntohl(*(unsigned int *)h->h_addr); + if (!dest_ip) dest_ip = ntohl(inet_addr(dest_host)); + if (!dest_ip) + { + printf("Can't resolve \"%s\"\n", dest_host); + return 0; + } + } + + if (!(packet = calloc(1400, 1))) + { + perror("calloc"); + return(1); + } + + srand(time(NULL)); + + // Deal with command & params + for (i = 0; i < (sizeof(commands) / sizeof(commands[0])); i++) + { + if (strcasecmp(commands[i].command, argv[2]) == 0) + { + int p; + pkt_type = commands[i].pkt_type; + len = new_packet(pkt_type, packet); + if (argc < (commands[i].params + 3)) + { + printf("Not enough parameters for %s\n", argv[2]); + return 1; + } + for (p = 0; p < commands[i].params; p++) + { + strncpy((packet + len), argv[p + 3], 1400 - len); + len += strlen(argv[p + 3]) + 1; + } + break; + } + } + if (!pkt_type) + { + printf("Unknown command\n"); + return 1; + } + + send_packet(udpfd, dest_ip, dest_port, packet, len); + + { + int n; + fd_set r; + struct timeval timeout; + + FD_ZERO(&r); + FD_SET(udpfd, &r); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + n = select(udpfd + 1, &r, 0, 0, &timeout); + if (n <= 0) + { + printf("Timeout waiting for packet\n"); + return 0; + } + } + if ((len = read_packet(udpfd, packet))) + { + printf("Received "); + dump_packet(packet, stdout); + } + + return 0; +} + diff --git a/plugin.h b/plugin.h new file mode 100644 index 0000000..28855e1 --- /dev/null +++ b/plugin.h @@ -0,0 +1,116 @@ +#ifndef __PLUGIN_H__ +#define __PLUGIN_H__ + +#define PLUGIN_API_VERSION 1 +#define MAX_PLUGIN_TYPES 30 + +#define PLUGIN_PRE_AUTH 1 +#define PLUGIN_POST_AUTH 2 +#define PLUGIN_PACKET_RX 3 +#define PLUGIN_PACKET_TX 4 +#define PLUGIN_TIMER 5 +#define PLUGIN_CONFIG 6 +#define PLUGIN_NEW_SESSION 7 +#define PLUGIN_KILL_SESSION 8 +#define PLUGIN_CONTROL 9 +#define PLUGIN_RADIUS_RESPONSE 10 + +#define PLUGIN_RET_ERROR 0 +#define PLUGIN_RET_OK 1 +#define PLUGIN_RET_STOP 2 + +struct pluginfuncs +{ + void (*_log)(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...); + void (*_log_hex)(int level, ipt address, sessionidt s, tunnelidt t, const char *title, const char *data, int maxsize); + char *(*inet_toa)(unsigned long addr); + sessionidt (*get_session_by_username)(char *username); + sessiont *(*get_session_by_id)(sessionidt s); + sessionidt (*get_id_by_session)(sessiont *s); + void (*sessionkill)(sessionidt s, char *reason); + u8 (*radiusnew)(sessionidt s); + void (*radiussend)(u8 r, u8 state); +}; + +struct param_pre_auth +{ + tunnelt *t; + sessiont *s; + char *username; + char *password; + int protocol; + int continue_auth; +}; + +struct param_post_auth +{ + tunnelt *t; + sessiont *s; + char *username; + short auth_allowed; + int protocol; +}; + +struct param_packet_rx +{ + tunnelt *t; + sessiont *s; + char *buf; + int len; +}; + +struct param_packet_tx +{ + tunnelt *t; + sessiont *s; + char *buf; + int len; +}; + +struct param_timer +{ + time_t time_now; +}; + +struct param_config +{ + char *key; + char *value; +}; + +struct param_control +{ + char *buf; + int l; + unsigned int source_ip; + unsigned short source_port; + char *response; + int response_length; + int send_response; + short type; + int id; + char *data; + int data_length; +}; + +struct param_new_session +{ + tunnelt *t; + sessiont *s; +}; + +struct param_kill_session +{ + tunnelt *t; + sessiont *s; +}; + +struct param_radius_response +{ + tunnelt *t; + sessiont *s; + char *key; + char *value; +}; + +#endif diff --git a/ppp.c b/ppp.c new file mode 100644 index 0000000..cf9122d --- /dev/null +++ b/ppp.c @@ -0,0 +1,725 @@ +// L2TPNS PPP Stuff +// $Id: ppp.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "constants.h" +#include "plugin.h" +#include "util.h" + +extern char debug; +extern tunnelt *tunnel; +extern sessiont *session; +extern radiust *radius; +extern u16 tapmac[3]; +extern int tapfd; +extern char hostname[1000]; +extern struct Tstats *_statistics; +extern unsigned long eth_tx; +extern time_t time_now; + +// Process PAP messages +void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ + char user[129]; + char pass[129]; + +#ifdef STAT_CALLS + STAT(call_processpap); +#endif + log_hex(5, "PAP", p, l); + if (l < 4) + { + log(1, 0, s, t, "Short PAP %u bytes", l); + STAT(tunnel_rx_errors); + return ; + } + if (*p != 1) + { + log(1, 0, s, t, "Unexpected PAP code %d\n", *p); + STAT(tunnel_rx_errors); + return ; + } + if (ntohs(*(u16 *) (p + 2)) > l) + { + log(1, 0, s, t, "Length mismatch PAP %d/%d\n", ntohs(*(u16 *) (p + 2)), l); + STAT(tunnel_rx_errors); + return ; + } + { + u8 *b = p; + b += 4; + if (*b && *b < sizeof(user)) + memcpy(user, b + 1, *b); + user[*b] = 0; + b += 1 + *b; + if (*b && *b < sizeof(pass)) + memcpy(pass, b + 1, *b); + pass[*b] = 0; + log(3, 0, s, t, "PAP login %s/%s\n", user, pass); + } + if (session[s].ip || !session[s].radius) + { + // respond now, either no RADIUS available or already authenticated + u8 b[MAXCONTROL]; + u8 id = p[1]; + u8 *p = makeppp(b, 0, 0, t, s, PPPPAP); + if (session[s].ip) + *p = 2; // ACK + else + *p = 3; // cant authorise + p[1] = id; + *(u16 *) (p + 2) = htons(5); // length + p[4] = 0; // no message + if (session[s].ip) + { + log(3, session[s].ip, s, t, "%d Already an IP allocated: %s (%d)\n", getpid(), inet_toa(htonl(session[s].ip)), session[s].ip); + } + else + { + log(1, 0, s, t, "No radius session available to authenticate session...\n"); + } + log(3, 0, s, t, "Fallback response to PAP (%s)\n", (session[s].ip) ? "ACK" : "NAK"); + tunnelsend(b, 5 + (p - b), t); // send it + } + else + { // set up RADIUS request + u8 r = session[s].radius; + + // Run PRE_AUTH plugins + struct param_pre_auth packet = { &tunnel[t], &session[s], strdup(user), strdup(pass), PPPPAP, 1 }; + run_plugins(PLUGIN_PRE_AUTH, &packet); + if (!packet.continue_auth) + { + log(3, 0, s, t, "A plugin rejected PRE_AUTH\n"); + if (packet.username) free(packet.username); + if (packet.password) free(packet.password); + return; + } + + strncpy(session[s].user, packet.username, sizeof(session[s].user)); + strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass)); + + free(packet.username); + free(packet.password); + + radius[r].id = p[1]; + log(3, 0, s, t, "Sending login for %s/%s to radius\n", user, pass); + radiussend(r, RADIUSAUTH); + } +} + +// Process CHAP messages +void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ + u8 r; + u16 len; + +#ifdef STAT_CALLS + STAT(call_processchap); +#endif + log_hex(5, "CHAP", p, l); + r = session[s].radius; + if (!r) + { + log(1, 0, s, t, "Unexpected CHAP message\n"); + STAT(tunnel_rx_errors); + return; + } + if (*p != 2) + { + log(1, 0, s, t, "Unexpected CHAP response code %d\n", *p); + STAT(tunnel_rx_errors); + return; + } + if (p[1] != radius[r].id) + { + log(1, 0, s, t, "Wrong CHAP response ID %d (should be %d) (%d)\n", p[1], radius[r].id, r); + STAT(tunnel_rx_errors); + return ; + } + len = ntohs(*(u16 *) (p + 2)); + if (len > l) + { + log(1, 0, s, t, "Bad CHAP length %d\n", len); + STAT(tunnel_rx_errors); + return ; + } + if (p[4] != 16) + { + log(1, 0, s, t, "Bad CHAP response length %d\n", p[4]); + STAT(tunnel_rx_errors); + return ; + } + if (len - 21 >= sizeof(session[s].user)) + { + log(1, 0, s, t, "CHAP user too long %d\n", len - 21); + STAT(tunnel_rx_errors); + return ; + } + + // Run PRE_AUTH plugins + { + struct param_pre_auth packet = { &tunnel[t], &session[s], NULL, NULL, PPPCHAP, 1 }; + + packet.username = calloc(len-20, 1); + packet.password = calloc(16, 1); + memcpy(packet.username, p + 21, len - 21); + memcpy(packet.password, p + 5, 16); + + run_plugins(PLUGIN_PRE_AUTH, &packet); + if (!packet.continue_auth) + { + log(3, 0, s, t, "A plugin rejected PRE_AUTH\n"); + if (packet.username) free(packet.username); + if (packet.password) free(packet.password); + return; + } + + strncpy(session[s].user, packet.username, sizeof(session[s].user)); + memcpy(radius[r].pass, packet.password, 16); + + free(packet.username); + free(packet.password); + } + + radius[r].chap = 1; + radiussend(r, RADIUSAUTH); + log(3, 0, s, t, "CHAP login %s\n", session[s].user); +} + +char *ppp_lcp_types[] = { + NULL, + "ConfigReq", + "ConfigAck", + "ConfigNak", + "ConfigRej", + "TerminateReq", + "TerminateAck", + "CodeRej", + "ProtocolRej", + "EchoReq", + "EchoReply", + "DiscardRequest", +}; + +void dumplcp(char *p, int l) +{ + signed int x = l - 3; + char *o = (p + 3); + + log_hex(5, "PPP LCP Packet", p, l); + log(4, 0, 0, 0, "PPP LCP Packet type %d (%s)\n", *p, ppp_lcp_types[(int)*p]); + log(4, 0, 0, 0, "Length: %d\n", l); + if (*p != ConfigReq && *p != ConfigRej && *p != ConfigAck) + return; + + while (x > 2) + { + int type = *(u8 *)(o); + int length = *(u8 *)(o + 1); + if (length == 0) + { + log(4, 0, 0, 0, " Option length is 0...\n"); + break; + } + if (type == 0) + { + log(4, 0, 0, 0, " Option type is 0...\n"); + x -= length; + o += length; + continue; + } + switch (type) + { + case 1: // Maximum-Receive-Unit + log(4, 0, 0, 0, " %s %d\n", lcp_types[type], ntohs(*(u16 *)(o + 2))); + break; + case 3: // Authentication-Protocol + { + int proto = ntohs(*(u16 *)(o + 2)); + log(4, 0, 0, 0, " %s %s\n", lcp_types[type], + proto == 0xC223 ? "CHAP" : "PAP"); + break; + } + case 5: // Magic-Number + { + u32 magicno = ntohl(*(u32 *)(o + 2)); + log(4, 0, 0, 0, " %s %x\n", lcp_types[type], magicno); + break; + } + case 4: // Quality-Protocol + { + u32 qp = ntohl(*(u32 *)(o + 2)); + log(4, 0, 0, 0, " %s %x\n", lcp_types[type], qp); + break; + } + case 7: // Protocol-Field-Compression + { + u32 pfc = ntohl(*(u32 *)(o + 2)); + log(4, 0, 0, 0, " %s %x\n", lcp_types[type], pfc); + break; + } + case 8: // Address-And-Control-Field-Compression + { + u32 afc = ntohl(*(u32 *)(o + 2)); + log(4, 0, 0, 0, " %s %x\n", lcp_types[type], afc); + break; + } + default: + log(2, 0, 0, 0, " Unknown PPP LCP Option type %d\n", type); + break; + } + x -= length; + o += length; + } +} + +// Process LCP messages +void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ + u8 b[MAXCONTROL]; + u8 *q = NULL; + +#ifdef STAT_CALLS + STAT(call_processlcp); +#endif + log_hex(5, "LCP", p, l); + if (l < 4) + { + log(1, session[s].ip, s, t, "Short LCP %d bytes", l); + STAT(tunnel_rx_errors); + return ; + } + if (*p == ConfigAck) + { + log(3, session[s].ip, s, t, "LCP: Discarding ConfigAck\n"); + } + else if (*p == ConfigReq) + { + signed int x = l - 1; + char *o = (p + 1); + + log(3, session[s].ip, s, t, "LCP: ConfigReq (%d bytes)...\n", l); + + while (x > 2) + { + int type = *(u8 *)(o); + int length = *(u8 *)(o + 1); + if (length == 0 || type == 0) break; + switch (type) + { + case 1: // Maximum-Receive-Unit + session[s].mru = ntohs(*(u16 *)(o + 2)); + break; + case 3: // Authentication-Protocol + { + int proto = ntohs(*(u16 *)(o + 2)); + if (proto == 0xC223) + { + log(2, session[s].ip, s, t, " Remote end is trying to do CHAP. Rejecting it.\n"); + + if (!q) + { + q = makeppp(b, p, l, t, s, PPPLCP); + *q++ = ConfigNak; + } + memcpy(q, o, length); + *(u16 *)(q += 2) = htons(0xC023); // NAK -> Use PAP instead + q += length; + } + break; + } + case 5: // Magic-Number + { +// u32 magicno = ntohl(*(u32 *)(o + 2)); + break; + } + case 4: // Quality-Protocol + { +// u32 qp = ntohl(*(u32 *)(o + 2)); + break; + } + case 7: // Protocol-Field-Compression + { +// u32 pfc = ntohl(*(u32 *)(o + 2)); + break; + } + case 8: // Address-And-Control-Field-Compression + { +// u32 afc = ntohl(*(u32 *)(o + 2)); + break; + } + default: + log(2, session[s].ip, s, t, " Unknown PPP LCP Option type %d\n", type); + break; + } + x -= length; + o += length; + } + + if (!q) + { + // Send back a ConfigAck + log(3, session[s].ip, s, t, "ConfigReq accepted, sending as Ack\n"); + q = makeppp(b, p, l, t, s, PPPLCP); + *q = ConfigAck; + tunnelsend(b, l + (q - b), t); + } + else + { + // Already built a ConfigNak... send it + log(3, session[s].ip, s, t, "Sending ConfigNak\n"); + tunnelsend(b, l + (q - b), t); + + log(3, session[s].ip, s, t, "Sending ConfigReq, requesting PAP login\n"); + q = makeppp(b, NULL, 0, t, s, PPPLCP); + *q++ = ConfigReq; + *(u8 *)(q++) = 3; + *(u8 *)(q++) = 4; + *(u16 *)(q += 2) = htons(0xC023); + tunnelsend(b, l + (q - b), t); + } + } + else if (*p == ConfigNak) + { + log(1, session[s].ip, s, t, "Remote end sent a ConfigNak. Ignoring\n"); + dumplcp(p, l); + return ; + } + else if (*p == TerminateReq) + { + *p = TerminateAck; // close + q = makeppp(b, p, l, t, s, PPPLCP); + log(3, session[s].ip, s, t, "LCP: Received TerminateReq. Sending TerminateAck\n"); + sessionshutdown(s, "Remote end closed connection."); + tunnelsend(b, l + (q - b), t); // send it + } + else if (*p == TerminateReq) + { + sessionshutdown(s, "Remote end closed connection."); + } + else if (*p == EchoReq) + { + *p = EchoReply; // reply + *(u32 *) (p + 4) = htonl(session[s].magic); // our magic number + q = makeppp(b, p, l, t, s, PPPLCP); + log(3, session[s].ip, s, t, "LCP: Received EchoReq. Sending EchoReply\n"); + tunnelsend(b, l + (q - b), t); // send it + } + else if (*p == EchoReply) + { + // Ignore it, last_packet time is set earlier than this. + } + else + { + log(1, session[s].ip, s, t, "Unexpected LCP code %d\n", *p); + STAT(tunnel_rx_errors); + return ; + } +} + +// Process IPCP messages +void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ +#ifdef STAT_CALLS + STAT(call_processipcp); +#endif + log_hex(5, "IPCP", p, l); + if (l < 5) + { + log(1, 0, s, t, "Short IPCP %d bytes", l); + STAT(tunnel_rx_errors); + return ; + } + if (*p == ConfigAck) + { // happy with our IPCP + u8 r = session[s].radius; + if ((!r || radius[r].state == RADIUSIPCP) && !session[s].walled_garden) + if (!r) + r = radiusnew(s); + if (r) + radiussend(r, RADIUSSTART); // send radius start, having got IPCP at last + return ; // done + } + if (*p != ConfigReq) + { + log(1, 0, s, t, "Unexpected IPCP code %d\n", *p); + STAT(tunnel_rx_errors); + return ; + } + if (ntohs(*(u16 *) (p + 2)) > l) + { + log(1, 0, s, t, "Length mismatch IPCP %d/%d\n", ntohs(*(u16 *) (p + 2)), l); + STAT(tunnel_rx_errors); + return ; + } + if (!session[s].ip) + { + log(3, 0, s, t, "Waiting on radius reply\n"); + return ; // have to wait on RADIUS eply + } + // form a config reply quoting the IP in the session + { + u8 b[MAXCONTROL]; + u8 *i, + *q; + + q = p + 4; + i = p + l; + while (q < i && q[1]) + { + if (*q != 0x81 && *q != 0x83 && *q != 3) + break; + q += q[1]; + } + if (q < i) + { // reject + u16 n = 4; + i = p + l; + q = makeppp(b, p, l, t, s, PPPIPCP); + *q = ConfigRej; + p += 4; + while (p < i && p[1]) + { + if (*p != 0x81 && *p != 0x83 && *p != 3) + { + log(2, 0, s, t, "IPCP reject %d\n", *p); + memcpy(q + n, p, p[1]); + n += p[1]; + } + p += p[1]; + } + *(u16 *) (q + 2) = htons(n); + tunnelsend(b, n + (q - b), t); // send it + } + else + { + *p = ConfigAck; + i = findppp(p, 0x81); // Primary DNS address + if (i) + { + if (*(u32 *) (i + 2) != htonl(session[s].dns1)) + { + *(u32 *) (i + 2) = htonl(session[s].dns1); + *p = ConfigNak; + } + } + i = findppp(p, 0x83); // Secondary DNS address (TBA, is it) + if (i) + { + if (*(u32 *) (i + 2) != htonl(session[s].dns2)) + { + *(u32 *) (i + 2) = htonl(session[s].dns2); + *p = ConfigNak; + } + } + i = findppp(p, 3); // IP address + if (!i || i[1] != 6) + { + log(1, 0, s, t, "No IP in IPCP request\n"); + STAT(tunnel_rx_errors); + return ; + } + if (*(u32 *) (i + 2) != htonl(session[s].ip)) + { + *(u32 *) (i + 2) = htonl(session[s].ip); + *p = ConfigNak; + } + q = makeppp(b, p, l, t, s, PPPIPCP); + tunnelsend(b, l + (q - b), t); // send it + } + } +} + +// process IP packet received +void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ +#ifdef STAT_CALLS + STAT(call_processipin); +#endif + log_hex(5, "IP", p, l); + + if (l > MAXETHER) + { + log(1, *(u32 *)(p + 12), s, t, "IP packet too long %d\n", l); + STAT(tunnel_rx_errors); + return ; + } + + session[s].cin += l; + session[s].pin++; + eth_tx += l; + + // Add on the tun header + p -= 4; + *(u32 *)p = htonl(0x00000800); + l += 4; + + // Plugin hook + { + struct param_packet_rx packet = { &tunnel[t], &session[s], p, l }; + run_plugins(PLUGIN_PACKET_TX, &packet); + } + + // send to ethernet + if (write(tapfd, p, l) < 0) + { + STAT(tap_tx_errors); + log(0, 0, s, t, "Error writing %d bytes to TAP device: %s (tapfd=%d, p=%p)\n", + l, strerror(errno), tapfd, p); + } + + if (session[s].snoop) + { + // Snooping this session, send it to ASIO + snoop_send_packet(p, l); + } + STAT(tap_tx_packets); + INC_STAT(tap_tx_bytes, l); +} + +// Process LCP messages +void processccp(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ +#ifdef STAT_CALLS + STAT(call_processccp); +#endif + log_hex(5, "CCP", p, l); + if (l < 2 || (*p != ConfigReq && *p != TerminateReq)) + { + log(1, 0, s, t, "Unexpecetd CCP request code %d\n", *p); + STAT(tunnel_rx_errors); + return ; + } + // reject + { + u8 b[MAXCONTROL]; + u8 *q; + if (*p == ConfigReq) + { + if (l < 6) + { + *p = ConfigAck; // accept no compression + } + else + { + *p = ConfigRej; // reject + } + } + else + *p = TerminateAck; // close + q = makeppp(b, p, l, t, s, PPPCCP); + tunnelsend(b, l + (q - b), t); // send it + } +} + +// send a CHAP PP packet +void sendchap(tunnelidt t, sessionidt s) +{ + u8 b[MAXCONTROL]; + u8 r = session[s].radius; + u8 *q; +#ifdef STAT_CALLS + STAT(call_sendchap); +#endif + if (!r) + { + log(1, 0, s, t, "No RADIUS to send challenge\n"); + STAT(tunnel_tx_errors); + return ; + } + log(1, 0, s, t, "Send CHAP challenge\n"); + { // new challenge + int n; + for (n = 0; n < 15; n++) + radius[r].auth[n] = rand(); + } + radius[r].chap = 1; // CHAP not PAP + radius[r].id++; + if (radius[r].state != RADIUSCHAP) + radius[r].try = 0; + radius[r].state = RADIUSCHAP; + radius[r].retry = backoff(radius[r].try++); + if (radius[r].try > 5) + { + sessionshutdown(s, "Timeout CHAP"); + STAT(tunnel_tx_errors); + return ; + } + q = makeppp(b, 0, 0, t, s, PPPCHAP); + *q = 1; // challenhe + q[1] = radius[r].id; // ID + q[4] = 16; // length + memcpy(q + 5, radius[r].auth, 16); // challenge + strcpy(q + 21, hostname); // our name + *(u16 *) (q + 2) = htons(strlen(hostname) + 21); // length + tunnelsend(b, strlen(hostname) + 21 + (q - b), t); // send it +} + +// fill in a L2TP message with a PPP frame, +// copies existing PPP message and changes magic number if seen +// returns start of PPP frame +u8 *makeppp(u8 * b, u8 * p, int l, tunnelidt t, sessionidt s, u16 mtype) +{ + *(u16 *) (b + 0) = htons(0x0002); // L2TP with no options + *(u16 *) (b + 2) = htons(tunnel[t].far); // tunnel + *(u16 *) (b + 4) = htons(session[s].far); // session + b += 6; + if (mtype != PPPLCP && !(session[s].flags & SESSIONACFC)) + { + *(u16 *) b = htons(0xFF03); // HDLC header + b += 2; + } + if (mtype < 0x100 && session[s].flags & SESSIONPFC) + *b++ = mtype; + else + { + *(u16 *) b = htons(mtype); + b += 2; + } + if (p && l) + memcpy(b, p, l); + return b; +} + +// find a PPP option, returns point to option, or 0 if not found +u8 *findppp(u8 * b, u8 mtype) +{ + u16 l = ntohs(*(u16 *) (b + 2)); + if (l < 4) + return 0; + b += 4; + l -= 4; + while (l) + { + if (l < b[1] || !b[1]) + return 0; // faulty + if (*b == mtype) + return b; + l -= b[1]; + b += b[1]; + } + return 0; +} + +// Send initial LCP ConfigReq +void initlcp(tunnelidt t, sessionidt s) +{ + char b[500] = {0}, *q; + + q = makeppp(b, NULL, 0, t, s, PPPLCP); + log(4, 0, s, t, "Sending LCP ConfigReq for PAP\n"); + *q = ConfigReq; + *(u8 *)(q + 1) = (time_now % 255) + 1; // ID + *(u16 *)(q + 2) = htons(8); // Length + *(u8 *)(q + 4) = 3; + *(u8 *)(q + 5) = 4; + *(u16 *)(q + 6) = htons(0xC023); // PAP + tunnelsend(b, 12 + 8, t); +} + diff --git a/radius.c b/radius.c new file mode 100644 index 0000000..3f1d541 --- /dev/null +++ b/radius.c @@ -0,0 +1,614 @@ +// L2TPNS Radius Stuff +// $Id: radius.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "md5.h" +#include "constants.h" +#include "l2tpns.h" +#include "plugin.h" +#include "util.h" + +extern char *radiussecret; +extern radiust *radius; +extern sessiont *session; +extern tunnelt *tunnel; +extern ipt radiusserver[MAXRADSERVER]; // radius servers +extern u32 sessionid; +extern u8 radiusfree; +extern int radfd; +extern u8 numradiusservers; +extern char debug; +extern unsigned long default_dns1, default_dns2; +extern struct Tstats *_statistics; +extern int radius_accounting; +extern uint32_t bind_address; + +const char *radius_state(int state) +{ + static char *tmp = NULL; + int i; + for (i = 0; radius_states[i]; i++) + if (i == state) return radius_states[i]; + + if (tmp == NULL) tmp = (char *)calloc(64, 1); + sprintf(tmp, "%d", state); + return tmp; +} + +// Set up socket for radius requests +void initrad(void) +{ + radfd = socket(AF_INET, SOCK_DGRAM, UDP); +} + +void radiusclear(u8 r, sessionidt s) +{ + radius[r].state = RADIUSNULL; + if (s) session[s].radius = 0; + memset(&radius[r], 0, sizeof(radius[r])); + radius[r].next = radiusfree; + radiusfree = r; +} + +u8 radiusnew(sessionidt s) +{ + u8 r; + if (!radiusfree) + { + log(1, 0, s, session[s].tunnel, "No free RADIUS sessions\n"); + STAT(radius_overflow); + return 0; + }; + r = radiusfree; + session[s].radius = r; + radiusfree = radius[r].next; + memset(&radius[r], 0, sizeof(radius[r])); + radius[r].session = s; + return r; +} + +// Send a RADIUS request +void radiussend(u8 r, u8 state) +{ + struct sockaddr_in addr; + u8 b[4096]; // RADIUS packet + char pass[129]; + int pl; + u8 *p; + sessionidt s; +#ifdef STAT_CALLS + STAT(call_radiussend); +#endif + if (!numradiusservers) + { + log(0, 0, 0, 0, "No RADIUS servers\n"); + return; + } + if (!radiussecret) + { + log(0, 0, 0, 0, "No RADIUS secret\n"); + return; + } + s = radius[r].session; + + if (state != RADIUSAUTH && !radius_accounting) + { + // Radius accounting is turned off + radiusclear(r, s); + return; + } + + if (radius[r].state != state) + radius[r].try = 0; + radius[r].state = state; + radius[r].retry = backoff(radius[r].try++); + log(4, 0, s, session[s].tunnel, "Send RADIUS %d state %s try %d\n", r, radius_state(radius[r].state), radius[r].try); + if (radius[r].try > numradiusservers * 2) + { + if (s) + { + sessionshutdown(s, "RADIUS timeout"); + STAT(radius_timeout); + } + else + { + STAT(radius_retries); + radius[r].state = RADIUSWAIT; + radius[r].retry = 100; + } + return ; + } + // contruct RADIUS access request + switch (state) + { + case RADIUSAUTH: + b[0] = 1; // access request + break; + case RADIUSSTART: + case RADIUSSTOP: + b[0] = 4; // accounting request + break; + default: + log(0, 0, 0, 0, "Unknown radius state %d\n", state); + } + b[1] = r; // identifier + memcpy(b + 4, radius[r].auth, 16); + p = b + 20; + if (s) + { + *p = 1; // user name + p[1] = strlen(session[s].user) + 2; + strcpy(p + 2, session[s].user); + p += p[1]; + } + if (state == RADIUSAUTH) + { + if (radius[r].chap) + { + *p = 3; // CHAP password + p[1] = 19; // length + p[2] = radius[r].id; // ID + memcpy(p + 3, radius[r].pass, 16); // response from CHAP request + p += p[1]; + *p = 60; // CHAP Challenge + p[1] = 18; // length + memcpy(p + 2, radius[r].auth, 16); + p += p[1]; + } + else + { + strcpy(pass, radius[r].pass); + pl = strlen(pass); + while (pl & 15) + pass[pl++] = 0; // pad + if (pl) + { // encrypt + hasht hash; + int p = 0; + while (p < pl) + { + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, radiussecret, strlen(radiussecret)); + if (p) + MD5Update(&ctx, pass + p - 16, 16); + else + MD5Update(&ctx, radius[r].auth, 16); + MD5Final(hash, &ctx); + do + { + pass[p] ^= hash[p & 15]; + p++; + } + while (p & 15); + } + } + *p = 2; // password + p[1] = pl + 2; + if (pl) + memcpy(p + 2, pass, pl); + p += p[1]; + } + } + else if (state == RADIUSSTART || state == RADIUSSTOP) + { // accounting + *p = 40; // accounting type + p[1] = 6; + *(u32 *) (p + 2) = htonl((state == RADIUSSTART) ? 1 : 2); + p += p[1]; + if (s) + { + *p = 44; // session ID + p[1] = 18; + sprintf(p + 2, "%08X%08X", session[s].id, session[s].opened); + p += p[1]; + if (state == RADIUSSTOP) + { // stop + *p = 42; // input octets + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].cin); + p += p[1]; + *p = 43; // output octets + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].cout); + p += p[1]; + *p = 46; // session time + p[1] = 6; + *(u32 *) (p + 2) = htonl(time(NULL) - session[s].opened); + p += p[1]; + *p = 47; // input packets + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].pin); + p += p[1]; + *p = 48; // output spackets + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].pout); + p += p[1]; + } + else + { // start + *p = 41; // delay + p[1] = 6; + *(u32 *) (p + 2) = htonl(time(NULL) - session[s].opened); + p += p[1]; + } + } + } + if (s) + { + *p = 5; // NAS-Port + p[1] = 6; + *(u32 *) (p + 2) = htonl(s); + p += p[1]; + } + if (s && session[s].ip) + { + *p = 8; // Framed-IP-Address + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].ip); + p += p[1]; + } + if (*session[s].called) + { + *p = 30; // called + p[1] = strlen(session[s].called) + 2; + strcpy(p + 2, session[s].called); + p += p[1]; + } + if (*radius[r].calling) + { + *p = 31; // calling + p[1] = strlen(radius[r].calling) + 2; + strcpy(p + 2, radius[r].calling); + p += p[1]; + } + else if (*session[s].calling) + { + *p = 31; // calling + p[1] = strlen(session[s].calling) + 2; + strcpy(p + 2, session[s].calling); + p += p[1]; + } + // NAS-IP-Address + *p = 4; + p[1] = 6; + *(u32 *)(p + 2) = bind_address; + p += p[1]; + + // All AVpairs added + *(u16 *) (b + 2) = htons(p - b); + if (state != RADIUSAUTH) + { + // Build auth for accounting packet + char z[16] = {0}; + char hash[16] = {0}; + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, b, 4); + MD5Update(&ctx, z, 16); + MD5Update(&ctx, b + 20, (p - b) - 20); + MD5Update(&ctx, radiussecret, strlen(radiussecret)); + MD5Final(hash, &ctx); + memcpy(b + 4, hash, 16); + memcpy(radius[r].auth, hash, 16); + } + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + *(u32 *) & addr.sin_addr = htonl(radiusserver[(radius[r].try - 1) % numradiusservers]); + addr.sin_port = htons((state == RADIUSAUTH) ? RADPORT : RADAPORT); + + log_hex(5, "RADIUS Send", b, (p - b)); + sendto(radfd, b, p - b, 0, (void *) &addr, sizeof(addr)); +} + +// process RADIUS response +void processrad(u8 * buf, int len) +{ + u8 b[MAXCONTROL]; + MD5_CTX ctx; + u8 r; + sessionidt s; + tunnelidt t = 0; + hasht hash; + u8 routes = 0; + +#ifdef STAT_CALLS + STAT(call_processrad); +#endif + log_hex(5, "RADIUS Response", buf, len); + if (len < 20 || len < ntohs(*(u16 *) (buf + 2))) + { + log(1, 0, 0, 0, "Duff RADIUS response length %d\n", len); + return ; + } + len = ntohs(*(u16 *) (buf + 2)); + r = buf[1]; + s = radius[r].session; + log(3, 0, s, session[s].tunnel, "Received %s, radius %d response for session %u\n", radius_states[radius[r].state], r, s); + if (!s && radius[r].state != RADIUSSTOP) + { + log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response\n"); + return; + } + if (radius[r].state != RADIUSAUTH && radius[r].state != RADIUSSTART && radius[r].state != RADIUSSTOP) + { + log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response\n"); + return; + } + t = session[s].tunnel; + MD5Init(&ctx); + MD5Update(&ctx, buf, 4); + MD5Update(&ctx, radius[r].auth, 16); + MD5Update(&ctx, buf + 20, len - 20); + MD5Update(&ctx, radiussecret, strlen(radiussecret)); + MD5Final(hash, &ctx); + do { + if (memcmp(hash, buf + 4, 16)) + { + log(0, 0, s, session[s].tunnel, " Incorrect auth on RADIUS response\n"); + radius[r].state = RADIUSWAIT; + break; + } + if ((radius[r].state == RADIUSAUTH && *buf != 2 && *buf != 3) || + ((radius[r].state == RADIUSSTART || radius[r].state == RADIUSSTOP) && *buf != 5)) + { + log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response %d\n", *buf); + radius[r].state = RADIUSWAIT; + break; + } + if (radius[r].state == RADIUSAUTH) + { + log(4, 0, s, session[s].tunnel, " Original response is \"%s\"\n", (*buf == 2) ? "accept" : "reject"); + // process auth response + if (radius[r].chap) + { + // CHAP + u8 *p = makeppp(b, 0, 0, t, s, PPPCHAP); + + { + struct param_post_auth packet = { &tunnel[t], &session[s], session[s].user, (*buf == 2), PPPCHAP }; + run_plugins(PLUGIN_POST_AUTH, &packet); + *buf = packet.auth_allowed ? 2 : 3; + } + + log(3, 0, s, session[s].tunnel, " CHAP User %s authentication %s.\n", session[s].user, + (*buf == 2) ? "allowed" : "denied"); + *p = (*buf == 2) ? 3 : 4; // ack/nak + p[1] = radius[r].id; + *(u16 *) (p + 2) = ntohs(4); // no message + tunnelsend(b, (p - b) + 4, t); // send it + } + else + { + // PAP + u8 *p = makeppp(b, 0, 0, t, s, PPPPAP); + + { + struct param_post_auth packet = { &tunnel[t], &session[s], session[s].user, (*buf == 2), PPPPAP }; + run_plugins(PLUGIN_POST_AUTH, &packet); + *buf = packet.auth_allowed ? 2 : 3; + } + + log(3, 0, s, session[s].tunnel, " PAP User %s authentication %s.\n", session[s].user, + (*buf == 2) ? "allowed" : "denied"); + // ack/nak + *p = *buf; + p[1] = radius[r].id; + *(u16 *) (p + 2) = ntohs(5); + p[4] = 0; // no message + tunnelsend(b, (p - b) + 5, t); // send it + } + + if (*buf == 2) + { + // Login successful + // Extract IP, routes, etc + u8 *p = buf + 20; + u8 *e = buf + len; + for (p = buf + 20; p < e && p[1]; p += p[1]) + { + if (*p == 8) + { + // Statically assigned address + log(3, 0, s, session[s].tunnel, " Radius reply contains IP address %s\n", inet_toa(*(u32 *) (p + 2))); + session[s].ip = ntohl(*(u32 *) (p + 2)); + } + else if (*p == 135) + { + // DNS address + log(3, 0, s, session[s].tunnel, " Radius reply contains primary DNS address %s\n", inet_toa(ntohl(*(u32 *) (p + 2)))); + session[s].dns1 = ntohl(*(u32 *) (p + 2)); + } + else if (*p == 136) + { + // DNS address + log(3, 0, s, session[s].tunnel, " Radius reply contains secondary DNS address %s\n", inet_toa(ntohl(*(u32 *) (p + 2)))); + session[s].dns2 = ntohl(*(u32 *) (p + 2)); + } + else if (*p == 22) + { + // framed-route + ipt ip = 0, mask = 0; + u8 u = 0; + u8 bits = 0; + u8 *n = p + 2; + u8 *e = p + p[1]; + while (n < e && (isdigit(*n) || *n == '.')) + { + if (*n == '.') + { + ip = (ip << 8) + u; + u = 0; + } + else + u = u * 10 + *n - '0'; + n++; + } + ip = (ip << 8) + u; + if (*n == '/') + { + n++; + while (n < e && isdigit(*n)) + bits = bits * 10 + *n++ - '0'; + mask = (( -1) << (32 - bits)); + } + else if ((ip >> 24) < 128) + mask = 0xFF0000; + else if ((ip >> 24) < 192) + mask = 0xFFFF0000; + else + mask = 0xFFFFFF00; + if (routes == MAXROUTE) + { + log(1, 0, s, session[s].tunnel, " Too many routes\n"); + } + else + { + log(3, 0, s, session[s].tunnel, " Radius reply contains route for %d/%d\n", + inet_toa(ip), + inet_toa(mask)); + session[s].route[routes].ip = ip; + session[s].route[routes].mask = mask; + routes++; + } + } + else if (*p == 26) + { + // Vendor-Specific Attribute + int vendor = ntohl(*(int *)(p + 2)); + char attrib = *(p + 6); + char attrib_length = *(p + 7) - 2; + log(3, 0, s, session[s].tunnel, " Radius reply contains Vendor-Specific. Vendor=%d Attrib=%d Length=%d\n", vendor, attrib, attrib_length); + if (attrib_length == 0) continue; + if (attrib != 1) + log(3, 0, s, session[s].tunnel, " Unknown vendor-specific\n"); + else + { + char *avpair, *value, *key, *newp; + avpair = key = calloc(attrib_length + 1, 1); + memcpy(avpair, p + 8, attrib_length); + log(3, 0, s, session[s].tunnel, " Cisco-Avpair value: %s\n", avpair); + do { + value = strchr(key, '='); + if (!value) break; + *value++ = 0; + + // Trim quotes off reply string + if (*value == '\'' || *value == '\"') + { + char *x; + value++; + x = value + strlen(value) - 1; + if (*x == '\'' || *x == '\"') + *x = 0; + } + + // Run hooks + newp = strchr(value, ','); + if (newp) *newp++ = 0; + { + struct param_radius_response p = { &tunnel[session[s].tunnel], &session[s], key, value }; + run_plugins(PLUGIN_RADIUS_RESPONSE, &p); + } + key = newp; + } while (newp); + free(avpair); + } + } + } + } + else if (*buf == 3) + { + log(2, 0, s, session[s].tunnel, " Authentication denied for %s\n", session[s].user); + break; + } + + // Check for Assign-IP-Address + if (!session[s].ip || session[s].ip == 0xFFFFFFFE) + { + session[s].ip = assign_ip_address(); + if (session[s].ip) + log(3, 0, s, t, " No IP allocated by radius. Assigned %s from pool\n", + inet_toa(htonl(session[s].ip))); + else + log(3, 0, s, t, " No IP allocated by radius. None available in pool\n"); + } + if (!session[s].dns1 && default_dns1) + { + session[s].dns1 = htonl(default_dns1); + log(3, 0, s, t, " Sending dns1 = %s\n", inet_toa(default_dns1)); + } + if (!session[s].dns2 && default_dns2) + { + session[s].dns2 = htonl(default_dns2); + log(3, 0, s, t, " Sending dns2 = %s\n", inet_toa(default_dns2)); + } + + if (session[s].ip) + { + // Valid Session, set it up + session[s].sid = 0; + sessionsetup(t, s, routes); + } + else + { + log(0, 0, s, t, " End of processrad(), but no valid session exists.\n"); + sessionkill(s, "Can't create valid session"); + } + } + else + { + log(3, 0, s, t, " RADIUS response in state %d\n", radius[r].state); + } + } while (0); + + // finished with RADIUS + radiusclear(r, s); +} + +// Send a retry for RADIUS/CHAP message +void radiusretry(u8 r) +{ + sessionidt s = radius[r].session; + tunnelidt t = 0; +#ifdef STAT_CALLS + STAT(call_radiusretry); +#endif + if (s) + t = session[s].tunnel; + radius[r].retry = 0; + switch (radius[r].state) + { + case RADIUSCHAP: // sending CHAP down PPP + sendchap(t, s); + break; + case RADIUSIPCP: + sendipcp(t, s); // send IPCP + break; + case RADIUSAUTH: // sending auth to RADIUS server + radiussend(r, RADIUSAUTH); + break; + case RADIUSSTART: // sending start accounting to RADIUS server + radiussend(r, RADIUSSTART); + break; + case RADIUSSTOP: // sending stop accounting to RADIUS server + radiussend(r, RADIUSSTOP); + break; + default: + case RADIUSNULL: // Not in use + case RADIUSWAIT: // waiting timeout before available, in case delayed reply from RADIUS server + // free up RADIUS task + radiusclear(r, s); + log(3, 0, s, session[s].tunnel, "Freeing up radius session %d\n", r); + break; + } +} + diff --git a/rl.c b/rl.c new file mode 100644 index 0000000..7b2084b --- /dev/null +++ b/rl.c @@ -0,0 +1,158 @@ +// L2TPNS Rate Limiting Stuff +// $Id: rl.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "l2tpns.h" + +extern char *radiussecret; +extern radiust *radius; +extern sessiont *session; +extern ipt radiusserver[MAXRADSERVER]; // radius servers +extern u32 sessionid; +extern u8 radiusfree; +extern int radfd; +extern u8 numradiusservers; +extern char debug; +extern char *tapdevice; +extern tbft *filter_buckets; + +#define DEVICE "tun0" + +unsigned long rl_rate = 0; +int next_tbf = 1; + +void init_rl() +{ +#ifdef TC_TBF + system("tc qdisc del dev " DEVICE " root"); + system("tc qdisc add dev " DEVICE " root handle 1: cbq avpkt 10000 bandwidth 100mbit"); + system("tc filter del dev " DEVICE " protocol ip pref 1 fw"); + system("iptables -t mangle -N throttle 2>&1 > /dev/null"); + system("iptables -t mangle -F throttle"); + system("iptables -t mangle -A l2tpns -j throttle"); +#endif +#ifdef TC_HTB + char *commands[] = { + "tc qdisc add dev " DEVICE " root handle 1: htb default 1", + "tc class add dev " DEVICE " parent 1: classid 1:1 htb rate 100mbit burst 300k", + "tc filter del dev " DEVICE " protocol ip pref 1 fw", + "iptables -t mangle -N throttle 2>&1 > /dev/null", + "iptables -t mangle -F throttle", + "iptables -t mangle -A l2tpns -j throttle", + NULL + }; + int i; + + if (!rl_rate) return; + + log(2, 0, 0, 0, "Initializing HTB\n"); + for (i = 0; commands[i] && *commands[i]; i++) + { + log(3, 0, 0, 0, "Running \"%s\"\n", commands[i]); + system(commands[i]); + } + log(2, 0, 0, 0, "Done initializing HTB\n"); +#endif +} + +u16 rl_create_tbf() +{ + u16 t; + char cmd[2048]; + if (!rl_rate) return 0; + + if (next_tbf >= MAXSESSION) return 0; + t = next_tbf++; + snprintf(filter_buckets[t].handle, 9, "1:%d0", t); + +#ifdef TC_TBF + log(2, 0, 0, 0, "Creating new tbf %s\n", filter_buckets[t].handle); + snprintf(cmd, 2048, "tc class add dev " DEVICE " parent 1: classid 1:%d cbq bandwidth 100Mbit rate 100Mbit " + "weight 1 prio 8 allot 1514 cell 8 maxburst 20 avpkt 1000 bounded isolated", + t); + log(3, 0, 0, 0, "%s\n", cmd); + system(cmd); + + snprintf(cmd, 2048, "tc qdisc add dev " DEVICE " parent 1:%d handle %s tbf rate %dkbit buffer 1600 limit 3000", + t, filter_buckets[t].handle, rl_rate); + log(3, 0, 0, 0, "%s\n", cmd); + system(cmd); + + snprintf(cmd, 2048, "tc filter add dev " DEVICE " protocol ip parent 1:0 prio 1 handle %d fw flowid 1:%d", + t, t); + log(3, 0, 0, 0, "%s\n", cmd); + system(cmd); +#endif +#ifdef TC_HTB + log(2, 0, 0, 0, "Creating new htb %s\n", filter_buckets[t].handle); + snprintf(cmd, 2048, "tc class add dev " DEVICE " parent 1: classid %s htb rate %lukbit burst 15k", + filter_buckets[t].handle, rl_rate); + log(3, 0, 0, 0, "%s\n", cmd); + system(cmd); + + snprintf(cmd, 2048, "tc filter add dev " DEVICE " protocol ip parent 1:0 prio 1 handle %d fw flowid %s", + t, filter_buckets[t].handle); + log(3, 0, 0, 0, "%s\n", cmd); + system(cmd); +#endif + + next_tbf++; + return t; +} + +u16 rl_get_tbf() +{ + int i; + if (!rl_rate) return 0; + + for (i = 1; i < MAXSESSION; i++) + { + if (!filter_buckets[i].in_use && *filter_buckets[i].handle) + { + filter_buckets[i].in_use = 1; + log(2, 0, 0, 0, "Returning tbf %s\n", filter_buckets[i].handle); + return i; + } + } + i = rl_create_tbf(); + if (i) filter_buckets[i].in_use = 1; + return i; +} + +void rl_done_tbf(u16 t) +{ + if (!t) return; + if (!rl_rate) return; + log(2, 0, 0, 0, "Freeing up TBF %s\n", filter_buckets[t].handle); + filter_buckets[t].in_use = 0; +} + +void rl_destroy_tbf(u16 t) +{ + char cmd[2048]; + if (!rl_rate) return; + if (filter_buckets[t].in_use) + { + log(0, 0, 0, 0, "Trying to destroy an in-use TBF %s\n", filter_buckets[t].handle); + return; + } +#ifdef TC_TBF + snprintf(cmd, 2048, "tc qdisc del dev " DEVICE " handle %s", filter_buckets[t].handle); + system(cmd); +#endif +#ifdef TC_HTB + snprintf(cmd, 2048, "tc qdisc del dev " DEVICE " handle %s", filter_buckets[t].handle); + system(cmd); +#endif + system("iptables -t mangle -D l2tpns -j throttle"); + system("iptables -t mangle -X throttle"); + memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle)); +} + diff --git a/stamp-h b/stamp-h new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/stamp-h @@ -0,0 +1 @@ +timestamp diff --git a/throttle.c b/throttle.c new file mode 100644 index 0000000..ba551d2 --- /dev/null +++ b/throttle.c @@ -0,0 +1,73 @@ +// L2TPNS Throttle Stuff +// $Id: throttle.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "util.h" + +extern char *radiussecret; +extern radiust *radius; +extern sessiont *session; +extern ipt radiusserver[MAXRADSERVER]; // radius servers +extern u32 sessionid; +extern u8 radiusfree; +extern int radfd; +extern u8 numradiusservers; +extern char debug; +extern unsigned long rl_rate; +extern tbft *filter_buckets; + +// Throttle or Unthrottle a session +int throttle_session(sessionidt s, int throttle) +{ + if (!rl_rate) return 0; + + if (!*session[s].user) + return 0; // User not logged in + + if (throttle) + { + // Throttle them + char cmd[2048] = {0}; + log(2, 0, s, session[s].tunnel, "Throttling session %d for user %s\n", s, session[s].user); + if (!session[s].tbf) session[s].tbf = rl_get_tbf(); + snprintf(cmd, 2048, "iptables -t mangle -A throttle -d %s -j MARK --set-mark %d", inet_toa(ntohl(session[s].ip)), + session[s].tbf); + log(4, 0, s, session[s].tunnel, "Running %s\n", cmd); + system(cmd); + } + else + { + char cmd[2048] = {0}; + log(2, 0, s, session[s].tunnel, "Unthrottling session %d for user %s\n", s, session[s].user); + if (session[s].tbf) + { + int count = 10; + snprintf(cmd, 2048, "iptables -t mangle -D throttle -d %s -j MARK --set-mark %d", inet_toa(ntohl(session[s].ip)), session[s].tbf); + log(4, 0, s, session[s].tunnel, "Running %s\n", cmd); + while (--count) + { + int status = system(cmd); + if (WEXITSTATUS(status) != 0) break; + } + system(cmd); + + rl_done_tbf(session[s].tbf); + session[s].tbf = 0; + } + } + session[s].throttle = throttle; + return 0; +} + diff --git a/util.c b/util.c new file mode 100644 index 0000000..343e956 --- /dev/null +++ b/util.c @@ -0,0 +1,16 @@ +/* Misc util functions */ + +#include "l2tpns.h" + +#include +#include +#include +#include + +char *inet_toa(unsigned long addr) +{ + struct in_addr in; + memcpy(&in, &addr, sizeof(unsigned long)); + return inet_ntoa(in); +} + diff --git a/util.h b/util.h new file mode 100644 index 0000000..ec2c017 --- /dev/null +++ b/util.h @@ -0,0 +1 @@ +char *inet_toa(unsigned long addr); -- 2.20.1