summaryrefslogtreecommitdiffstats
path: root/tcpip
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-04-21 09:51:03 +0200
committerRalf Baechle <ralf@linux-mips.org>1999-04-21 09:51:03 +0200
commit17287576555a5c46fa23549e2e5f073660dccb70 (patch)
tree08be5f5005dad609a2803758b8b825170f6701cb /tcpip
Import ax25-tools 0.0.1 from tarballax25-tools-0.0.1
Diffstat (limited to 'tcpip')
-rw-r--r--tcpip/Makefile.am23
-rw-r--r--tcpip/Makefile.in386
-rw-r--r--tcpip/rip98.conf1
-rw-r--r--tcpip/rip98.conf.519
-rw-r--r--tcpip/rip98d.859
-rw-r--r--tcpip/rip98d.c360
-rw-r--r--tcpip/rip98d.h58
-rw-r--r--tcpip/rip98r.c255
-rw-r--r--tcpip/rip98t.c76
-rw-r--r--tcpip/ttylinkd.853
-rw-r--r--tcpip/ttylinkd.c719
-rw-r--r--tcpip/ttylinkd.conf10
-rw-r--r--tcpip/ttylinkd.conf.522
13 files changed, 2041 insertions, 0 deletions
diff --git a/tcpip/Makefile.am b/tcpip/Makefile.am
new file mode 100644
index 0000000..d047ac5
--- /dev/null
+++ b/tcpip/Makefile.am
@@ -0,0 +1,23 @@
+
+etcfiles = rip98.conf ttylinkd.conf
+etcdir = $(sysconfdir)/ax25
+
+installconf:
+ $(mkinstalldirs) $(DESTDIR)$(etcdir)
+ @list='$(etcfiles)'; for p in $$list; do \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(etcdir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(etcdir)/$$p; \
+ done
+
+sbin_PROGRAMS = rip98d ttylinkd
+
+man_MANS = rip98.conf.5 rip98d.8 ttylinkd.conf.5 ttylinkd.8
+
+EXTRA_DIST = $(man_MANS) $(etcfiles)
+
+rip98d_SOURCES = \
+ rip98d.c \
+ rip98r.c \
+ rip98t.c \
+ rip98d.h
+
diff --git a/tcpip/Makefile.in b/tcpip/Makefile.in
new file mode 100644
index 0000000..90da959
--- /dev/null
+++ b/tcpip/Makefile.in
@@ -0,0 +1,386 @@
+# Makefile.in generated automatically by automake 1.4 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+AWK = @AWK@
+CC = @CC@
+MAKEINFO = @MAKEINFO@
+NCURSES_LIB = @NCURSES_LIB@
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+etcfiles = rip98.conf ttylinkd.conf
+etcdir = $(sysconfdir)/ax25
+
+sbin_PROGRAMS = rip98d ttylinkd
+
+man_MANS = rip98.conf.5 rip98d.8 ttylinkd.conf.5 ttylinkd.8
+
+EXTRA_DIST = $(man_MANS) $(etcfiles)
+
+rip98d_SOURCES = rip98d.c rip98r.c rip98t.c rip98d.h
+
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(sbin_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) -I..
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+X_CFLAGS = @X_CFLAGS@
+X_LIBS = @X_LIBS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+rip98d_OBJECTS = rip98d.o rip98r.o rip98t.o
+rip98d_LDADD = $(LDADD)
+rip98d_DEPENDENCIES =
+rip98d_LDFLAGS =
+ttylinkd_SOURCES = ttylinkd.c
+ttylinkd_OBJECTS = ttylinkd.o
+ttylinkd_LDADD = $(LDADD)
+ttylinkd_DEPENDENCIES =
+ttylinkd_LDFLAGS =
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+man5dir = $(mandir)/man5
+man8dir = $(mandir)/man8
+MANS = $(man_MANS)
+
+NROFF = nroff
+DIST_COMMON = Makefile.am Makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = tar
+GZIP_ENV = --best
+SOURCES = $(rip98d_SOURCES) ttylinkd.c
+OBJECTS = $(rip98d_OBJECTS) ttylinkd.o
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .o .s
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps tcpip/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-sbinPROGRAMS:
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+
+distclean-sbinPROGRAMS:
+
+maintainer-clean-sbinPROGRAMS:
+
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(sbindir)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ done
+
+.c.o:
+ $(COMPILE) -c $<
+
+.s.o:
+ $(COMPILE) -c $<
+
+.S.o:
+ $(COMPILE) -c $<
+
+mostlyclean-compile:
+ -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+ -rm -f *.tab.c
+
+maintainer-clean-compile:
+
+rip98d: $(rip98d_OBJECTS) $(rip98d_DEPENDENCIES)
+ @rm -f rip98d
+ $(LINK) $(rip98d_LDFLAGS) $(rip98d_OBJECTS) $(rip98d_LDADD) $(LIBS)
+
+ttylinkd: $(ttylinkd_OBJECTS) $(ttylinkd_DEPENDENCIES)
+ @rm -f ttylinkd
+ $(LINK) $(ttylinkd_LDFLAGS) $(ttylinkd_OBJECTS) $(ttylinkd_LDADD) $(LIBS)
+
+install-man5:
+ $(mkinstalldirs) $(DESTDIR)$(man5dir)
+ @list='$(man5_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst; \
+ done
+
+uninstall-man5:
+ @list='$(man5_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man5dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man5dir)/$$inst; \
+ done
+
+install-man8:
+ $(mkinstalldirs) $(DESTDIR)$(man8dir)
+ @list='$(man8_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst; \
+ done
+
+uninstall-man8:
+ @list='$(man8_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man8dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man8dir)/$$inst; \
+ done
+install-man: $(MANS)
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-man5 install-man8
+uninstall-man:
+ @$(NORMAL_UNINSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-man5 uninstall-man8
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = tcpip
+
+distdir: $(DISTFILES)
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+rip98d.o: rip98d.c ../config.h ../pathnames.h rip98d.h
+rip98r.o: rip98r.c rip98d.h
+rip98t.o: rip98t.c rip98d.h
+ttylinkd.o: ttylinkd.c ../pathnames.h
+
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am: install-sbinPROGRAMS
+install-exec: install-exec-am
+
+install-data-am: install-man
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-sbinPROGRAMS uninstall-man
+uninstall: uninstall-am
+all-am: Makefile $(PROGRAMS) $(MANS)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(sbindir) $(DESTDIR)$(mandir)/man5 \
+ $(DESTDIR)$(mandir)/man8
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am: mostlyclean-sbinPROGRAMS mostlyclean-compile \
+ mostlyclean-tags mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-sbinPROGRAMS clean-compile clean-tags clean-generic \
+ mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-sbinPROGRAMS distclean-compile distclean-tags \
+ distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-sbinPROGRAMS \
+ maintainer-clean-compile maintainer-clean-tags \
+ maintainer-clean-generic distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS \
+clean-sbinPROGRAMS maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \
+install-sbinPROGRAMS mostlyclean-compile distclean-compile \
+clean-compile maintainer-clean-compile install-man5 uninstall-man5 \
+install-man8 uninstall-man8 install-man uninstall-man tags \
+mostlyclean-tags distclean-tags clean-tags maintainer-clean-tags \
+distdir info-am info dvi-am dvi check check-am installcheck-am \
+installcheck install-exec-am install-exec install-data-am install-data \
+install-am install uninstall-am uninstall all-redirect all-am all \
+installdirs mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+installconf:
+ $(mkinstalldirs) $(DESTDIR)$(etcdir)
+ @list='$(etcfiles)'; for p in $$list; do \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(etcdir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(etcdir)/$$p; \
+ done
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/tcpip/rip98.conf b/tcpip/rip98.conf
new file mode 100644
index 0000000..3574d56
--- /dev/null
+++ b/tcpip/rip98.conf
@@ -0,0 +1 @@
+44.131.4.7
diff --git a/tcpip/rip98.conf.5 b/tcpip/rip98.conf.5
new file mode 100644
index 0000000..7e2d3a4
--- /dev/null
+++ b/tcpip/rip98.conf.5
@@ -0,0 +1,19 @@
+.TH RIP98.CONF 5 "2 August 1996" Linux "Linux Programmer's Manual"
+.SH NAME
+rip98.conf \- control RIP98 routing messages.
+.SH DESCRIPTION
+.LP
+.B Rip98.conf
+controls the transmission of RIP98 routing messages. RIP98 is not a
+broadcast protocol but instead is transmitted individually to each other
+known RIP98 capable system. The list of these systems is kept in the RIP98
+configuration file as a list of host names or IP numbers that are to be
+transmitted to, and messages received from.
+.SH FILES
+.LP
+/etc/ax25/rip98.conf
+.SH "SEE ALSO"
+.BR arp (8),
+.BR ifconfig (8),
+.BR rip98d (8),
+.BR route (8).
diff --git a/tcpip/rip98d.8 b/tcpip/rip98d.8
new file mode 100644
index 0000000..0ee12dc
--- /dev/null
+++ b/tcpip/rip98d.8
@@ -0,0 +1,59 @@
+.TH RIP98D 8 "20 August 1996" Linux "Linux System Managers Manual"
+.SH NAME
+rip98d \- Send and receive RIP98 routing messages
+.SH SYNOPSIS
+.B rip98d [-d] [-l] [-r] [-t interval] [-v]
+.SH DESCRIPTION
+.LP
+The RIP98 routing protocol was devised by John Wiseman G8BPQ as an
+alternative to both traditional RIP and RSPF for use in an RF environment.
+Its main advantage is that each routing element only takes six bytes and is
+therefore much more efficient in terms of bandwidth than other routing
+protocols.
+.LP
+In operation
+.B rip98d
+that each neighbour that also uses RIP98 must be listed. RIP98 is not a
+broadcast protocol and each neighbour is individually contacted. Any
+incoming RIP98 message is also validated against this list. The list of
+neighbours is held in /etc/ax25/rip98.conf, and each line is
+either the name or the dotted decimal IP address of the neighbour.
+.LP
+The time interval between RIP98 transmissions is set to one hour by default
+but other intervals can be set with the \-t option. The routes advertised
+and received can be restricted by the \-r option which only allows
+processing of ampr.org addresses. At present
+.B rip98d
+is under development and any feedback on its operation would be welcome.
+.SH OPTIONS
+.TP 16
+.BI \-d
+Set debugging on. Information is only output if the logging option is also
+enabled.
+.TP 16
+.BI \-l
+Enable logging to the system log, the default is off.
+.TP 16
+.BI \-r
+Restricts the transmitting and receiving of routes to ampr.org (44.x.x.x)
+addresses only.
+.TP 16
+.BI "\-t interval"
+The time interval between routing broadcasts, in minutes. The default is 60
+minutes.
+.TP 16
+.BI \-v
+Display the version.
+.SH FILES
+.nf
+/proc/net/route
+.br
+/etc/ax25/rip98.conf
+.fi
+.SH "SEE ALSO"
+.BR rip98.conf (5),
+.BR arp (8),
+.BR ifconfig (8),
+.BR route (8).
+.SH AUTHOR
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
diff --git a/tcpip/rip98d.c b/tcpip/rip98d.c
new file mode 100644
index 0000000..78851cd
--- /dev/null
+++ b/tcpip/rip98d.c
@@ -0,0 +1,360 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netax25/daemon.h>
+
+#include <config.h>
+
+#include "../pathnames.h"
+#include "rip98d.h"
+
+struct dest_struct dest_list[50];
+
+int dest_count = 0;
+
+int debug = FALSE;
+int restrict = FALSE;
+int logging = FALSE;
+
+struct route_struct *first_route = NULL;
+
+static struct mask_struct {
+ unsigned long int mask;
+ unsigned int bits;
+} mask_table[] = {
+ {0xFFFFFFFF, 32},
+ {0xFFFFFFFE, 31},
+ {0xFFFFFFFC, 30},
+ {0xFFFFFFF8, 29},
+ {0xFFFFFFF0, 28},
+ {0xFFFFFFE0, 27},
+ {0xFFFFFFC0, 26},
+ {0xFFFFFF80, 25},
+ {0xFFFFFF00, 24},
+ {0xFFFFFE00, 23},
+ {0xFFFFFC00, 22},
+ {0xFFFFF800, 21},
+ {0xFFFFF000, 20},
+ {0xFFFFE000, 19},
+ {0xFFFFC000, 18},
+ {0xFFFF8000, 17},
+ {0xFFFF0000, 16},
+ {0xFFFE0000, 15},
+ {0xFFFC0000, 14},
+ {0xFFF80000, 13},
+ {0xFFF00000, 12},
+ {0xFFE00000, 11},
+ {0xFFC00000, 10},
+ {0xFF800000, 9},
+ {0xFF000000, 8},
+ {0xFE000000, 7},
+ {0xFC000000, 6},
+ {0xF8000000, 5},
+ {0xF0000000, 4},
+ {0xE0000000, 3},
+ {0xC0000000, 2},
+ {0x80000000, 1},
+ {0x00000000, 0},
+};
+
+static void terminate(int sig)
+{
+ if (logging) {
+ syslog(LOG_INFO, "terminating on SIGTERM\n");
+ closelog();
+ }
+
+ exit(0);
+}
+
+unsigned int mask2bits(unsigned long int mask)
+{
+ struct mask_struct *t;
+
+ for (t = mask_table; t->mask != 0; t++)
+ if (mask == t->mask)
+ return t->bits;
+
+ return 0;
+}
+
+unsigned long int bits2mask(unsigned int bits)
+{
+ struct mask_struct *t;
+
+ for (t = mask_table; t->mask != 0; t++)
+ if (bits == t->bits)
+ return htonl(t->mask);
+
+ return 0;
+}
+
+static unsigned long int hex2intrev(char *buffer)
+{
+ unsigned long int result = 0L;
+
+ if (buffer[0] >= 'A')
+ result += (buffer[0] - 'A' + 10) * 16;
+ else
+ result += (buffer[0] - '0') * 16;
+
+ if (buffer[1] >= 'A')
+ result += buffer[1] - 'A' + 10;
+ else
+ result += buffer[1] - '0';
+
+ if (buffer[2] >= 'A')
+ result += (buffer[2] - 'A' + 10) * 16 * 16 * 16;
+ else
+ result += (buffer[2] - '0') * 16 * 16 * 16;
+
+ if (buffer[3] >= 'A')
+ result += (buffer[3] - 'A' + 10) * 16 * 16;
+ else
+ result += (buffer[3] - '0') * 16 * 16;
+
+ if (buffer[4] >= 'A')
+ result += (buffer[4] - 'A' + 10) * 16 * 16 * 16 * 16 * 16;
+ else
+ result += (buffer[4] - '0') * 16 * 16 * 16 * 16 * 16;
+
+ if (buffer[5] >= 'A')
+ result += (buffer[5] - 'A' + 10) * 16 * 16 * 16 * 16;
+ else
+ result += (buffer[5] - '0') * 16 * 16 * 16 * 16;
+
+ if (buffer[6] >= 'A')
+ result += (buffer[6] - 'A' + 10) * 16 * 16 * 16 * 16 * 16 * 16 * 16;
+ else
+ result += (buffer[6] - '0') * 16 * 16 * 16 * 16 * 16 * 16 * 16;
+
+ if (buffer[7] >= 'A')
+ result += (buffer[7] - 'A' + 10) * 16 * 16 * 16 * 16 * 16 * 16;
+ else
+ result += (buffer[7] - '0') * 16 * 16 * 16 * 16 * 16 * 16;
+
+ return result;
+}
+
+static int read_routes(void)
+{
+ struct route_struct *route, *temp;
+ char buffer[1023], iface[16], net_addr[64], gate_addr[64], mask_addr[64];
+ int n, iflags, refcnt, use, metric, mss, window;
+ struct in_addr address;
+ unsigned long int netmask;
+ unsigned long int network;
+ FILE *fp;
+
+ if (first_route != NULL) {
+ route = first_route;
+
+ while (route != NULL) {
+ temp = route->next;
+ free(route);
+ route = temp;
+ }
+
+ first_route = NULL;
+ }
+
+ if ((fp = fopen(PROC_IP_ROUTE_FILE, "r")) == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "error cannot open %s\n", PROC_IP_ROUTE_FILE);
+ return FALSE;
+ }
+
+ while (fgets(buffer, 1023, fp) != NULL) {
+ n = sscanf(buffer, "%s %s %s %X %d %d %d %s %d %d\n",
+ iface, net_addr, gate_addr, &iflags, &refcnt, &use,
+ &metric, mask_addr, &mss, &window);
+
+ if (n != 10)
+ continue;
+
+ address.s_addr = htonl(hex2intrev(net_addr));
+ netmask = mask2bits(hex2intrev(mask_addr));
+
+ network = inet_netof(address);
+
+ if (network == 0 || network == 127) {
+ if (debug && logging)
+ syslog(LOG_DEBUG, "rejecting route to %s/%ld - should not be propogated\n", inet_ntoa(address), netmask);
+ continue;
+ }
+
+ if (restrict) {
+ if (inet_netof(address) != 44) {
+ if (debug && logging)
+ syslog(LOG_DEBUG, "rejecting route to %s/%ld - not ampr.org\n", inet_ntoa(address), netmask);
+ continue;
+ }
+ }
+
+ if ((route = malloc(sizeof(struct route_struct))) == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "out of memory !\n");
+ return FALSE;
+ }
+
+ route->addr = address;
+ route->bits = netmask;
+ route->metric = metric;
+ route->action = (iflags & RTF_DYNAMIC) ? ORIG_ROUTE : FIXED_ROUTE;
+
+ route->next = first_route;
+ first_route = route;
+ }
+
+ fclose(fp);
+
+ return TRUE;
+}
+
+static int load_dests(void)
+{
+ struct hostent *host;
+ char buffer[255], *s;
+ FILE *fp;
+
+ if ((fp = fopen(CONF_RIP98D_FILE, "r")) == NULL) {
+ fprintf(stderr, "rip98d: cannot open config file\n");
+ return FALSE;
+ }
+
+ while (fgets(buffer, 255, fp) != NULL) {
+ if ((s = strchr(buffer, '\n')) != NULL) *s = '\0';
+
+ if ((host = gethostbyname(buffer)) == NULL) {
+ fprintf(stderr, "rip98d: cannot resolve name %s\n", buffer);
+ fclose(fp);
+ return FALSE;
+ }
+
+ bcopy(host->h_addr, (char *)&dest_list[dest_count].dest_addr, host->h_length);
+ dest_count++;
+ }
+
+ fclose(fp);
+
+ if (dest_count == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+int main(int argc, char **argv)
+{
+ int s, i;
+ struct sockaddr_in loc_addr;
+ struct timeval timeout;
+ time_t timenow, timelast = 0;
+ int interval = 3600;
+ fd_set fdset;
+
+ if (!load_dests()) {
+ fprintf(stderr, "rip98d: no destination routers defined\n");
+ return 1;
+ }
+
+ while ((i = getopt(argc, argv, "dlrt:v")) != -1) {
+ switch (i) {
+ case 'd':
+ debug = TRUE;
+ break;
+ case 'l':
+ logging = TRUE;
+ break;
+ case 'r':
+ restrict = TRUE;
+ break;
+ case 't':
+ interval = atoi(optarg) * 60;
+ if (interval < 60 || interval > 7200) {
+ fprintf(stderr, "rip98d: invalid time interval\n");
+ return 1;
+ }
+ break;
+ case 'v':
+ printf("rip98d: %s\n", VERSION);
+ return 0;
+ case ':':
+ fprintf(stderr, "rip98d: invalid time interval\n");
+ return 1;
+ case '?':
+ fprintf(stderr, "usage: rip98d [-d] [-l] [-r] [-t interval] [-v]\n");
+ return 1;
+ }
+ }
+
+ signal(SIGTERM, terminate);
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("rip98d: socket");
+ return 1;
+ }
+
+ bzero((char *)&loc_addr, sizeof(loc_addr));
+ loc_addr.sin_family = AF_INET;
+ loc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ loc_addr.sin_port = htons(RIP_PORT);
+
+ if (bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
+ perror("rip98d: bind");
+ close(s);
+ return 1;
+ }
+
+ if (!daemon_start(FALSE)) {
+ fprintf(stderr, "rip98d: cannot become a daemon\n");
+ close(s);
+ return 1;
+ }
+
+ if (logging) {
+ openlog("rip98d", LOG_PID, LOG_DAEMON);
+ syslog(LOG_INFO, "starting");
+ }
+
+ for (;;) {
+ FD_ZERO(&fdset);
+ FD_SET(s, &fdset);
+
+ timeout.tv_sec = 60;
+ timeout.tv_usec = 0;
+
+ select(s + 1, &fdset, NULL, NULL, &timeout);
+
+ if (!read_routes()) {
+ if (logging)
+ closelog();
+ return 1;
+ }
+
+ if (FD_ISSET(s, &fdset))
+ receive_routes(s);
+
+ time(&timenow);
+
+ if ((timenow - timelast) > interval) {
+ timelast = timenow;
+ if (first_route != NULL)
+ transmit_routes(s);
+ }
+ }
+}
diff --git a/tcpip/rip98d.h b/tcpip/rip98d.h
new file mode 100644
index 0000000..fb00a7d
--- /dev/null
+++ b/tcpip/rip98d.h
@@ -0,0 +1,58 @@
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define RIP_PORT 520
+
+#define RIPCMD_REQUEST 1
+#define RIPCMD_RESPONSE 2
+
+#define RIP98_INFINITY 16
+
+#define RIP_VERSION_98 98
+
+#define RIP_AF_INET 2
+
+#define RIP98_HEADER 4
+#define RIP98_ENTRY 6
+
+#define RIP98_MAX_FRAME 30
+
+struct route_struct {
+ struct route_struct *next;
+ struct in_addr addr;
+ int bits;
+ int metric;
+#define ORIG_ROUTE 0
+#define FIXED_ROUTE 1
+#define NEW_ROUTE 2
+#define DEL_ROUTE 3
+ int action;
+};
+
+extern struct route_struct *first_route;
+
+struct dest_struct {
+ struct in_addr dest_addr;
+};
+
+extern struct dest_struct dest_list[];
+
+extern int dest_count;
+
+extern int debug;
+extern int restrict;
+extern int logging;
+
+/* In rip98d.c */
+extern unsigned int mask2bits(unsigned long int);
+extern unsigned long int bits2mask(unsigned int);
+
+/* In rip98t.c */
+extern void transmit_routes(int);
+
+/* In rip98r.c */
+extern void receive_routes(int);
diff --git a/tcpip/rip98r.c b/tcpip/rip98r.c
new file mode 100644
index 0000000..765e786
--- /dev/null
+++ b/tcpip/rip98r.c
@@ -0,0 +1,255 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/route.h>
+
+
+#include "rip98d.h"
+
+#define UNMATCH_ROUTE 0
+#define NO_ROUTE 1
+#define REPLACE_ROUTE 2
+#define ADDITIONAL_ROUTE 3
+
+static int cmp_route(struct route_struct *route, struct in_addr addr, int bits, int metric)
+{
+ unsigned long int old_mask, new_mask;
+ unsigned long int old_addr, new_addr;
+
+ old_mask = ntohl(bits2mask(route->bits));
+ new_mask = ntohl(bits2mask(bits));
+
+ old_addr = route->addr.s_addr;
+ new_addr = addr.s_addr;
+
+ if (bits < route->bits) {
+ new_addr = ntohl(new_addr) & new_mask;
+ old_addr = ntohl(old_addr) & new_mask;
+ } else {
+ new_addr = ntohl(new_addr) & old_mask;
+ old_addr = ntohl(old_addr) & old_mask;
+ }
+
+ if (route->action == DEL_ROUTE || route->action == NEW_ROUTE)
+ return UNMATCH_ROUTE;
+
+ if (old_addr != new_addr)
+ return UNMATCH_ROUTE;
+
+ if (route->metric <= metric)
+ return NO_ROUTE;
+
+ if (debug && logging) {
+ syslog(LOG_DEBUG, "comparing old: %s/%d to\n", inet_ntoa(route->addr), route->bits);
+ syslog(LOG_DEBUG, " new: %s/%d\n", inet_ntoa(addr), bits);
+ }
+
+ if (route->bits <= bits) {
+ if (route->action == ORIG_ROUTE) {
+ if (debug && logging)
+ syslog(LOG_DEBUG, " better route, replacing existing route\n");
+ return REPLACE_ROUTE;
+ } else {
+ if (debug && logging)
+ syslog(LOG_DEBUG, " better route, adding additional route\n");
+ return ADDITIONAL_ROUTE;
+ }
+ } else {
+ if (debug && logging)
+ syslog(LOG_DEBUG, " better route, adding additional route\n");
+ return ADDITIONAL_ROUTE;
+ }
+}
+
+void receive_routes(int s)
+{
+ unsigned char message[500], *p;
+ struct sockaddr_in rem_addr;
+ struct in_addr addr;
+ struct route_struct *route, *new;
+ struct sockaddr_in trg;
+ struct rtentry rt;
+ unsigned long int netmask;
+ unsigned long int network;
+ int bits, metric;
+ int size, found, matched;
+ int mess_len;
+ int i;
+
+ size = sizeof(struct sockaddr_in);
+
+ mess_len = recvfrom(s, message, 500, 0, (struct sockaddr *)&rem_addr, &size);
+
+ if (message[0] != RIPCMD_RESPONSE || message[1] != RIP_VERSION_98) {
+ if (debug && logging) {
+ syslog(LOG_DEBUG, "invalid RIP98 header received\n");
+ syslog(LOG_DEBUG, " cmd: %d vers: %d\n", message[0], message[1]);
+ }
+ return;
+ }
+
+ for (found = FALSE, i = 0; i < dest_count; i++) {
+ if (rem_addr.sin_addr.s_addr == dest_list[i].dest_addr.s_addr) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (debug && logging)
+ syslog(LOG_DEBUG, "RIP98 message from unknown address %s\n", inet_ntoa(rem_addr.sin_addr));
+ return;
+ }
+
+ if (debug && logging)
+ syslog(LOG_DEBUG, "RIP98 message received from %s\n", inet_ntoa(rem_addr.sin_addr));
+
+ for (p = message + RIP98_HEADER; p < message + mess_len; p += RIP98_ENTRY) {
+ bcopy((char *)p, (char *)&addr, sizeof(addr));
+ bits = p[4];
+ metric = p[5];
+
+ network = inet_netof(addr);
+
+ if (network == 0 || network == 127) {
+ if (debug && logging)
+ syslog(LOG_DEBUG, " route to %s/%d metric %d - rejected\n", inet_ntoa(addr), bits, metric);
+ continue;
+ }
+
+ if (restrict) {
+ if (network != 44) {
+ if (debug && logging)
+ syslog(LOG_DEBUG, " route to %s/%d metric %d - rejected\n", inet_ntoa(addr), bits, metric);
+ continue;
+ }
+ }
+
+ if (debug && logging)
+ syslog(LOG_DEBUG, " route to %s/%d metric %d\n", inet_ntoa(addr), bits, metric);
+
+ metric++;
+
+ if (metric > RIP98_INFINITY)
+ metric = RIP98_INFINITY;
+
+ found = FALSE;
+ matched = FALSE;
+
+ for (route = first_route; route != NULL; route = route->next) {
+
+ switch (cmp_route(route, addr, bits, metric)) {
+
+ case NO_ROUTE:
+ matched = TRUE;
+ break;
+
+ case REPLACE_ROUTE:
+ route->action = DEL_ROUTE;
+
+ case ADDITIONAL_ROUTE:
+ if (!found) {
+ if ((new = malloc(sizeof(struct route_struct))) == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "out of memory\n");
+ return;
+ }
+
+ new->addr = addr;
+ new->bits = bits;
+ new->metric = metric;
+ new->action = NEW_ROUTE;
+
+ new->next = first_route;
+ first_route = new;
+
+ found = TRUE;
+ }
+
+ matched = TRUE;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!matched) {
+ if ((new = malloc(sizeof(struct route_struct))) == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "out of memory\n");
+ return;
+ }
+
+ new->addr = addr;
+ new->bits = bits;
+ new->metric = metric;
+ new->action = NEW_ROUTE;
+
+ new->next = first_route;
+ first_route = new;
+ }
+ }
+
+ for (route = first_route; route != NULL; route = route->next) {
+ if (route->action == DEL_ROUTE) {
+ bzero((char *)&rt, sizeof(rt));
+
+ trg.sin_family = AF_INET;
+ trg.sin_addr = route->addr;
+ trg.sin_port = 0;
+ bcopy((char *)&trg, (char *)&rt.rt_dst, sizeof(struct sockaddr));
+
+ if (ioctl(s, SIOCDELRT, &rt) < 0) {
+ if (logging)
+ syslog(LOG_ERR, "SIOCDELRT: %m");
+ }
+ }
+ }
+
+ for (route = first_route; route != NULL; route = route->next) {
+ if (route->action == NEW_ROUTE) {
+ bzero((char *)&rt, sizeof(rt));
+
+ trg.sin_family = AF_INET;
+ trg.sin_addr = route->addr;
+ trg.sin_port = 0;
+ bcopy((char *)&trg, (char *)&rt.rt_dst, sizeof(struct sockaddr));
+
+ rt.rt_flags = RTF_UP | RTF_GATEWAY | RTF_DYNAMIC;
+
+ if (route->bits == 32) {
+ rt.rt_flags |= RTF_HOST;
+ } else {
+ netmask = bits2mask(route->bits);
+
+ trg.sin_family = AF_INET;
+ bcopy((char *)&netmask, (char *)&trg.sin_addr, sizeof(struct in_addr));
+ trg.sin_port = 0;
+ bcopy((char *)&trg, (char *)&rt.rt_genmask, sizeof(struct sockaddr));
+ }
+
+ rt.rt_metric = route->metric;
+
+ trg.sin_family = AF_INET;
+ trg.sin_addr = rem_addr.sin_addr;
+ trg.sin_port = 0;
+ bcopy((char *)&trg, (char *)&rt.rt_gateway, sizeof(struct sockaddr));
+
+ if (ioctl(s, SIOCADDRT, &rt) < 0) {
+ if (logging)
+ syslog(LOG_ERR, "SIOCADDRT: %m");
+ }
+ }
+ }
+}
diff --git a/tcpip/rip98t.c b/tcpip/rip98t.c
new file mode 100644
index 0000000..c227fc4
--- /dev/null
+++ b/tcpip/rip98t.c
@@ -0,0 +1,76 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include "rip98d.h"
+
+static int build_header(unsigned char *message)
+{
+ message[0] = RIPCMD_RESPONSE;
+ message[1] = RIP_VERSION_98;
+
+ message[2] = 0;
+ message[3] = 0;
+
+ return RIP98_HEADER;
+}
+
+
+void transmit_routes(int s)
+{
+ unsigned char message[500];
+ struct route_struct *route;
+ struct sockaddr_in rem_addr;
+ int mess_len;
+ int size;
+ int i;
+
+ size = sizeof(struct sockaddr_in);
+
+ for (i = 0; i < dest_count; i++) {
+
+ if (debug && logging)
+ syslog(LOG_DEBUG, "transmit_routes: sending routing message to %s\n", inet_ntoa(dest_list[i].dest_addr));
+
+ bzero((char *)&rem_addr, sizeof(rem_addr));
+ rem_addr.sin_family = AF_INET;
+ rem_addr.sin_addr = dest_list[i].dest_addr;
+ rem_addr.sin_port = htons(RIP_PORT);
+
+ route = first_route;
+
+ while (route != NULL) {
+
+ mess_len = build_header(message);
+
+ while (mess_len < 184 && route != NULL) {
+ if (route->action != DEL_ROUTE) {
+ bcopy((char *)&route->addr, message + mess_len + 0, sizeof(struct in_addr));
+ message[mess_len + 4] = route->bits;
+ message[mess_len + 5] = route->metric;
+
+ mess_len += RIP98_ENTRY;
+ }
+
+ route = route->next;
+ }
+
+ if (mess_len > RIP98_HEADER) {
+ if (sendto(s, message, mess_len, 0, (struct sockaddr *)&rem_addr, size) < 0) {
+ if (logging)
+ syslog(LOG_ERR, "sendto: %m");
+ }
+ }
+ }
+ }
+}
diff --git a/tcpip/ttylinkd.8 b/tcpip/ttylinkd.8
new file mode 100644
index 0000000..7b2379f
--- /dev/null
+++ b/tcpip/ttylinkd.8
@@ -0,0 +1,53 @@
+.TH TTYLINKD 8 "5 March 1997" Linux "Linux System Managers Manual"
+.SH NAME
+ttylinkd \- TTYlink daemon for AX.25, NET/ROM, ROSE and IP.
+.SH SYNOPSIS
+.B ttylinkd [-hv] [-c callsign] [-f file]
+.SH DESCRIPTION
+.LP
+.B ttylinkd
+is a simple daemon that allows incoming ttylink calls to be routed through
+to Linux's normal talkd(8)
+system and provides a pipe between the two.
+.LP
+.B ttylinkd
+is usually setup to be spawned from ax25d(8) or inetd(8)
+but can, since version 0.03, be used on the command line, although
+talk(1) is a much better idea for interactive users.
+.LP
+Use splitscreen(1)
+for outgoing ttylink sessions.
+.SH OPTIONS
+.TP 10
+.BI \-v
+Display the version.
+.TP 10
+.BI \-h
+Display some help and information on it.
+.TP 10
+.BI "\-c callsign"
+Disable callsign checking and use the given callsign for a name for this
+end of the link.
+.TP 10
+.BI "\-f file"
+Use <file> for the configuration file, instead of the default
+/etc/ax25/ttylinkd.conf
+.SH FILES
+.LP
+/etc/ax25/ttylinkd.conf the configuration file for ttylinkd
+.SH "SEE ALSO"
+.BR talk (1),
+.BR splitscreen (1),
+.BR ttylinkd.conf (5),
+.BR ax25d (8),
+.BR inetd (8),
+.BR talkd (8).
+.SH BUGS
+.LP
+There is still some need for some checking of who is still logged on.
+.LP
+The daemon may hang if you disconnect from it strangely.
+.LP
+There is no real way of working out what is going on with talkd.
+.SH AUTHOR
+Craig Small VK2XLZ <csmall@gonzo.triode.net.au>
diff --git a/tcpip/ttylinkd.c b/tcpip/ttylinkd.c
new file mode 100644
index 0000000..e185c7e
--- /dev/null
+++ b/tcpip/ttylinkd.c
@@ -0,0 +1,719 @@
+/*
+ * ttylinkd: A ttylink daemon using the ntalkd protocol.
+ * Copyright (C) 1996,1197 Craig Small (csmall@gonzo.triode.net.au)
+ *
+ * This program is free software ; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have recieved a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge. MA 02139, USA.
+ */
+ /*
+ * Versions:
+ * 13/01/96 cs Original Version
+ * 29/01/96 cs Added AX.25 proper support, thanks Tomi OH2BNS!!
+ * 27/01/97 cs Added support for stdin/stdout.
+ * 04/03/97 cs Added config file, no more defines.
+ * 09/03/97 tjd Enhanced to allow specifying user@hostname.wherever.
+ * rather than be confined to localhost.
+ */
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define osockaddr sockaddr
+#include <protocols/talkd.h>
+
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+
+#include "../pathnames.h"
+
+
+static char version[] = "ttylink daemon (Version 0.05, 9 March 1997) ready.\n";
+
+#define SYSOP_USER "root"
+
+/* TTY control characters from the sysop's talk screen */
+char erasec;
+char killc;
+char werasec;
+
+/* Users address family */
+int userfamily;
+
+#define ADDR_SIZE 256
+char sysop_addr[ADDR_SIZE];
+char *sysop_user=NULL, *sysop_host=NULL;
+char config_file[128];
+
+int send_control(int skt, struct in_addr addr, CTL_MSG msg, CTL_RESPONSE *resp);
+void alarm_handle(int i);
+void do_talk(int skt);
+void read_config_file(int dummy);
+
+/*static char *Commands[] = {
+ "leaving invitation",
+ "look-up",
+ "delete",
+ "announce" };
+*/
+static char *Responses[] = {
+ "success",
+ "sysop not logged on",
+ "failed for unknown reason",
+ "sysop's machine unknown",
+ "permission denied",
+ "unknown request",
+ "bad version",
+ "bad address",
+ "bad control address" };
+
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in sin, ctl_sin;
+ struct in_addr my_addr, rem_addr;
+ int ctl_skt, skt, new_skt;
+ int length;
+ CTL_RESPONSE resp;
+ CTL_MSG msg;
+ struct protoent *ppe;
+ struct hostent *phe, *rhe=NULL;
+ char hostname[256];
+ int local_id, remote_id;
+ char buf[256];
+ char user[NAME_SIZE];
+ struct sockaddr sa;
+ int sa_len, i;
+ struct sockaddr_in *peer_sin=NULL;
+ struct sockaddr_ax25 *peer_sax;
+ struct sockaddr_rose *peer_srose;
+
+ /* Open up the system logger */
+ openlog(argv[0], LOG_PID, LOG_DAEMON);
+
+ write(STDOUT_FILENO, version, strlen(version));
+
+ /* Work out who is calling us */
+ userfamily = AF_UNSPEC;
+ bzero(user, NAME_SIZE);
+ strcpy(sysop_addr, SYSOP_USER);
+ strcpy(config_file, CONF_TTYLINKD_FILE);
+ for(i=1 ; i < argc ; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'v':
+ return 0;
+ break;
+ case 'h':
+ printf("ttylinkd comes with ABSOLUTELY NO WARRANY, see the file COPYING\n");
+ printf("This is free software, and you are welcome to redistribute it\n");
+ printf("under the terms of the GNU General Public License.\n\n");
+ printf("Usage: %s [-hv] [-c <callsign>] [-f <config_file>]\n", argv[0]);
+ return 0;
+ break;
+ case 'f':
+ if (i+2 > argc)
+ {
+ printf("The -f flag needs a parameter.\n");
+ return 0;
+ break;
+ }
+ strncpy(config_file, argv[++i], 127);
+ break;
+ case 'c':
+ if ( i+2 > argc)
+ {
+ printf("The -c flag need a parameter.\n");
+ return 0;
+ break;
+ }
+ strncpy(user, argv[++i], NAME_SIZE);
+ break;
+ default:
+ fprintf(stderr, "%s: Unknown flag, type %s -h for help\n", argv[0], argv[0]);
+ return -1;
+ break;
+ } /*switch */
+ } /* - */
+ } /* for */
+ if (user[0] == '\0')
+ {
+ sa_len = sizeof(sa);
+ if (getpeername(STDOUT_FILENO, &sa, &sa_len) < 0)
+ {
+ fprintf(stderr, "%s: getpeername() failed, you must specify a callsign in stdin mode.\n", argv[0]);
+ syslog(LOG_CRIT | LOG_DAEMON, "main(): getpeername() failed.");
+ return 0;
+ } else {
+ userfamily = sa.sa_family;
+ switch(sa.sa_family) {
+ case AF_INET:
+ peer_sin = (struct sockaddr_in*)&sa;
+ write(STDOUT_FILENO, buf, strlen(buf));
+ sprintf(buf, "Please enter your callsign: ");
+ write(STDOUT_FILENO, buf, strlen(buf));
+ fflush(stdout);
+ if (fgets(user, NAME_SIZE-1, stdin) == NULL)
+ return 0;
+ for (i = 0; user[i] != '\0' && user[i] != '\n' && user[i] != '\r'; i++)
+ ;
+ user[i] = '\0';
+ if (strlen(user) < 1)
+ return 0;
+ userfamily = AF_INET;
+ break;
+ case AF_AX25:
+ case AF_NETROM:
+ peer_sax = (struct sockaddr_ax25*)&sa;
+ for(i=0 ; i < 6 ; i++)
+ {
+ user[i] = tolower(((peer_sax->sax25_call.ax25_call[i]) >>1)&0x7f);
+ if (user[i] == ' ')
+ break;
+ }
+ user[i] = '\0';
+ break;
+ case AF_ROSE:
+ peer_srose = (struct sockaddr_rose*)&sa;
+ for(i=0 ; i < 6 ; i++)
+ {
+ user[i] = tolower(((peer_srose->srose_call.ax25_call[i]) >>1)&0x7f);
+ if (user[i] == ' ')
+ break;
+ }
+ user[i] = '\0';
+ break;
+ default:
+ syslog(LOG_DAEMON | LOG_CRIT, "Unsupported address family.");
+ exit(1);
+ }
+
+ }
+ } /* argc */
+
+ /* Read the configuration file to find the System Operator. */
+ read_config_file(0);
+ /* ... and parse it into user@host */
+ sysop_user=strtok(sysop_addr,"@");
+ sysop_host=strtok(NULL,"@");
+
+ if ((ppe = getprotobyname("udp")) == 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "Cannot find udp protocol entry.");
+ exit(1);
+ }
+
+ /* Obtain our local hostname */
+ gethostname(hostname,256);
+
+ /* Get the remote address, or use ours if remote isn't specified */
+ if (sysop_host)
+ {
+ if ((rhe = gethostbyname(sysop_host)) == NULL)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): gethostbyname failed.");
+ exit(1);
+ }
+ }
+ else
+ {
+ if ((rhe = gethostbyname(hostname)) == NULL)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): gethostbyname failed.");
+ exit(1);
+ }
+ }
+ bcopy(rhe->h_addr, (char*)&rem_addr, rhe->h_length);
+
+ /* Get our local address */
+ if ((phe = gethostbyname(hostname)) == NULL)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): gethostbyname failed.");
+ exit(1);
+ }
+ bcopy(phe->h_addr, (char*)&my_addr, phe->h_length);
+
+ /* Create local data socket */
+ bzero((char*)&sin, sizeof(sin));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(0);
+ bcopy(phe->h_addr, (char*)&sin.sin_addr, phe->h_length);
+
+ if ((skt = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): socket() failed.");
+ exit(1);
+ }
+
+ if (bind( skt, (struct sockaddr*)&sin, sizeof(sin)) != 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): bind() failed.");
+ exit(1);
+ }
+
+ length = sizeof(sin);
+ if (getsockname(skt, (struct sockaddr*)&sin, &length) < 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): getsockname() failed.");
+ exit(1);
+ }
+
+ /* Create local control socket */
+ bzero((char*)&ctl_sin, sizeof(ctl_sin));
+
+ ctl_sin.sin_family = AF_INET;
+ bcopy(phe->h_addr, (char*)&ctl_sin.sin_addr, phe->h_length);
+ ctl_sin.sin_port = htons(0);
+
+ if ((ctl_skt = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): socket() while attempting to create control socket.");
+ close(skt);
+ exit(1);
+ }
+
+ if (bind(ctl_skt, (struct sockaddr*)&ctl_sin, sizeof(ctl_sin)) != 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): Error when trying to bind() control socket.");
+ close(skt);
+ exit(1);
+ }
+
+ length = sizeof(ctl_sin);
+ if (getsockname(ctl_skt, (struct sockaddr*)&ctl_sin, &length) < 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT,"main(): Error when trying to getsockname() for control socket.");
+ close(skt);
+ close(ctl_skt);
+ exit(1);
+ }
+
+ /* Start talking to the talk daemon */
+ bzero((char*)&msg, sizeof(msg));
+ msg.vers = TALK_VERSION;
+ msg.id_num = htonl(0);
+ msg.addr.sa_family = ntohs(AF_INET);
+ msg.ctl_addr = *(struct sockaddr*)&ctl_sin;
+ msg.ctl_addr = *(struct sockaddr*)&ctl_sin;
+ msg.ctl_addr.sa_family = ntohs(AF_INET);
+ msg.pid = htonl(getpid());
+ strncpy(msg.l_name, user, NAME_SIZE-1);
+ strncpy(msg.r_name, sysop_user, NAME_SIZE-1);
+
+
+ /* Now look for an invite */
+ msg.type = LOOK_UP;
+ (void) send_control(ctl_skt, rem_addr, msg, &resp);
+
+ /* The person not there? Send an announce and wake him up */
+ msg.type = ANNOUNCE;
+ msg.addr = *(struct sockaddr*)&sin;
+ msg.addr.sa_family = htons(AF_INET);
+ msg.id_num = -1;
+ i = send_control(ctl_skt, rem_addr, msg, &resp);
+ if ( i != SUCCESS)
+ {
+ if (i > BADCTLADDR)
+ printf("Cannot talk to sysop errno=%d.\n",i);
+ else
+ printf("Cannot talk to sysop, reason: %s.\n",Responses[i]);
+
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ /* Update the ID number so that we both know what we're talking about. */
+ remote_id = resp.id_num;
+
+ /* Get the TCP port ready for a connection */
+ if (listen(skt, 5) != 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): Error when trying to listen() on socket.");
+ exit(1);
+ }
+
+ /* Now we have to make an invitation for the other user */
+ msg.type = LEAVE_INVITE;
+ if (send_control(ctl_skt, my_addr, msg, &resp) != SUCCESS)
+ {
+ printf("Problem with leaving an invitation\n");
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): Cannot leave invititation.");
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ local_id = resp.id_num;
+
+ sprintf(buf, "Paging sysop.\n");
+ write(STDOUT_FILENO, buf, strlen(buf));
+
+ /* Wait for the sysop to connect to us */
+ signal(SIGALRM, alarm_handle);
+ alarm(30);
+
+ if ((new_skt = accept(skt, 0, 0)) < 0)
+ {
+ if (errno == EINTR)
+ {
+ /* Delete invitations from both daemons */
+ msg.type = DELETE;
+ msg.id_num = htonl(local_id);
+ send_control(ctl_skt, my_addr, msg, &resp);
+ msg.id_num = htonl(remote_id);
+ send_control(ctl_skt, rem_addr, msg, &resp);
+
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ syslog(LOG_DAEMON | LOG_WARNING, "main(): accept() failed. (%m)");
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ close(skt);
+ skt = new_skt;
+
+ /* Delete invitations from both daemons */
+ msg.type = DELETE;
+ msg.id_num = htonl(local_id);
+ send_control(ctl_skt, my_addr, msg, &resp);
+ msg.id_num = htonl(remote_id);
+ send_control(ctl_skt, rem_addr, msg, &resp);
+
+ sprintf(buf, "Sysop has responded.\n");
+ write(STDOUT_FILENO, buf, strlen(buf));
+
+ /*
+ * A little thing that they don't mention anywhere is the fact that the
+ * first three characters on a connection are used to work out to erase,
+ * kill and word erase characters. Nice to know eh?
+ */
+ /* We send ours first, but then again we get data in cooked form, so
+ * we don't use this. */
+ buf[0] = '\0';
+ buf[1] = '\0';
+ buf[2] = '\0';
+ if (write(skt, buf, 3) != 3)
+ {
+ printf("Connnection lost\n");
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ /* Get their character stuff */
+ if (read(skt, buf, 3) != 3)
+ {
+ printf("Connection lost\n");
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ erasec = buf[0];
+ killc = buf[1];
+ werasec = buf[2];
+
+ /* Tell the sysop who this person is */
+ if (sa.sa_family == AF_AX25)
+ {
+ sprintf(buf, "Incoming ttylink from %s.\n", user);
+ write(skt, buf, strlen(buf));
+ }
+ if (sa.sa_family == AF_INET)
+ {
+ sprintf(buf, "Incoming ttylink from %s@%s.\n", user, inet_ntoa(peer_sin->sin_addr));
+ write(skt, buf, strlen(buf));
+ }
+
+
+ do_talk(skt);
+
+ close(skt);
+ close(ctl_skt);
+ return 0;
+}
+
+/*
+ * Used to send control messages to our friendly local talk daemon
+ */
+int send_control(int skt, struct in_addr addr, CTL_MSG msg, CTL_RESPONSE *resp)
+{
+ fd_set fdvar;
+ struct timeval timeout;
+ struct sockaddr_in sin;
+ static int talk_port = 0;
+ struct servent *pse;
+
+ /* Look up talk port once only */
+ if (talk_port == 0)
+ {
+ if ((pse = getservbyname("ntalk", "udp")) == NULL)
+ {
+ perror("getservbyname, assuming 518");
+ talk_port = 518;
+ } else {
+ talk_port = pse->s_port;
+ }
+ }
+
+ /* Create the socket address */
+ bzero((char*)&sin, sizeof(sin));
+ sin.sin_addr = addr;
+ sin.sin_family = AF_INET;
+ sin.sin_port = talk_port;
+
+ if (sendto(skt, (char*)&msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) != sizeof(msg))
+ {
+ syslog(LOG_DAEMON | LOG_ERR, "send_control(): sendto failed (%m).");
+ return -1;
+ }
+
+ /* Wait for reply */
+ FD_ZERO(&fdvar);
+ FD_SET(skt, &fdvar);
+ timeout.tv_sec = RING_WAIT;
+ timeout.tv_usec = 0;
+
+ if (select(32, &fdvar, NULL, NULL, &timeout) < 0)
+ syslog(LOG_DAEMON | LOG_ERR, "send_control(): select failed. (%m)");
+
+ /*
+ * The server is ignoring us, see ya later
+ */
+ if (!(FD_ISSET(skt, &fdvar)))
+ {
+ printf("Talk server not responding after %d seconds, aborting.\n", RING_WAIT);
+ return -1;
+ }
+
+ /* Get the message */
+ if(recv(skt, resp, sizeof(resp),0) ==0)
+ syslog(LOG_DAEMON | LOG_ERR, "send_control(): recv failed. (%m)");
+
+ return resp->answer;
+}
+
+/* Used to process the data from the sysop */
+int send_sysop_data(char *buf, int len)
+{
+ static char outbuf[82];
+ static char *bptr = outbuf;
+ int i;
+
+ for(i = 0; i < len; i++)
+ {
+ /* Check for erase character */
+ if (buf[i] == erasec)
+ {
+ if (bptr > outbuf)
+ bptr--;
+ continue;
+ }
+
+ /* Check for kill character */
+ if (buf[i] == killc)
+ {
+ bptr = outbuf;
+ continue;
+ }
+
+ /* Check for word-erase character */
+ if (buf[i] == werasec)
+ {
+ while( (bptr > outbuf) && (*bptr != ' ') )
+ bptr--;
+ continue;
+ }
+
+ /* Check for newline character */
+ if (buf[i] == '\n')
+ {
+ if ( (userfamily == AF_AX25) || (userfamily == AF_NETROM) || (userfamily == AF_ROSE) )
+ {
+ *bptr = '\r';
+ bptr++;
+ } else {
+ *bptr = '\r';
+ bptr++;
+ *bptr = '\n';
+ bptr++;
+ }
+ } else {
+ *bptr = buf[i];
+ bptr++;
+ }
+
+ /* Check for carriage return, which means send it */
+ /* We also send if we have more than 80 characters */
+ if (buf[i] == '\n' || (bptr - outbuf) > 80 )
+ {
+ if (write(STDOUT_FILENO, outbuf, (bptr - outbuf) ) != (bptr - outbuf) )
+ {
+ return -1;
+ }
+ bptr = outbuf;
+ }
+ } /* for */
+ return 0;
+}
+
+/* Used to process the data from the user - len must not exceed 256 */
+int send_user_data(int skt, char *buf, int len)
+{
+ char outbuf[256];
+ char *bptr = outbuf;
+ int i;
+
+ for(i = 0; i < len; i++)
+ {
+ if (buf[i] == '\r')
+ {
+ if (userfamily == AF_INET)
+ {
+ *bptr = '\n';
+ bptr++;
+ /*
+ * check if this is a <CR><LF> or a <CR><NUL>
+ * and skip the <LF> or <NUL>.
+ */
+ if (buf[i + 1] == '\n' || buf[i + 1] == '\0')
+ {
+ i++;
+ }
+ } else
+ {
+ *bptr = '\n';
+ bptr++;
+ }
+ continue;
+ }
+ *bptr = buf[i];
+ bptr++;
+ } /* for */
+
+ if (write(skt, outbuf, bptr - outbuf) != bptr - outbuf)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+
+/* The main talking loop */
+void do_talk(int skt)
+{
+ fd_set fdvar;
+ char inbuf[256], outbuf[256];
+ struct timeval timeout;
+ int i;
+
+
+ while(1)
+ {
+ FD_ZERO(&fdvar);
+ FD_SET(skt, &fdvar);
+ FD_SET(STDIN_FILENO, &fdvar);
+
+ timeout.tv_sec = 30;
+ timeout.tv_usec = 0;
+ if (select(32, &fdvar, NULL, NULL, &timeout) == 0)
+ {
+ if (ioctl(STDIN_FILENO, FIONREAD, (struct sgttyb *)&i) < 0)
+ return;
+ if (ioctl(skt, FIONREAD, (struct sgttyb*)&i) < 0)
+ return;
+ }
+
+ if (FD_ISSET(skt, &fdvar))
+ {
+ if ((i = read(skt, inbuf, 256)) <= 0)
+ {
+ strcpy(inbuf,"Sysop closed connection.\n");
+ write(STDOUT_FILENO, inbuf, strlen(inbuf));
+ return;
+ }
+ if (send_sysop_data(inbuf, i) < 0)
+ {
+ strcpy(outbuf, "User closed connection.\n");
+ (void) write(skt, outbuf, strlen(outbuf));
+ return;
+ }
+ }
+ if (FD_ISSET(STDIN_FILENO, &fdvar))
+ {
+ if ((i = read(STDIN_FILENO, outbuf, 256)) <= 0)
+ {
+ strcpy(outbuf, "User closed connection.\n");
+ (void) write(skt, outbuf, strlen(outbuf));
+ return;
+ }
+ if (send_user_data(skt, outbuf, i) < 0)
+ {
+ strcpy(inbuf,"Sysop closed connection.\n");
+ write(STDOUT_FILENO, inbuf, strlen(inbuf));
+ return;
+ }
+ }
+ }
+}
+
+void alarm_handle(int i)
+{
+ char buf[256];
+
+ strcpy(buf, "Sysop not responding.\n");
+ write(STDOUT_FILENO, buf, strlen(buf));
+}
+
+void read_config_file(int dummy)
+{
+ char buf[128];
+ char param[20], value[108];
+ FILE *fp;
+
+ if ( (fp = fopen(config_file, "r")) == NULL) {
+ syslog(LOG_DAEMON | LOG_ERR, "Cannot find configuration file: %s (%m)\n",config_file);
+ return;
+ }
+ /* Reset any variables here */
+
+ while ( fgets(buf, 128, fp) != NULL) {
+ if ( buf[0] == '#')
+ continue;
+ if (sscanf(buf, "%[^=]%*c%[^\n]", param, value) == 2) {
+ /* A big wierdo switch */
+ if (strcasecmp(param, "sysop") == 0) {
+ strncpy(sysop_addr, value, ADDR_SIZE);
+ }
+ }
+ } /* while */
+ fclose(fp);
+ signal(SIGHUP, read_config_file);
+}
diff --git a/tcpip/ttylinkd.conf b/tcpip/ttylinkd.conf
new file mode 100644
index 0000000..ae65749
--- /dev/null
+++ b/tcpip/ttylinkd.conf
@@ -0,0 +1,10 @@
+# /etc/ax25/ttylinkd.conf : config file for ttylinkd(8)
+#
+# We just have one line, who gets the incoming ttylink calls.
+#
+# sysop=user@hostname.domain.name
+# or:
+# sysop=user
+#
+sysop=root
+
diff --git a/tcpip/ttylinkd.conf.5 b/tcpip/ttylinkd.conf.5
new file mode 100644
index 0000000..ecba3a1
--- /dev/null
+++ b/tcpip/ttylinkd.conf.5
@@ -0,0 +1,22 @@
+.TH TTYLINKD.CONF 5 "9 March 1997" Linux "Linux Programmer's Manual"
+.SH NAME
+ttylinkd.conf \- ttylinkd configuration file.
+.SH DESCRIPTION
+.LP
+.B ttylinkd.conf
+allows the super user to determine certain parameters with ttylinkd(8).
+Current there is only one field, with the view of not extending this further
+(ttylinkd is about a resourceful as it is going to get).
+.SH PARAMETERS
+.TP 10
+.B #
+Any line beginning with the hash (#) symbol is a comment and wille be ignored.
+.TP 10
+.B "sysop=<user>[@hostname.domain.name]"
+The login <user> will be attempted to be contacted with ttylinkd(8) is
+launched. If you do not supply a hostname then the local host will be assumed.
+.SH SEE ALSO
+.BR ttylinkd (8).
+.SH AUTHOR
+Craig Small VK2XLZ <csmall@gonzo.triode.net.au>
+