ax25-apps/0000755000175000017500000000000013521356741010743 5ustar irlirlax25-apps/configure.ac0000644000175000017500000000414713521356721013235 0ustar irlirlAC_PREREQ([2.59]) AC_INIT([ax25-apps],[0.0.8-rc5],[linux-hams@vger.kernel.org]) AC_CONFIG_SRCDIR(ax25ipd/config.c) AM_INIT_AUTOMAKE([1.7]) AC_CONFIG_HEADERS(config.h) dnl Checks for programs. AC_PROG_AWK AC_PROG_CC AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_GCC_TRADITIONAL dnl Checks for libraries. AC_SUBST(AX25_LIB) AC_SUBST(NCURSES_LIB) AC_CHECK_LIB(ax25, ax25_config_load_ports, AX25_LIB="-lax25", AC_MSG_ERROR(Could not find the libax25 libraries; aborting)) AC_CHECK_LIB(ncursesw, initscr,NCURSES_LIB="-lncursesw", AC_MSG_ERROR(Could not find the ncursesw library; aborting)) dnl Checks for working glibc 2.1 headers AC_CHECK_TYPES([struct ax25_fwd_struct], [], [AC_MSG_ERROR([Both glibc and libax25 are too old or both installed])], [[#include ]]) AC_CHECK_MEMBER([struct nr_route_struct.ndigis], [], [AC_MSG_ERROR([Both glibc and libax25 are too old or both installed])], [[#include ]]) AC_CHECK_TYPES([struct rose_facilities_struct], [], [AC_MSG_ERROR([Both glibc and libax25 are too old or both installed])], [[#include /* Or will blow up */ #include ]]) dnl Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(fcntl.h sys/file.h sys/ioctl.h sys/time.h syslog.h termio.h unistd.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_TYPE_OFF_T AC_HEADER_TIME AC_STRUCT_TM dnl Checks for library functions. AC_FUNC_MEMCMP AC_FUNC_UTIME_NULL AC_FUNC_VPRINTF AC_CHECK_FUNCS(gettimeofday mktime select socket strdup strerror strspn strstr strtol strtoul uname) dnl Only use -Wall if we have gcc if test "x$GCC" = "xyes"; then if test -z "`echo "$CFLAGS" | grep "\-Wall" 2> /dev/null`" ; then CFLAGS="$CFLAGS -Wall" fi fi basever=$(echo $PACKAGE_VERSION | sed -e 's@-.*$@@') AC_SUBST(BASEVERSION, [$basever]) extraver=$(echo $PACKAGE_VERSION | sed -e 's@@<:@^-@:>@*-@@' -e 's@-@_@') AC_SUBST(EXTRAVERSION, [$extraver]) AC_CONFIG_FILES([call/Makefile ax25ipd/Makefile listen/Makefile Makefile ax25rtd/Makefile ax25mond/Makefile ax25-apps.spec]) AC_OUTPUT ax25-apps/Makefile.am0000644000175000017500000000054613521356721013002 0ustar irlirlSUBDIRS = ax25ipd ax25rtd call listen ax25mond EXTRA_DIST = pathnames.h ax25-apps.spec installconf: @for app in $(SUBDIRS); do $(MAKE) -C $$app installconf; done AX25_SYSCONFDIR=$(sysconfdir)/ax25 AX25_LOCALSTATEDIR=$(localstatedir)/ax25 AM_CPPFLAGS = -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" ax25-apps/ax25-apps.spec.in0000644000175000017500000000323513521356721013745 0ustar irlirlName: ax25-apps Version: @BASEVERSION@.@EXTRAVERSION@ Release: 1%{?dist} Summary: AX.25 ham radio applications #ax25ipd is BSD licensed, rest is GPLv2+ License: GPLv2+ and BSD URL: http://www.linux-ax25.org/ Source0: http://www.linux-ax25.org/pub/%{name}/%{name}-@VERSION@.tar.gz BuildRoot: %{_tmppath}/%{name}-@VERSION@-%{release}-root-%(%{__id_u} -n) BuildRequires: gcc gcc-c++ BuildRequires: libax25-devel BuildRequires: ncurses-devel %description This package provides specific user applications for hamradio that use AX.25 Net/ROM or ROSE network protocols: * axcall: a general purpose AX.25, NET/ROM and ROSE connection program. * axlisten: a network monitor of all AX.25 traffic heard by the system. * ax25ipd: an RFC1226 compliant daemon which provides encapsulation of AX.25 traffic over IP. * ax25mond: retransmits data received from sockets into an AX.25 monitor socket. %prep %setup -q -n %{name}-@VERSION@ %build %configure --program-transform-name='s@^call$@ax&@;s@^listen$@ax&@' make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install make DESTDIR=$RPM_BUILD_ROOT installconf %clean rm -rf $RPM_BUILD_ROOT %files %doc AUTHORS ChangeLog COPYING README %doc ax25ipd/COPYING.ax25ipd ax25ipd/HISTORY.ax25ipd ax25ipd/README.ax25ipd %doc ax25rtd/TODO.ax25rtd ax25rtd/README.ax25rtd %{_bindir}/* %config(noreplace) %{_sysconfdir}/ax25/ax25ipd.conf %config(noreplace) %{_sysconfdir}/ax25/ax25mond.conf %config(noreplace) %{_sysconfdir}/ax25/ax25rtd.conf %{_sbindir}/* %{_mandir}/man?/* %changelog * Sat Jun 27 2009 Ralf Baechle - Initial version ax25-apps/ChangeLog0000644000175000017500000000603113521356721012513 0ustar irlirlax25-apps (0.0.8) * Switch to more modern autoconf and automake to fix build issues on many modern systems. * Fix various application crashes. * Support for UNIX98 pseudo terminals. * New option -S to silence call for use in shell scripts. * Add support for building rpm packages. * Relicense listen/ripdump.c to permit commercial use and distribution. * Fix build issues with modern autoconf, automake and GCC. * For the tarball release Regenerate generates files with most recent autoconf and automake. * Fix grammar of bpqether log message. * Support all standard baud rates from 50 to 4000000 bits per second with ax25ipd. * Fix grammar of bpqether log message. * Support all standard baud rates from 50 to 4000000 bits per second with ax25ipd. * call: Do not exit when running in curses mode and the window is resized. call now relies on libncursesw and is UTF8-capable. * Lots of small cleanup and fixes to silence warnings and issues raised by modern compilers, code checkers and manual review. * Improvments to git usage. * Update to work without warnings with latest autotools. * Lots of stylistic cleanups to the code trying to follow the coding style of the kernel. * Many distributions have changed the names of call to axcall and listen to axlisten. Change the rpm spec file to do that by default. For now however the defaults when building straight from source will stay unchanged. ax25-apps (0.0.7) * Added opentrac to listen, Thanks to Scott Miller * More autobreak nightmares ax25-apps (0.0.6) * listen can decode INP Packets. Thanks to Jeroen Vreeken * Added \n to flexdump after "Poll" and "data" - Alexandre Fornieles * ax25ipd bug-fixes - Terry Dawson, Ray Wells, et.al. * ssid correct matches in ax25ipd - Steve Fraser ax25-apps (0.0.5) * New rosedump.c from Jean-Paul. * Changes to ax25rtd from Hubert F5LCT for problems with kernel 2.2.13. * Fix for ax25rtd in VC mode * Patch from Hans to work better with newer GCCs * doc dirs are now relocatable * Added Jens patches - Adapted ax25rtd to new kernel-ax25. ax25-learn-only-mine has to be set to "true" for all interfaces to work right now. Note that this is only a temporary solution. - Made new-ax25-stack ready (listen, ax25rtd). Will still work with old kernel-AX25 but need recompile. * New program, ax25mond -- Craig Small ax25-apps 0.0.4 * Added Tomi's patch so it correctly detects broken headers -- Craig Small Mon, 23 Aug 1999 11:45:46 +1000 ax25-apps 0.0.3 * Should now compile on libc5 (probably) * Some nice formatting changes to listen * Added Klaus Kudielka's ax25rtd patch * Added some authors to AUTHORS -- Craig Small Tue, 10 Aug 1999 10:06:56 +1000 ax25-apps 0.0.2 * Fixed where to find config files * Had another crack at working around broken headers -- Craig Small 7 July 1999 ax25-apps 0.0.1 * First public release -- Craig Small ax25-apps/ax25ipd/0000755000175000017500000000000013521356721012215 5ustar irlirlax25-apps/ax25ipd/ax25ipd.c0000644000175000017500000001465413521356721013647 0ustar irlirl/* ax25ipd.c main entrypoint * * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. * This software may be freely used, distributed, or modified, providing * this header is not removed. * */ /* * cleaned up and prototyped for inclusion into the standard linux ax25 * toolset in january 1997 by rob mayfield, vk5xxx/vk5zeu */ #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" #include "ax25ipd.h" int udp_mode; /* true if we need a UDP socket */ int ip_mode; /* true if we need the raw IP socket */ unsigned short my_udp; /* the UDP port to use (network byte order) */ char ttydevice[PATH_MAX]; /* the tty device for serial comms */ int ttyspeed; /* The baud rate on the tty device */ unsigned char mycallsign[7]; /* My callsign, shifted ASCII with SSID */ unsigned char mycallsign2[7]; /* My seconds port callsign, shifted ASCII with SSID */ unsigned char myalias[7]; /* An alias to use */ unsigned char myalias2[7]; /* An alias for second port */ char bc_text[128]; /* The text for beacon messages */ int bc_interval; /* The interval, in seconds, between beacons */ int bc_every; /* true=every, false=after */ int digi; /* True if we are connected to a TNC */ int loglevel; /* Verbosity level */ struct ax25ipd_stats stats; /* Usage statistics */ int dual_port; /* addition for dual port flag */ static jmp_buf restart_env; static int opt_version; static int opt_loglevel; static int opt_nofork; static int opt_help; static char opt_configfile[PATH_MAX]; static char opt_ttydevice[PATH_MAX]; static struct option options[] = { {"version", 0, NULL, 'v'}, {"loglevel", 1, NULL, 'l'}, {"help", 0, NULL, 'h'}, {"configfile", 1, NULL, 'c'}, {"ttydevice", 1, NULL, 'd'}, {"nofork", 0, NULL, 'f'}, {NULL, 0, NULL, 0} }; static void greet_world(void) { printf("\nax25ipd %s\n", VERSION); printf ("Copyright 1991, Michael Westerhof, Sun Microsystems, Inc.\n"); printf ("This software may be freely used, distributed, or modified, providing\nthis header is not removed\n\n"); fflush(stdout); } static void do_stats(void) { int save_loglevel; /* save the old loglevel, and force at least loglevel 1 */ save_loglevel = loglevel; loglevel = 1; printf("\nSIGUSR1 signal: statistics and configuration report\n"); greet_world(); dump_config(); dump_routes(); dump_params(); printf("\nInput stats:\n"); printf("KISS input packets: %d\n", stats.kiss_in); printf(" too big: %d\n", stats.kiss_toobig); printf(" bad type: %d\n", stats.kiss_badtype); printf(" too short: %d\n", stats.kiss_tooshort); printf(" not for me: %d\n", stats.kiss_not_for_me); printf(" I am destination: %d\n", stats.kiss_i_am_dest); printf(" no route found: %d\n", stats.kiss_no_ip_addr); printf("UDP input packets: %d\n", stats.udp_in); printf("IP input packets: %d\n", stats.ip_in); printf(" failed CRC test: %d\n", stats.ip_failed_crc); printf(" too short: %d\n", stats.ip_tooshort); printf(" not for me: %d\n", stats.ip_not_for_me); printf(" I am destination: %d\n", stats.ip_i_am_dest); printf("\nOutput stats:\n"); printf("KISS output packets: %d\n", stats.kiss_out); printf(" beacons: %d\n", stats.kiss_beacon_outs); printf("UDP output packets: %d\n", stats.udp_out); printf("IP output packets: %d\n", stats.ip_out); printf("\n"); fflush(stdout); /* restore the old loglevel */ loglevel = save_loglevel; } static void hupper(int i) { printf("\nSIGHUP!\n"); longjmp(restart_env, 1); } static void usr1_handler(int i) { printf("\nSIGUSR1!\n"); do_stats(); } static void int_handler(int i) { printf("\nSIGINT!\n"); do_stats(); exit(1); } static void term_handler(int i) { printf("\nSIGTERM!\n"); do_stats(); exit(1); } int main(int argc, char **argv) { if (setjmp(restart_env) == 0) { signal(SIGHUP, hupper); } *opt_configfile = 0; *opt_ttydevice = 0; /* set up the handler for statistics reporting */ signal(SIGUSR1, usr1_handler); signal(SIGINT, int_handler); signal(SIGTERM, term_handler); while (1) { int c; c = getopt_long(argc, argv, "c:d:fhl:v", options, NULL); if (c == -1) break; switch (c) { case 'c': strncpy(opt_configfile, optarg, sizeof(opt_configfile)-1); opt_configfile[sizeof(opt_configfile)-1] = 0; break; case 'd': strncpy(opt_ttydevice, optarg, sizeof(opt_ttydevice)-1); opt_ttydevice[sizeof(opt_ttydevice)-1] = 0; break; case 'f': opt_nofork = 1; break; case 'h': opt_help = 1; break; case 'v': opt_version = 1; break; case 'l': opt_loglevel = atoi(optarg); break; default: opt_help = 1; break; } } if (optind < argc) { printf("Unknown argument '%s' ...\n\n", argv[optind++]); opt_help = 1; } if (opt_version == 1) { greet_world(); exit(0); } if (opt_help == 1) { greet_world(); printf("Usage:\n"); printf("%s [flags]\n", argv[0]); printf("\nFlags:\n"); printf (" --version, -v Print version of program\n"); printf(" --help, -h This help screen\n"); printf (" --loglevel NUM, -l NUM Set logging level to NUM\n"); printf (" --configfile FILE, -c FILE Set configuration file to FILE\n"); printf (" --ttydevice TTYDEV, -d TTYDEV Set device parameter to TTYDEV\n"); printf (" --nofork, -f Do not put daemon in background\n"); exit(0); } /* Initialize all routines */ config_init(); kiss_init(); route_init(); process_init(); io_init(); /* read config file */ config_read(opt_configfile); if (opt_ttydevice[0] != '\0') { strncpy(ttydevice, opt_ttydevice, sizeof(ttydevice)-1); ttydevice[sizeof(ttydevice)-1] = '\0'; } /* print the current config and route info */ dump_config(); dump_routes(); dump_params(); /* Open the IO stuff */ io_open(); /* if we get this far without error, let's fork off ! :-) */ if (opt_nofork == 0) { if (!daemon_start(TRUE)) { syslog(LOG_DAEMON | LOG_CRIT, "ax25ipd: cannot become a daemon\n"); return 1; } } /* we need to close stdin, stdout, stderr: because otherwise * scripting like ttyname=$(ax25ipd | tail -1) does not work */ if (!isatty(1)) { fflush(stdout); fflush(stderr); close(0); close(1); close(2); } /* and let the games begin */ io_start(); return 0; } ax25-apps/ax25ipd/.gitignore0000644000175000017500000000004113521356721014200 0ustar irlirlax25ipd ax25ipd.8 ax25ipd.conf.5 ax25-apps/ax25ipd/crc.c0000644000175000017500000001152213521356721013131 0ustar irlirl/* crc.c Computations involving CRCs */ #include "ax25ipd.h" /* ********************************************************************** * The following code was taken from Appendix B of RFC 1171 * (Point-to-Point Protocol) * * The RFC credits the following sources for this implementation: * * Perez, "Byte-wise CRC Calculations", IEEE Micro, June, 1983. * * Morse, G., "Calculating CRC's by Bits and Bytes", Byte, * September 1986. * * LeVan, J., "A Fast CRC", Byte, November 1987. * * * The HDLC polynomial: x**0 + x**5 + x**12 + x**16 */ /* * u16 represents an unsigned 16-bit number. Adjust the typedef for * your hardware. */ typedef unsigned short u16; /* * FCS lookup table as calculated by the table generator in section 2. */ static u16 fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; #define PPPINITFCS 0xffff /* Initial FCS value */ #define PPPGOODFCS 0xf0b8 /* Good final FCS value */ /* * Calculate a new fcs given the current fcs and the new data. */ u16 pppfcs(u16 fcs, unsigned char *cp, int len) { /* ASSERT(sizeof (u16) == 2); */ /* ASSERT(((u16) -1) > 0); */ while (len--) fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff]; return fcs; } /* * End code from Appendix B of RFC 1171 ********************************************************************** */ /* * The following routines are simply convenience routines... * I'll merge them into the mainline code when suitably debugged */ /* Return the computed CRC */ unsigned short int compute_crc(unsigned char *buf, int l) { int fcs; fcs = PPPINITFCS; fcs = pppfcs(fcs, buf, l); fcs ^= 0xffff; return fcs; } /* Return true if the CRC is correct */ int ok_crc(unsigned char *buf, int l) { int fcs; fcs = PPPINITFCS; fcs = pppfcs(fcs, buf, l); return fcs == PPPGOODFCS; } /* * A test routine to make sure the CRC is working right on your hardware. * cc -DTEST crc.c * */ #ifdef TEST void main(int argc, char *argv[]) { unsigned char buf[258]; int l, i; unsigned short int f; l = 256; for (i = 0; i < l; i++) { buf[i] = i; } f = compute_crc(buf, l); printf("computed crc=0x%04x\n", f); buf[l] = (f & 0xff); buf[l + 1] = (f >> 8); printf("crc should be good... "); i = ok_crc(buf, l + 2); if (i) printf("CRC declared OK\n"); else printf("CRC declared bad\n"); buf[l + 1] = (f & 0xff); buf[l] = (f >> 8); printf("reversed the CRC byte order... CRC should be bad..."); i = ok_crc(buf, l + 2); if (i) printf("CRC declared OK\n"); else printf("CRC declared bad\n"); printf("changed the frame length... CRC should be bad..."); i = ok_crc(buf, l + 1); if (i) printf("CRC declared OK\n"); else printf("CRC declared bad\n"); buf[0] -= 1; printf("corrupted the data... CRC should be bad..."); i = ok_crc(buf, l + 2); if (i) printf("CRC declared OK\n"); else printf("CRC declared bad\n"); } #endif ax25-apps/ax25ipd/ax25ipd.conf0000644000175000017500000000574313521356721014351 0ustar irlirl# # ax25ipd configuration file for station floyd.vk5xxx.ampr.org # # Select axip transport. 'ip' is what you want for compatibility # with most other gates ... # socket ip # # Set ax25ipd mode of operation. (digi or tnc) # mode tnc # # If you selected digi, you must define a callsign. If you selected # tnc mode, the callsign is currently optional, but this may change # in the future! (2 calls if using dual port kiss) # #mycall vk5xxx-4 #mycall2 vk5xxx-5 # # In digi mode, you may use an alias. (2 for dual port) # #myalias svwdns #myalias2 svwdn2 # # Send an ident every 540 seconds ... # #beacon after 540 #btext ax25ip -- tncmode rob/vk5xxx -- Experimental AXIP gateway # # # Serial port, ethertap interface, or pipe connected to a kissattach in my case # # alternatively, if you have the kernel module bpqether: # if you use tun/tap or ethertap instead of kissattach you may say, without # leading slashes (!! - that's how ax25ipd consideres using the tty kiss # driver or tun/tap or ethertap): # with tun/tap: # device foobar # with ethertap (obsolete): # device tap0 # make sure you set a mycall above, or say axparms foobar -setcall te1st # note: the device will be up when you assign an ip address # _after_ starting ax25rtd (which initializes the device), start ax25d # with bpqether or pty, you do not need to care about the speed # tun/tap: as descriped in /usr/src/linux/Documentation/networking/tuntap.txt, # make a device # like this: # crw-r--r-- 1 root root 10, 200 Nov 26 13:32 tun # with the command mknod /dev/net/tun c 10 200 # As serial port, you could assign physical ports like ttyS0 or ptys. # ax25ipd supports BSD-style pseudo-terminals as well as the Unix98 pty's. # If the tty argument is "/dev/ptmx", then Unix98 behaviour # will automaticaly take effekt. With Unix98 pty's, the slave tty name # could not be forseen. That's why ax25ipd will print the corresponding # slave pty name as a separate line on stdout. # #device ampr device /dev/ttyp0 # # # Set the device speed # speed 9600 # # loglevel 0 - no output # loglevel 1 - config info only # loglevel 2 - major events and errors # loglevel 3 - major events, errors, and AX25 frame trace # loglevel 4 - all events # log 0 for the moment, syslog not working yet ... # loglevel 0 # # If we are in digi mode, we might have a real tnc here, so use param to # set the tnc parameters ... # #param 1 20 # # Broadcast Address definition. Any of the addresses listed will be forwarded # to any of the routes flagged as broadcast capable routes. # broadcast QST-0 NODES-0 # # ax.25 route definition, define as many as you need. # format is route (call/wildcard) (ip host at destination) # ssid of 0 routes all ssid's # # route [flags] # # Valid flags are: # b - allow broadcasts to be transmitted via this route # d - this route is the default route # #route vk2sut-0 44.136.8.68 b #route vk5xxx 44.136.188.221 b #route vk2abc 44.1.1.1 # In case of axudp port 93: #route vk2abc 44.1.1.1 udp 93 # # ax25-apps/ax25ipd/Makefile.am0000644000175000017500000000346613521356721014262 0ustar irlirl sbin_PROGRAMS = ax25ipd man_MANS = ax25ipd.8 ax25ipd.conf.5 EXTRA_DIST = ax25ipd.man ax25ipd.conf.man $(etcfiles) $(doc_DATA) CLEANFILES = ax25ipd.8 ax25ipd.8.tmp ax25ipd.conf.5 ax25ipd.conf.5.tmp ax25ipd.8: ax25ipd.man name_ax25ipd=$$(echo ax25ipd | sed -e '$(transform)') \ name_Ax25ipd=$$(echo $$name_ax25ipd | sed -r 's@^(.)@\U\1\E@') && \ name_AX25IPD=$$(echo $$name_ax25ipd | sed -r 's@^(.*)@\U\1\E@') && \ sed -e "s/@@@ax25ipd@@@/$$name_ax25ipd/g" \ -e "s/@@@Ax25ipd@@@/$$name_Ax25ipd/g" \ -e "s/@@@AX25IPD@@@/$$name_AX25IPD/g" \ $(srcdir)/ax25ipd.man > ax25ipd.8.tmp && \ mv ax25ipd.8.tmp ax25ipd.8; ax25ipd.conf.5: ax25ipd.conf.man name_ax25ipd=$$(echo ax25ipd | sed -e '$(transform)') \ name_Ax25ipd=$$(echo $$name_ax25ipd | sed -r 's@^(.)@\U\1\E@') && \ name_AX25IPD=$$(echo $$name_ax25ipd | sed -r 's@^(.*)@\U\1\E@') && \ sed -e "s/@@@ax25ipd@@@/$$name_ax25ipd/g" \ -e "s/@@@Ax25ipd@@@/$$name_Ax25ipd/g" \ -e "s/@@@AX25IPD@@@/$$name_AX25IPD/g" \ $(srcdir)/ax25ipd.conf.man > ax25ipd.conf.5.tmp && \ mv ax25ipd.conf.5.tmp ax25ipd.conf.5; doc_DATA = README.ax25ipd HISTORY.ax25ipd COPYING.ax25ipd ax25ipd_LDADD = $(AX25_LIB) ax25ipd_SOURCES = \ config.c \ crc.c \ io.c \ kiss.c \ ax25ipd.c \ ax25ipd.h \ process.c \ routing.c \ syslog.c \ bpqether.c # Needed so that install is optional etcfiles = ax25ipd.conf installconf: $(mkinstalldirs) $(DESTDIR)$(AX25_SYSCONFDIR) @list='$(etcfiles)'; for p in $$list; do \ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p"; \ $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p; \ done AX25_SYSCONFDIR=$(sysconfdir)/ax25 AX25_LOCALSTATEDIR=$(localstatedir)/ax25 AM_CPPFLAGS = -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" ax25-apps/ax25ipd/ax25ipd.man0000644000175000017500000000415613521356721014174 0ustar irlirl.TH @@@AX25IPD@@@ 1 "12 September 2001" Linux "Linux Programmer's Manual" .SH NAME @@@ax25ipd@@@ \- AX.25 into IP Encapsulator .SH SYNOPSIS .B @@@ax25ipd@@@ [options] .SH DESCRIPTION .LP .B @@@ax25ipd@@@ is an RFC1226 compliant daemon capable of providing the encapsualtion neccessary to send and receive AX.25 traffic across an IP transport. .LP Normally invoked as part of the boot order network initialisation, .B @@@ax25ipd@@@ is capable of talking directly to a serial port connected tnc, or over a pipe to a linux AX.25 port. KAM DE Dual Port Kiss is also supported. .LP .B @@@ax25ipd@@@ can be run as a pure encapsulator, or as a digital repeater with a functional callsign. Destination IP addresses are determined from a hardcoded map of callsign/IP Address pairs. .LP .B @@@ax25ipd@@@ defaults to using .B /etc/ax25/ax25ipd.conf as its config file. .LP .SH OPTIONS .TP 10 .BI \-v,--version Display the version. .TP 10 .BI \-h,--help Display a summary of the command line options. .TP 10 .BI \-l,--loglevel NUM Set the logging level to NUM. .TP 10 .BI \-c,--configfile FILE Use the configuration file named by FILE instead of the default. .TP 10 .BI \-d,--ttydevice TTYDEV Use ttydevice instead of the configured. This is useful because on systems with unix98 PTYs the device is allocated dynamicaly and thus the name is not predictable. .TP 10 .BI \-f,--nofork Do not become a daemon. Run in foreground. .SH FILES /etc/ax25/ax25ipd.conf .SH "SEE ALSO" .BR ax25ipd.conf (5), .BR ax25 (4), .BR kissattach (8). .LP .SH BUGS Routing needs to be looked at seriously, the method of routing is a real hack, and improvements to it have only added to the hack. .LP Logging needs to be properly implemented. .LP Command line parsing needs to be properly implemented. .LP .B @@@ax25ipd@@@ should be part of the Linux AX.25 kernel support probably :-) .SH AUTHORS .nf Michael Westerhof .br Michael Durrant VE3PNX .br D. Jeff Dionne VE3DJF .br Rob Mayfield VK5XXX/VK5ZEU .br Terry Dawson VK2KTJ .br .fi ax25-apps/ax25ipd/io.c0000644000175000017500000004117113521356721012774 0ustar irlirl/* io.c All base I/O routines live here * * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. * This software may be freely used, distributed, or modified, providing * this header is not removed. * * This is the only module that knows about base level UNIX/SunOS I/O * This is also the key dispatching module, so it knows about a lot more * than just I/O stuff. */ #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ax25ipd.h" static struct termio nterm; int ttyfd = -1; static int udpsock = -1; static int sock = -1; static struct sockaddr_in udpbind; static struct sockaddr_in to; static struct sockaddr_in from; static socklen_t fromlen; static time_t last_bc_time; int ttyfd_bpq = 0; /* * I/O modes for the io_error routine */ #define READ_MSG 0x00 #define SEND_MSG 0x01 #define IP_MODE 0x10 #define UDP_MODE 0x20 #define TTY_MODE 0x30 #ifndef FNDELAY #define FNDELAY O_NDELAY #endif /* * process an I/O error; return true if a retry is needed * * oops - the error flag; < 0 indicates a problem * buf - the data in question * bufsize - the size of the data buffer * dir - the direction; input or output * mode - the fd on which we got the error * where - line in the code where this function was called */ static int io_error( int oops, unsigned char *buf, int bufsize, int dir, int mode, int where) { /* if (oops >= 0) return 0; */ /* do we have an error ? */ /* dl9sau: nobody has set fd's to O_NONBLOCK. * thus EAGAIN (below) or EWOULDBLOCK are never be set. * Has someone removed this behaviour previously? * Anyway, in the current implementation, with blocking * read/writes, a read or write of 0 bytes means EOF, * for e.g. if the attached tty is closed. * We have to exit then. We've currentlsy no mechanism * for regulary reconnects. */ if (oops > 0) return 0; /* do we have an error ? */ if (oops == 0) { if (dir == READ_MSG && oops != TTY_MODE /* && != TCP_MODE, if we'd implement this */ ) return 0; fprintf(stderr, "Close event on mode 0x%2.2x (during %s). LINE %d. Terminating normaly.\n", mode, (dir == READ_MSG ? "READ" : "WRITE"), where); exit(1); } #ifdef EAGAIN if (errno == EAGAIN) { #ifdef notdef /* select() said that data is available, but recvfrom sais * EAGAIN - i really do not know what's the sense in this.. */ if (dir == READ_MSG && oops != TTY_MODE /* && != TCP_MODE, if we'd implement this */ ) return 0; perror("System 5 I/O error!"); fprintf(stderr, "A System 5 style I/O error was detected. This rogram requires BSD 4.2\n"); fprintf(stderr, "behaviour. This is probably a result of compile-time environment.\n"); fprintf(stderr, "Mode 0x%2.2x, LINE: %d. During %s\n", mode, where, (dir == READ_MSG ? "READ" : "WRITE")); exit(3); #else int ret = 0; if (dir == READ_MSG) { LOGL4("read / recv returned -1 EAGAIN\n"); ret = 0; } else if (dir == SEND_MSG) { LOGL4("write / send returned -1 EAGAIN, sleeping and retrying!\n"); usleep(100000); ret = 1; } return ret; #endif } #endif if (dir == READ_MSG) { if (errno == EINTR) return 0; /* never retry read */ if (errno == EWOULDBLOCK) { LOGL4("READ would block (?!), sleeping and retrying!\n"); usleep(100000); /* sleep a bit */ return 1; /* and retry */ } if (mode == IP_MODE) { perror("reading from raw ip socket"); exit(2); } else if (mode == UDP_MODE) { perror("reading from udp socket"); exit(2); } else if (mode == TTY_MODE) { perror("reading from tty device"); exit(2); } else { perror("reading from unknown I/O"); exit(2); } } else if (dir == SEND_MSG) { if (errno == EINTR) return 1; /* always retry on writes */ if (mode == IP_MODE) { if (errno == EMSGSIZE) { /* msg too big, drop it */ perror("message dropped on raw ip socket"); fprintf (stderr, "message was %d bytes long.\n", bufsize); return 0; } if (errno == ENETDOWN || errno == ENETRESET || errno == ENETUNREACH || errno == EHOSTDOWN || errno == EHOSTUNREACH || errno == ENONET || errno == EPERM) { /* host closed his axip receiver or dropped the line */ perror("error after sending on to axip partner. ignoring."); LOGL4("error after sending on to axip partner: %s; ignoring!\n", strerror(errno)); return 0; } if (errno == ENOBUFS) { /* congestion; sleep + retry */ LOGL4("send congestion on raw ip, sleeping and retrying!\n"); usleep(100000); return 1; } if (errno == EWOULDBLOCK) { LOGL4("send on raw ip would block, sleeping and retrying!\n"); usleep(100000); /* sleep a bit */ return 1; /* and retry */ } perror("writing to raw ip socket"); exit(2); } else if (mode == UDP_MODE) { if (errno == EMSGSIZE) { /* msg too big, drop it */ perror("message dropped on udp socket"); fprintf(stderr, "message was %d bytes long.\n", bufsize); return 0; } if (errno == ENETDOWN || errno == ENETRESET || errno == ENETUNREACH || errno == EHOSTDOWN || errno == EHOSTUNREACH || errno == ENONET || errno == EPERM) { /* host closed his axudp receiver or dropped the line */ perror("error after sending to axudp partner. ignoring."); LOGL4("error after sending to axudp partner: %s; ignoring!\n", strerror(errno)); return 0; } if (errno == ENOBUFS) { /* congestion; sleep + retry */ LOGL4("send congestion on udp, sleeping and retrying!\n"); usleep(100000); return 1; } if (errno == EWOULDBLOCK) { LOGL4("send on udp would block, sleeping and retrying!\n"); usleep(100000); /* sleep a bit */ return 1; /* and retry */ } perror("writing to udp socket"); exit(2); } else if (mode == TTY_MODE) { if (errno == EWOULDBLOCK) { LOGL4("write to tty would block, sleeping and retrying!\n"); usleep(100000); /* sleep a bit */ return 1; /* and retry */ } perror("writing to tty device"); exit(2); } else { perror("writing to unknown I/O"); exit(2); } } else { perror("Unknown direction and I/O"); exit(2); } return 0; } /* * Initialize the io variables */ void io_init(void) { /* * Close the file descriptors if they are open. The idea is that we * will be able to support a re-initialization if sent a SIGHUP. */ if (ttyfd >= 0) { close(ttyfd); ttyfd = -1; } if (sock >= 0) { close(sock); sock = -1; } if (udpsock >= 0) { close(udpsock); udpsock = -1; } /* * The memset is not strictly required - it simply zeros out the * address structure. Since both to and from are static, they are * already clear. */ memset(&to, 0, sizeof(struct sockaddr)); to.sin_family = AF_INET; memset(&from, 0, sizeof(struct sockaddr)); from.sin_family = AF_INET; memset(&udpbind, 0, sizeof(struct sockaddr)); udpbind.sin_family = AF_INET; } /* * open and initialize the IO interfaces */ void io_open(void) { int baudrate; int i_am_unix98_pty_master = 0; /* unix98 ptmx support */ char *namepts = NULL; /* name of the unix98 pts slave, which * the client has to use */ if (ip_mode) { sock = socket(AF_INET, SOCK_RAW, IPPROTO_AX25); if (sock < 0) { perror("opening raw socket"); exit(1); } if (fcntl(sock, F_SETFL, FNDELAY) < 0) { perror("setting non-blocking I/O on raw socket"); exit(1); } } if (udp_mode) { udpsock = socket(AF_INET, SOCK_DGRAM, 0); if (udpsock < 0) { perror("opening udp socket"); exit(1); } if (fcntl(udpsock, F_SETFL, FNDELAY) < 0) { perror("setting non-blocking I/O on UDP socket"); exit(1); } /* * Ok, the udp socket is open. Now express our interest in receiving * data destined for a particular socket. */ udpbind.sin_addr.s_addr = INADDR_ANY; udpbind.sin_port = my_udp; if (bind(udpsock, (struct sockaddr *) &udpbind, sizeof udpbind) < 0) { perror("binding udp socket"); exit(1); } } if (!strcmp("/dev/ptmx", ttydevice)) i_am_unix98_pty_master = 1; ttyfd = ((ttyfd_bpq = (strchr(ttydevice, '/') ? 0 : 1)) ? open_ethertap(ttydevice) : open(ttydevice, O_RDWR, 0)); if (ttyfd < 0) { perror("opening tty device"); exit(1); } if (fcntl(ttyfd, F_SETFL, FNDELAY) < 0) { perror("setting non-blocking I/O on tty device"); exit(1); } if (i_am_unix98_pty_master) { /* get name of pts-device */ namepts = ptsname(ttyfd); if (namepts == NULL) { perror("Cannot get name of pts-device."); exit(1); } /* unlock pts-device */ if (unlockpt(ttyfd) == -1) { perror("Cannot unlock pts-device."); exit(1); } } if (ttyfd_bpq) { set_bpq_dev_call_and_up(ttydevice); goto behind_normal_tty; } if (ioctl(ttyfd, TCGETA, &nterm) < 0) { perror("fetching tty device parameters"); exit(1); } if (ttyspeed == 50) baudrate = B50; else if (ttyspeed == 50) baudrate = B50; else if (ttyspeed == 75) baudrate = B75; else if (ttyspeed == 110) baudrate = B110; else if (ttyspeed == 134) baudrate = B134; else if (ttyspeed == 150) baudrate = B150; else if (ttyspeed == 200) baudrate = B200; else if (ttyspeed == 300) baudrate = B300; else if (ttyspeed == 600) baudrate = B600; else if (ttyspeed == 1200) baudrate = B1200; else if (ttyspeed == 1800) baudrate = B1800; else if (ttyspeed == 2400) baudrate = B2400; else if (ttyspeed == 4800) baudrate = B4800; else if (ttyspeed == 9600) baudrate = B9600; #ifdef B19200 else if (ttyspeed == 19200) baudrate = B19200; #else #ifdef EXTA else if (ttyspeed == 19200) baudrate = EXTA; #endif /* EXTA */ #endif /* B19200 */ #ifdef B38400 else if (ttyspeed == 38400) baudrate = B38400; #else #ifdef EXTB else if (ttyspeed == 38400) baudrate = EXTB; #endif /* EXTB */ #endif /* B38400 */ #ifdef B57600 else if (ttyspeed == 57600) baudrate = B57600; #endif /* B57600 */ #ifdef B76800 /* SPARC-specific */ else if (ttyspeed == 76800) baudrate = B76800; #endif /* B76800 */ #ifdef B115200 else if (ttyspeed == 115200) baudrate = B115200; #endif /* B115200 */ #ifdef B153600 /* SPARC-specific */ else if (ttyspeed == 153600) baudrate = B153600; #endif /* B153600 */ #ifdef B230400 else if (ttyspeed == 230400) baudrate = B230400; #endif /* B230400 */ #ifdef B307200 /* SPARC-specific */ else if (ttyspeed == 307200) baudrate = B307200; #endif /* B307200 */ #ifdef B460800 else if (ttyspeed == 460800) baudrate = B460800; #endif /* B460800 */ #ifdef B500000 else if (ttyspeed == 500000) baudrate = B500000; #endif /* B500000 */ #ifdef B576000 else if (ttyspeed == 576000) baudrate = B576000; #endif /* B576000 */ #ifdef B614400 /* SPARC-specific */ else if (ttyspeed == 614400) baudrate = B614400; #endif /* B614400 */ #ifdef B921600 /* SPARC-specific */ else if (ttyspeed == 921600) baudrate = B921600; #endif /* B921600 */ #ifdef B1000000 else if (ttyspeed == 1000000) baudrate = B1000000; #endif /* B1000000 */ #ifdef B1152000 else if (ttyspeed == 1152000) baudrate = B1152000; #endif /* B1152000 */ #ifdef B1500000 else if (ttyspeed == 1500000) baudrate = B1500000; #endif /* B1500000 */ #ifdef B2000000 else if (ttyspeed == 2000000) baudrate = B2000000; #endif /* B2000000 */ #ifdef B2500000 else if (ttyspeed == 2500000) baudrate = B2500000; #endif /* B2500000 */ #ifdef B3000000 else if (ttyspeed == 3000000) baudrate = B3000000; #endif /* B3000000 */ #ifdef B3500000 else if (ttyspeed == 3500000) baudrate = B3500000; #endif /* B3500000 */ #ifdef B4000000 else if (ttyspeed == 4000000) baudrate = B4000000; #endif /* B4000000 */ else baudrate = B9600; nterm.c_iflag = 0; nterm.c_oflag = 0; nterm.c_cflag = baudrate | CS8 | CREAD | CLOCAL; nterm.c_lflag = 0; nterm.c_cc[VMIN] = 0; nterm.c_cc[VTIME] = 0; if (ioctl(ttyfd, TCSETA, &nterm) < 0) { perror("setting tty device parameters"); exit(1); } if (digi) send_params(); if (i_am_unix98_pty_master) { /* Users await the slave pty to be referenced in the last line */ printf("Awaiting client connects on\n%s\n", namepts); syslog(LOG_INFO, "Bound to master pty /dev/ptmx with slave pty %s\n", namepts); } behind_normal_tty: last_bc_time = 0; /* force immediate id */ } /* * Start up and run the I/O mechanisms. * run in a loop, using the select call to handle input. */ void io_start(void) { int n, nb, hdr_len; fd_set readfds; unsigned char buf[MAX_FRAME]; struct timeval wait; struct iphdr *ipptr; time_t now; for (;;) { if ((bc_interval > 0) && digi) { now = time(NULL); if (last_bc_time + bc_interval < now) { last_bc_time = now; LOGL4("iostart: BEACON\n"); do_beacon(); } } wait.tv_sec = 10; /* lets us keep the beacon going */ wait.tv_usec = 0; FD_ZERO(&readfds); FD_SET(ttyfd, &readfds); if (ip_mode) { FD_SET(sock, &readfds); } if (udp_mode) { FD_SET(udpsock, &readfds); } nb = select(FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &wait); if (nb < 0) { if (errno == EINTR) continue; /* Ignore */ perror("select"); exit(1); } if (nb == 0) { fflush(stdout); fflush(stderr); /* just so we go back to the top of the loop! */ continue; } if (FD_ISSET(ttyfd, &readfds)) { do { n = read(ttyfd, buf, MAX_FRAME); } while (io_error(n, buf, n, READ_MSG, TTY_MODE, __LINE__)); LOGL4("ttydata l=%d\n", n); if (n > 0) { if (!ttyfd_bpq) { assemble_kiss(buf, n); } else { /* no crc but MAC header on bpqether */ if (receive_bpq(buf, n) < 0) { goto out_ttyfd; } } } /* * If we are in "beacon after" mode, reset the "last_bc_time" each time * we hear something on the channel. */ if (!bc_every) last_bc_time = time(NULL); } out_ttyfd: if (udp_mode) { if (FD_ISSET(udpsock, &readfds)) { do { fromlen = sizeof from; n = recvfrom(udpsock, buf, MAX_FRAME, 0, (struct sockaddr *) &from, &fromlen); } while (io_error(n, buf, n, READ_MSG, UDP_MODE, __LINE__)); LOGL4("udpdata from=%s port=%d l=%d\n", inet_ntoa(from. sin_addr), ntohs(from. sin_port), n); stats.udp_in++; if (n > 0) from_ip(buf, n); } } /* if udp_mode */ if (ip_mode) { if (FD_ISSET(sock, &readfds)) { do { fromlen = sizeof from; n = recvfrom(sock, buf, MAX_FRAME, 0, (struct sockaddr *) &from, &fromlen); } while (io_error(n, buf, n, READ_MSG, IP_MODE, __LINE__)); ipptr = (struct iphdr *) buf; hdr_len = 4 * ipptr-> ihl; LOGL4("ipdata from=%s l=%d, hl=%d\n", inet_ntoa(from. sin_addr), n, hdr_len); stats.ip_in++; if (n > hdr_len) from_ip(buf + hdr_len, n - hdr_len); } } /* if ip_mode */ } /* for forever */ } /* Send an IP frame */ void send_ip(unsigned char *buf, int l, unsigned char *targetip) { int n; if (l <= 0) return; memcpy(&to.sin_addr, targetip, 4); memcpy(&to.sin_port, &targetip[4], 2); LOGL4("sendipdata to=%s %s %d l=%d\n", inet_ntoa(to. sin_addr), to.sin_port ? "udp" : "ip", ntohs(to.sin_port), l); if (to.sin_port) { if (udp_mode) { stats.udp_out++; do { n = sendto(udpsock, buf, l, 0, (struct sockaddr *) &to, sizeof to); } while (io_error(n, buf, l, SEND_MSG, UDP_MODE, __LINE__)); } } else { if (ip_mode) { stats.ip_out++; do { n = sendto(sock, buf, l, 0, (struct sockaddr *) &to, sizeof to); } while (io_error(n, buf, l, SEND_MSG, IP_MODE, __LINE__)); } } } /* Send a kiss frame */ void send_tty(unsigned char *buf, int l) { int n; unsigned char *p; int nc; if (l <= 0) return; LOGL4("sendttydata l=%d\tsent: ", l); stats.kiss_out++; p = buf; nc = l; n = 0; /* * we have to loop around here because each call to write may write a few * characters. So we simply increment the buffer each time around. If * we ever write no characters, we should get an error code, and io_error * will sleep for a fraction of a second. Note that we are keyed to * the BSD 4.2 behaviour... the Sys 5 non-blocking I/O may or may not work * in this loop. We may detect system 5 behaviour (this would result from * compile-time options) by having io_error barf when it detects an EAGAIN * error code. */ do { if ((n > 0) && (n < nc)) { /* did we put only write a bit? */ p += n; /* point to the new data */ nc -= n; /* drop the length */ } n = write(ttyfd, p, nc); if (n > 0) { if (n != nc) { LOGL4("%d ", n); /* no-one said loglevel 4 */ } else { LOGL4("%d\n", n); /* was efficient!!! */ } } } while (((n > 0) && (n < nc)) || (io_error(n, p, nc, SEND_MSG, TTY_MODE, __LINE__))); } ax25-apps/ax25ipd/kiss.c0000644000175000017500000000735013521356721013337 0ustar irlirl/* kiss.c KISS assembly and byte-stuffing stuff * * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. * This software may be freely used, distributed, or modified, providing * this header is not removed. * * This is the only module that knows about the internal structure of * KISS frames. */ /* * dual port changes Feb 95 ve3pnx & ve3djf */ #include #include #include "ax25ipd.h" #define FEND 0xc0 #define FESC 0xdb #define TFEND 0xdc #define TFESC 0xdd static unsigned char iframe[MAX_FRAME]; static unsigned char *ifptr; static int ifcount; static int iescaped; static unsigned char oframe[MAX_FRAME]; static unsigned char *ofptr; static int ofcount; #define PTABLE_SIZE 10 static struct param_table_entry { unsigned char parameter; unsigned char value; } param_tbl[PTABLE_SIZE]; static int param_tbl_top; /* * Initialize the KISS variables */ void kiss_init(void) { ifptr = iframe; ifcount = 0; iescaped = 0; ofptr = oframe; ofcount = 0; param_tbl_top = 0; } /* * Assemble a kiss frame from random hunks of incoming data * Calls the "from_kiss" routine with the kiss frame when a * frame has been assembled. */ void assemble_kiss(unsigned char *buf, int l) { int i; unsigned char c; for (i = 0; i < l; i++, buf++) { c = *buf; if (c == FEND) { if (ifcount > 0) { /* Make sure that the control byte is zero */ if (*iframe == '\0' || *iframe == 0x10) { /* Room for CRC in buffer? */ if (ifcount < (MAX_FRAME - 2)) { stats.kiss_in++; from_kiss(iframe + 1, ifcount - 1); } else { stats.kiss_toobig++; LOGL2 ("assemble_kiss: dumped - frame too large\n"); } } else { stats.kiss_badtype++; LOGL2 ("assemble_kiss: dumped - control byte non-zero\n"); } } ifcount = 0; iescaped = 0; ifptr = iframe; continue; } if (c == FESC) { iescaped = 1; continue; } if (iescaped) { if (c == TFEND) c = FEND; if (c == TFESC) c = FESC; iescaped = 0; } if (ifcount < MAX_FRAME) { *ifptr = c; ifptr++; ifcount++; } } /* for every character in the buffer */ } /* convert a standard AX25 frame into a kiss frame */ void send_kiss(unsigned char type, unsigned char *buf, int l) { #define KISSEMIT(x) if (ofcount= PTABLE_SIZE) { fprintf(stderr, "param table is full; entry ignored.\n"); } param_tbl[param_tbl_top].parameter = p & 0xff; param_tbl[param_tbl_top].value = v & 0xff; LOGL4("added param: %d\t%d\n", param_tbl[param_tbl_top].parameter, param_tbl[param_tbl_top].value); param_tbl_top++; } /* dump the contents of the parameter table */ void dump_params(void) { int i; LOGL1("\n%d parameters\n", param_tbl_top); for (i = 0; i < param_tbl_top; i++) { LOGL1(" %d\t%d\n", param_tbl[i].parameter, param_tbl[i].value); } fflush(stdout); } /* send the parameters to the TNC */ void send_params(void) { int i; unsigned char p, v; for (i = 0; i < param_tbl_top; i++) { p = param_tbl[i].parameter; v = param_tbl[i].value; send_kiss(p, &v, 1); LOGL2("send_params: param %d %d\n", p, v); } } ax25-apps/ax25ipd/config.c0000644000175000017500000002314613521356721013634 0ustar irlirl/* config.c config file manipulation routines * * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. * This software may be freely used, distributed, or modified, providing * this header is not removed. * */ /* * Modifications made for dual port TNC's * by Michael Durrant VE3PNX and D. Jeff Dionne February 4, 1995 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ax25ipd.h" #include "../pathnames.h" /* Initialize the config table */ void config_init(void) { int i; *ttydevice = '\0'; for (i = 0; i < 7; i++) mycallsign[i] = '\0'; for (i = 0; i < 7; i++) myalias[i] = '\0'; for (i = 0; i < 7; i++) mycallsign2[i] = '\0'; for (i = 0; i < 7; i++) myalias2[i] = '\0'; digi = 1; ttyspeed = 9600; loglevel = 0; bc_interval = 0; bc_text[0] = '\0'; bc_every = 0; my_udp = htons(0); udp_mode = 0; ip_mode = 0; dual_port = 0; stats.kiss_in = 0; stats.kiss_toobig = 0; stats.kiss_badtype = 0; stats.kiss_tooshort = 0; stats.kiss_not_for_me = 0; stats.kiss_i_am_dest = 0; stats.kiss_no_ip_addr = 0; stats.kiss_out = 0; stats.kiss_beacon_outs = 0; stats.udp_in = 0; stats.udp_out = 0; stats.ip_in = 0; stats.ip_out = 0; stats.ip_failed_crc = 0; stats.ip_tooshort = 0; stats.ip_not_for_me = 0; stats.ip_i_am_dest = 0; } /* Open and read the config file */ void config_read(char *f) { FILE *cf; char buf[256], cbuf[256]; int errflag, e, lineno; char *fname; if (f==NULL || strlen(f) == 0) fname=CONF_AX25IPD_FILE; else fname=f; cf = fopen(fname, "r"); if (cf == NULL) { fprintf(stderr, "Config file %s not found or could not be opened\n", fname); exit(1); } errflag = 0; lineno = 0; while (fgets(buf, 255, cf) != NULL) { strcpy(cbuf, buf); lineno++; e = parse_line(buf); if (e < 0) { fprintf(stderr, "Config error at line %d: ", lineno); if (e == -1) fprintf(stderr, "Missing argument\n"); else if (e == -2) fprintf(stderr, "Bad callsign format\n"); else if (e == -3) fprintf(stderr, "Bad option - on/off\n"); else if (e == -4) fprintf(stderr, "Bad option - tnc/digi\n"); else if (e == -5) fprintf(stderr, "Host not known\n"); else if (e == -6) fprintf(stderr, "Unknown command\n"); else if (e == -7) fprintf(stderr, "Text string too long\n"); else if (e == -8) fprintf(stderr, "Bad option - every/after\n"); else if (e == -9) fprintf(stderr, "Bad option - ip/udp\n"); else fprintf(stderr, "Unknown error\n"); fprintf(stderr, "%s", cbuf); errflag++; } } if (errflag) exit(1); if (strlen(ttydevice) == 0) { fprintf(stderr, "No device specified in config file\n"); exit(1); } if ((udp_mode == 0) && (ip_mode == 0)) { fprintf(stderr, "Must specify ip and/or udp sockets\n"); exit(1); } if (digi) { if (mycallsign[0] == '\0') { fprintf(stderr, "No mycall line in config file\n"); exit(1); } } if ((digi) && (dual_port)) { if (mycallsign2[0] == '\0') { fprintf(stderr, "No mycall2 line in config file\n"); exit(1); } } fclose(cf); } /* Process each line from the config file. The return value is encoded. */ int parse_line(char *buf) { char *p, *q; unsigned char tcall[7], tip[4]; struct hostent *he; int i, j, uport; unsigned int flags; p = strtok(buf, " \t\n\r"); if (p == NULL) return 0; if (*p == '#') return 0; if (strcmp(p, "mycall") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; if (a_to_call(q, mycallsign) != 0) return -2; return 0; } else if (strcmp(p, "mycall2") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; if (a_to_call(q, mycallsign2) != 0) return -2; return 0; } else if (strcmp(p, "myalias") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; if (a_to_call(q, myalias) != 0) return -2; dual_port = 1; if (mycallsign2[0] == '\0') { dual_port = 0; } return 0; } else if (strcmp(p, "myalias2") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; if (a_to_call(q, myalias2) != 0) return -2; return 0; } else if (strcmp(p, "device") == 0) { /* already set? i.e. with commandline option, which overwrites * the preconfigured setting. useful for systems with unix98 * style pty's */ if (*ttydevice) return 0; q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; strncpy(ttydevice, q, sizeof(ttydevice)-1); ttydevice[sizeof(ttydevice)-1] = 0; return 0; } else if (strcmp(p, "mode") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; if (strcmp(q, "digi") == 0) digi = 1; else if (strcmp(q, "tnc") == 0) digi = 0; else return -4; return 0; } else if (strcmp(p, "speed") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; ttyspeed = atoi(q); return 0; } else if (strcmp(p, "socket") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; if (strcmp(q, "ip") == 0) { ip_mode = 1; } else if (strcmp(q, "udp") == 0) { udp_mode = 1; my_udp = htons(DEFAULT_UDP_PORT); q = strtok(NULL, " \t\n\r"); if (q != NULL) { i = atoi(q); if (i > 0) my_udp = htons(i); } } else return -9; return 0; } else if (strcmp(p, "beacon") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; if (strcmp(q, "every") == 0) bc_every = 1; else if (strcmp(q, "after") == 0) bc_every = 0; else return -8; q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; bc_interval = atoi(q); return 0; /* This next one is a hack!!!!!! watch out!!!! */ } else if (strcmp(p, "btext") == 0) { q = p + strlen(p) + 1; if (strlen(q) < 2) return -1; /* line ends with a \n */ if (strlen(q) > sizeof(bc_text)) return -7; q[strlen(q) - 1] = '\0'; strncpy(bc_text, q, sizeof(bc_text)-1); bc_text[sizeof(bc_text)-1] = 0; return 0; } else if (strcmp(p, "loglevel") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; loglevel = atoi(q); return 0; } else if (strcmp(p, "route") == 0) { uport = 0; flags = 0; q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; if (a_to_call(q, tcall) != 0) return -2; q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; he = gethostbyname(q); if (he != NULL) { memcpy(tip, he->h_addr_list[0], 4); } else { /* maybe user specified a numeric addr? */ j = inet_addr(q); if (j == -1) return -5; /* if -1, bad deal! */ memcpy(tip, &j, 4); } if (my_udp) uport = ntohs(my_udp); while ((q = strtok(NULL, " \t\n\r")) != NULL) { if (strcmp(q, "udp") == 0) { /* uport == 0 should never happen. * re-use setting. It costed me a long time * to realize that "udp" in ax25ipd.conf * is not enough. It used the wrong port; * 93 is standard, I configured socket udp 93, * but ax25ipd talked to partners on that * strange 10093. Even more, ax25ipd.conf's * examples for axip did not help - and the * manual is far from complete. */ if (uport == 0) uport = DEFAULT_UDP_PORT; q = strtok(NULL, " \t\n\r"); if (q != NULL) { i = atoi(q); if (i > 0) uport = i; } } else { /* Test for broadcast flag */ if (strchr(q, 'b')) { flags |= AXRT_BCAST; } /* Test for Default flag */ if (strchr(q, 'd')) { flags |= AXRT_DEFAULT; } } } route_add(tip, tcall, uport, flags); return 0; } else if (strcmp(p, "broadcast") == 0) { while ((q = strtok(NULL, " \t\n\r")) != NULL) { if (a_to_call(q, tcall) != 0) return -2; bcast_add(tcall); } return 0; } else if (strcmp(p, "param") == 0) { q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; i = atoi(q); q = strtok(NULL, " \t\n\r"); if (q == NULL) return -1; j = atoi(q); param_add(i, j); return 0; } return -999; } /* Convert ascii callsign to internal format */ int a_to_call(char *text, unsigned char *tcall) { int i; int ssid; unsigned char c; if (strlen(text) == 0) return -1; ssid = 0; for (i = 0; i < 6; i++) { tcall[i] = (' ' << 1); } tcall[6] = '\0'; for (i = 0; i < strlen(text); i++) { c = text[i]; if (c == '-') { ssid = atoi(&text[i + 1]); if (ssid > 15) return -1; tcall[6] = (ssid << 1); return 0; } if (islower(c)) c = toupper(c); if (i > 5) return -1; tcall[i] = (c << 1); } return 0; } /* Convert internal callsign to printable format */ char *call_to_a(unsigned char *tcall) { int i; int ssid; char *tptr; static char t[10]; for (i = 0, tptr = t; i < 6; i++) { if (tcall[i] == (' ' << 1)) break; *tptr = tcall[i] >> 1; tptr++; } ssid = (tcall[6] >> 1) & 0x0f; if (ssid > 0) { *tptr = '-'; tptr++; if (ssid > 9) { *tptr = '1'; tptr++; ssid -= 10; } *tptr = '0' + ssid; tptr++; } *tptr = '\0'; return &t[0]; } /* print the configuration data out */ void dump_config(void) { LOGL1("\nCurrent configuration:\n"); if (ip_mode) LOGL1(" socket ip\n"); if (udp_mode) LOGL1(" socket udp on port %d\n", ntohs(my_udp)); LOGL1(" mode %s\n", digi ? "digi" : "tnc"); LOGL1(" device %s\n", ttydevice); LOGL1(" speed %d\n", ttyspeed); if (digi) LOGL1(" mycall %s\n", call_to_a(mycallsign)); if (digi && myalias[0]) LOGL1(" myalias %s\n", call_to_a(myalias)); if (bc_interval > 0) { LOGL1(" beacon %s %d\n", bc_every ? "every" : "after", bc_interval); LOGL1(" btext %s\n", bc_text); } LOGL1(" loglevel %d\n", loglevel); (void) fflush(stdout); } ax25-apps/ax25ipd/process.c0000644000175000017500000002115413521356721014042 0ustar irlirl/* process.c Handle processing and routing of AX25 frames * * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. * This software may be freely used, distributed, or modified, providing * this header is not removed. * * This is the only module that knows about the internal structure of * AX25 frames. */ /* * Dual port additions by M.Durrant VE3PNX and D.J.Dionne Feb 4, 1995 */ #include #include #include #include "ax25ipd.h" /* if dual port the upper nibble will have a value of 1 (not 0) */ #define FROM_PORT2(p) (((*(p+1))&0x10)!=0) #define FOR_PORT2(p) (addrmatch(p,mycallsign2) || addrmatch(p,myalias2)) /* ve3djf and ve3pnx addition above */ #define IS_LAST(p) (((*(p+6))&0x01)!=0) #define NOT_LAST(p) (((*(p+6))&0x01)==0) #define REPEATED(p) (((*(p+6))&0x80)!=0) #define NOTREPEATED(p) (((*(p+6))&0x80)==0) #define IS_ME(p) (addrmatch(p,mycallsign) || addrmatch(p,myalias) || addrmatch(p,mycallsign2) || addrmatch(p,myalias2) ) #define NOT_ME(p) (!(addrmatch(p,mycallsign) || addrmatch(p,myalias) || addrmatch(p,mycallsign2) || addrmatch(p,myalias2) ) ) #define ARE_DIGIS(f) (((*(f+13))&0x01)==0) #define NO_DIGIS(f) (((*(f+13))&0x01)!=0) #define SETREPEATED(p) (*(p+6))|=0x80 #define SETLAST(p) (*(p+6))|=0x01 static unsigned char bcbuf[256]; /* Must be larger than bc_text!!! */ static int bclen; /* The size of bcbuf */ /* * Initialize the process variables */ void process_init(void) { bclen = -1; /* flag that we need to rebuild the bctext */ } /* * handle a frame given us by the kiss routines. The buf variable is * a pointer to an AX25 frame. Note that the AX25 frame from kiss does * not include the CRC bytes. These are computed by this routine, and * it is expected that the buffer we have has room for the CRC bytes. * We will either dump this frame, or send it via the IP interface. * * If we are in digi mode, we validate in several ways: * a) we must be the next digi in line to pick up the packet * b) the next site to get the packet (the next listed digi, or * the destination if we are the last digi) must be known to * us via the route table. * If we pass validation, we then set the digipeated bit for our entry * in the packet, compute the CRC, and send the packet to the IP * interface. * * If we are in tnc mode, we have less work to do. * a) the next site to get the packet (the next listed digi, or * the destination) must be known to us via the route table. * If we pass validation, we compute the CRC, and send the packet to * the IP interface. */ void from_kiss(unsigned char *buf, int l) { unsigned char *a, *ipaddr; if (l < 15) { LOGL2("from_kiss: dumped - length wrong!\n"); stats.kiss_tooshort++; return; } if (loglevel > 2) dump_ax25frame("from_kiss: ", buf, l); if (digi) { /* if we are in digi mode */ a = next_addr(buf); if (NOT_ME(a)) { stats.kiss_not_for_me++; LOGL4("from_kiss: (digi) dumped - not for me\n"); return; } if (a == buf) { /* must be a digi */ stats.kiss_i_am_dest++; LOGL2 ("from_kiss: (digi) dumped - I am destination!\n"); return; } SETREPEATED(a); a = next_addr(buf); /* find who gets it after us */ } else { /* must be tnc mode */ a = next_addr(buf); #ifdef TNC_FILTER if (IS_ME(a)) { LOGL2 ("from_kiss: (tnc) dumped - addressed to self!\n"); return; } #endif } /* end of tnc mode */ /* Lookup the IP address for this route */ ipaddr = call_to_ip(a); if (ipaddr == NULL) { if (is_call_bcast(a)) { /* Warning - assuming buffer has room for 2 bytes */ add_crc(buf, l); l += 2; send_broadcast(buf, l); } else { stats.kiss_no_ip_addr++; LOGL2 ("from_kiss: dumped - cannot figure out where to send this!\n"); } return; } else { /* Warning - assuming buffer has room for 2 bytes */ add_crc(buf, l); l += 2; send_ip(buf, l, ipaddr); if (is_call_bcast(a)) { send_broadcast(buf, l); } } } /* * handle a frame given us by the IP routines. The buf variable is * a pointer to an AX25 frame. * Note that the frame includes the CRC bytes, which we dump ASAP. * We will either dump this frame, or send it via the KISS interface. * * If we are in digi mode, we only validate that: * a) we must be the next digi in line to pick up the packet * If we pass validation, we then set the digipeated bit for our entry * in the packet, and send the packet to the KISS send routine. * * If we are in tnc mode, we validate pretty well nothing, just like a * real TNC... #define FILTER_TNC will change this. * We simply send the packet to the KISS send routine. */ void from_ip(unsigned char *buf, int l) { int port = 0; unsigned char *a; if (!ok_crc(buf, l)) { stats.ip_failed_crc++; LOGL2("from_ip: dumped - CRC incorrect!\n"); return; } l = l - 2; /* dump the blasted CRC */ if (l < 15) { stats.ip_tooshort++; LOGL2("from_ip: dumped - length wrong!\n"); return; } if (loglevel > 2) dump_ax25frame("from_ip: ", buf, l); if (digi) { /* if we are in digi mode */ a = next_addr(buf); if (NOT_ME(a)) { stats.ip_not_for_me++; LOGL2("from_ip: (digi) dumped - not for me!\n"); return; } if (a == buf) { /* must be a digi */ stats.ip_i_am_dest++; LOGL2 ("from_ip: (digi) dumped - I am destination!\n"); return; } if (dual_port == 1 && FOR_PORT2(a)) { port = 0x10; } SETREPEATED(a); } else { /* must be tnc mode */ a = next_addr(buf); #ifdef TNC_FILTER if (NOT_ME(a)) { LOGL2 ("from_ip: (tnc) dumped - I am not destination!\n"); return; } #endif } /* end of tnc mode */ if (!ttyfd_bpq) send_kiss(port, buf, l); else { send_bpq(buf, l); } } /* * Send an ID frame out the KISS port. */ void do_beacon(void) { int i; unsigned char *p; if (bclen == 0) return; /* nothing to do! */ if (bclen < 0) { /* build the id string */ p = bcbuf; *p++ = ('I' << 1); *p++ = ('D' << 1); *p++ = (' ' << 1); *p++ = (' ' << 1); *p++ = (' ' << 1); *p++ = (' ' << 1); *p++ = '\0' | 0x60; /* SSID, set reserved bits */ for (i = 0; i < 6; i++) *p++ = mycallsign[i]; *p++ = mycallsign[6] | 0x60; /* ensure reserved bits are set */ SETLAST(bcbuf + 7); /* Set the E bit -- last address */ *p++ = 0x03; /* Control field -- UI frame */ *p++ = 0xf0; /* Protocol ID -- 0xf0 is no protocol */ strcpy((char *)p, bc_text); /* add the text field */ bclen = 16 + strlen(bc_text); /* adjust the length nicely */ } if (loglevel > 2) dump_ax25frame("do_beacon: ", bcbuf, bclen); stats.kiss_beacon_outs++; if (!ttyfd_bpq) send_kiss(0, bcbuf, bclen); else { send_bpq(bcbuf, bclen); } } /* * return true if the addresses supplied match * modified for wildcarding by vk5xxx */ int addrmatch(unsigned char *a, unsigned char *b) { if ((*a == '\0') || (*b == '\0')) return 0; if ((*a++ ^ *b++) & 0xfe) return 0; /* "K" */ if ((*a++ ^ *b++) & 0xfe) return 0; /* "A" */ if ((*a++ ^ *b++) & 0xfe) return 0; /* "9" */ if ((*a++ ^ *b++) & 0xfe) return 0; /* "W" */ if ((*a++ ^ *b++) & 0xfe) return 0; /* "S" */ if ((*a++ ^ *b++) & 0xfe) return 0; /* "B" */ if (((*b) & 0x1e) == 0) return 1; /* ssid 0 matches all ssid's */ if ((*a++ ^ *b) & 0x1e) return 0; /* ssid */ /* if ((*a++^*b++)&0x1e)return 0; ssid (how it was ...) */ return 1; } /* * return pointer to the next station to get this packet */ unsigned char *next_addr(unsigned char *f) { unsigned char *a; /* If no digis, return the destination address */ if (NO_DIGIS(f)) return f; /* check each digi field. The first one that hasn't seen it is the one */ a = f + 7; do { a += 7; if (NOTREPEATED(a)) return a; } while (NOT_LAST(a)); /* all the digis have seen it. return the destination address */ return f; } /* * tack on the CRC for the frame. Note we assume the buffer is long * enough to have the two bytes tacked on. */ void add_crc(unsigned char *buf, int l) { unsigned short int u; u = compute_crc(buf, l); buf[l] = u & 0xff; /* lsb first */ buf[l + 1] = (u >> 8) & 0xff; /* msb next */ } /* * Dump AX25 frame. */ void dump_ax25frame(char *t, unsigned char *buf, int l) { #ifdef DEBUG int i; #endif unsigned char *a; printf("%s AX25: (l=%3d) ", t, l); if (l < 15) { printf("Bogus size...\n"); return; } printf("%s -> ", call_to_a(buf + 7)); printf("%s", call_to_a(buf)); if (ARE_DIGIS(buf)) { printf(" v"); a = buf + 7; do { a += 7; printf(" %s", call_to_a(a)); if (REPEATED(a)) printf("*"); } while (NOT_LAST(a)); } printf("\n"); #ifdef DEBUG for (i = 0; i < l; i++) printf("%02x ", buf[i]); printf("\n"); #endif fflush(stdout); } ax25-apps/ax25ipd/HISTORY.ax25ipd0000644000175000017500000000431113521356721014553 0ustar irlirl/* * HISTORY: * * 0.2.1 First distribution. * * 0.2.2 Comments, documentation, increased routing table to 128. * * 0.3.0 Added the "myalias" stuff, and began work on a beacon * mechanism. * Added support for the SIGHUP signal. Now a "kill -1 " * will make ax25ip re-read the config file and restart. * * 0.3.1 Now can specify the internet host by internet number as * well as host name. * * 0.4.0 Removed "autoadd" references * Added support for sending and listening on UDP sockets. * Added "SIGUSR" handler for dumping statistics on demand. * The Beacon frame is computed only once. * Any I/O errors will result in an exit()... This should be * fixed, specially for the UDP socket, but for the moment * this prevents errors from going wild and consuming vast * amounts of system and user time. * Fixed a bug in the KISSEMIT macro (thanks Jim Durham) * Added support for both termio and termios (thanks Jim Durham) * * 0.4.1 added a generic "io_error" routine to handle errors in the * main processing loop. This is in hopes of building a mech- * anism that can deal with temporary network failures in a * more robust fashion. * * 0.4.2 added support for the BSD sgtty method for setting and * controlling the serial port. * * 1.0.0 modifications to bring ax25ip into line with the standard * linux ax25 tools distribution. (vk5xxx) * * 1.0.1 various cleanups and changes ... now called ax25ipd * added prototypes, fork's itself ... (vk5xxx) * * 1.0.2 Incorporated patch allowing wildcarding of ax.25 callsigns. * The old method required a route for every call/ssid * combination. (thanks to John Woolner. Wildcarding to be * enhanced later ... * * July97 Added support for broadcast addresses and routes. * Converted route array to a linked list and removed the * maximum route limitations. (vk2ktj) * * 2005-10-26 - dl9sau: * added a new fast and efficient link to the linux kernel ax25 * stack, via the ethertap interface. * ax25 goes directly to the bpqether interface in the kernel via * ethertap/tuntap interfaces, which is a much better way than * traditional kissattach to a ttyp/ptyp pair. */ ax25-apps/ax25ipd/COPYING.ax25ipd0000644000175000017500000000234713521356721014531 0ustar irlirl AX25IPD +++++++ ax25ipd is a derivative work of ax25ip written by Michael Westerhof, and as such copyright is governed by Mike's original copyright notice. > /* ax25ipd.c main entrypoint > * > * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. > * This software may be freely used, distributed, or modified, providing > * this header is not removed. > * > */ And an extract from the original README file. > Comments, Criticism, Enhancements, Problems, Bugs > ------------------------------------------------- > > You can reach the person responsible for ax25ip at any of the following > addresses: > > Mike.Westerhof@Central.Sun.COM > > KA9WSB@WB9YAE > > Mike Westerhof, 17 N Main St, Mt Prospect, IL 60056 > > ------------------------------------------------------------------------- > Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. > This software may be freely used, distributed, or modified, providing > this footer is not removed. > ------------------------------------------------------------------------- This release of ax25ipd was cleaned up slightly, and prototyped, man page written, and had a few other's enhancements included along the way, by Rob Mayfield VK5XXX, mayfieldrob@mail.dec.com. ax25-apps/ax25ipd/ax25ipd.conf.man0000644000175000017500000001031013521356721015105 0ustar irlirl.TH AX25IPD.CONF 5 "7 July 1997" Linux "Linux Programmer's Manual" .SH NAME ax25ipd.conf \- Control the operation of @@@ax25ipd@@@. .SH DESCRIPTION .LP The .B ax25ipd.conf file controls the operation of the @@@ax25ipd@@@(8) program. The operation of the config file can best be seen in an example: .LP # .br # @@@ax25ipd@@@ configuration file for station floyd.vk5xxx.ampr.org .br # .br # Select axip transport. 'ip' is what you want for compatibility .br # with most other gates ... .br # .br socket ip .br # .br # Set @@@ax25ipd@@@ mode of operation. (digi or tnc) .br # .br mode tnc .br # .br # If you selected digi, you must define a callsign. If you selected .br # tnc mode, the callsign is currently optional, but this may change .br # in the future! (2 calls if using dual port kiss) .br # .br # mycall vk5xxx-4 .br # mycall2 vk5xxx-5 .br # .br # In digi mode, you may use an alias. (2 for dual port) .br # .br # myalias svwdns .br # myalias2 svwdn2 .br # .br # Send an ident every 540 seconds ... .br # .br # beacon after 540 .br # btext ax25ip -- tncmode rob/vk5xxx -- Experimental AXIP gateway .br # .br # Serial port, ethertap interface, or pipe connected to a kissattach in my case .br # alternatively, if you have the kernel module bpqether: .br # if you use tun/tap or ethertap instead of kissattach you may say, without .br # leading slashes (!! - that's how @@@ax25ipd@@@ consideres using the tty kiss .br # driver or tun/tap or ethertap): .br # with tun/tap: .br # device foobar .br # with ethertap (obsolete): .br # device tap0 .br # make sure you set a mycall above, or say axparms foobar -setcall te1st .br # note: the device will be up when you assign an ip address .br # _after_ starting ax25rtd (which initializes the device), start ax25d .br # with bpqether or pty, you do not need to care about the speed .br # tun/tap: as descriped in /usr/src/linux/Documentation/networking/tuntap.txt, .br # make a device # like this: .br # crw-r--r-- 1 root root 10, 200 Nov 26 13:32 tun .br # with the command mknod /dev/net/tun c 10 200 .br # As serial port, you could assign physical ports like ttyS0 or ptys. .br # @@@ax25ipd@@@ supports BSD-style pseudo-terminals as well as the Unix98 pty's. .br # If the tty argument is "/dev/ptmx", then Unix98 behaviour .br # will automaticaly take effekt. With Unix98 pty's, the slave tty name .br # could not be forseen. That's why @@@ax25ipd@@@ will print the corresponding .br # slave pty name as a separate line on stdout. .br # .br #device ampr .br device /dev/ttyp0 .br # .br # Set the device speed .br # .br speed 9600 .br # .br # loglevel 0 - no output .br # loglevel 1 - config info only .br # loglevel 2 - major events and errors .br # loglevel 3 - major events, errors, and AX25 frame trace .br # loglevel 4 - all events .br # log 0 for the moment, syslog not working yet ... .br # .br loglevel 4 .br # .br # If we are in digi mode, we might have a real tnc here, so use param to .br # set the tnc parameters ... .br # .br # param 1 20 .br # .br # Broadcast Address definition. Any of the addresses listed will be forwarded .br # to any of the routes flagged as broadcast capable routes. .br # .br broadcast QST-0 NODES-0 .br # .br # ax.25 route definition, define as many as you need. .br # format is route (call/wildcard) (ip host at destination) .br # ssid of 0 routes all ssid's .br # .br # route [flags] .br # .br # Valid flags are: .br # b - allow broadcasts to be transmitted via this route .br # d - this route is the default route .br # .br route vk2sut-0 44.136.8.68 b .br route vk5asf 44.136.188.221 b .br route vk2abc 44.1.1.1 d .br # .br # .br .LP More to come ... .br For the .I speed statement 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000 and 4000000 are supported on all architectures on Linux. On .B SPARC in addition to these line speeds 76800, 153600, 307200, 614400 and 921600 bits per second are also supported. If a system does not support a particular line speed, a speed of 9600 bits per second will be set instead. .SH FILES .LP /etc/ax25/ax25ipd.conf .SH "SEE ALSO" .BR @@@ax25ipd@@@ (8). ax25-apps/ax25ipd/README.ax25ipd0000644000175000017500000005131313521356721014353 0ustar irlirl ** ** This file is a cobbling together of the various notes on ax25ipd. ** ax25ipd ======= This README files pertains to version 1.0.2 of ax25ipd. (this document is still incomplete -- the UDP features lack examples) Overview -------- RFC1226 describes a method of using TCP/IP based networks to transport AX.25 frames. The ax25ipd utility uses this technique to transfer AX.25 frames in and out of TCP/IP environments. The interface to the AX.25 world is via KISS, with ax25ipd emulating either a standard KISS TNC or operating as a digipeater. AX.25 frames arriving on the KISS interface are routed to the IP interface and sent to the appropriate IP host system. Incoming frames from the IP interface are filtered and sent out the KISS interface. The operator controls the mode (tnc or digipeater), the routing (callsign to ip host mapping), and a few sundry controls via a configuration file, read at startup. ax25ipd is the Linux incarnation of ax25ip by Mike Westerhof, and is not intended to support any OS other than Linux. Although getting it to run on BSD should not present too many issues, there has been no attempt to retain BSD(Sun) operability since it was updated in an environment where there was no access to BSD. ax25ipd requires a serial port, and uses a raw socket to gain access to the IP layer of the host's TCP/IP implementation (this requires that ax25ipd be started by root). ax25ipd also supports using a standard UDP socket in place of the raw socket, although this does not adhere to RFC1226. Modes and Routing ----------------- There are two interfaces into and out of ax25ipd. The KISS interface is a standard serial port, running speeds up to 38400 baud. The IP interface reads and writes packets to an IP network in accordance with the specifications in RFC1226. Several factors determine how AX.25 frames route within ax25ipd and through the IP network, although some basic rules always apply: A frame will never leave ax25ipd on the same interface it came in on. This fundamental rule eliminates much complexity and much confusion on how to set up ax25ipd. A frame will never be sent to more than one IP host. Using an IP network as a broadcast media is probably not a good idea, especially across the Internet! Most other routing factors involve user configuration, and depend heavily on the mode of operation. In tnc mode, ax25ipd emulates a KISS tnc, and behaves as one would expect. The next destination callsign (either the destination or the next digipeater in line) is extracted from frames arriving on the KISS interface, and looked up in a routing table (built by the operator in the configuration file). The routing table provides the IP address to which this frame should be sent. The standard CRC is computed, tacked onto the frame, and the result is sent off to the target system. Frames arriving from the IP interface have the CRC checked and removed, and are forwarded to the KISS interface. Digi mode is a little more complicated. In this mode, ax25ipd is assigned a callsign, and acts as a digipeater. If a frame arriving on the KISS interface has ax25ipd's callsign specified as the next digipeater, ax25ipd will mark the frame as digipeated by it, look up the next callsign in the routing table, compute and append the CRC, and send the frame to the specified IP host. Frames arriving on the IP interface will be handled in a similar fashion, but note that in no cases will the frame be sent out the same port it arrived in on. Configuration ------------- A single configuration file is used to tailor ax25ipd. The file is usually named "ax25ipd.cfg" and should be located in the same directory ax25ipd is executed from. An example is probably the best way to become familiar with configuring ax25ipd. # Sample Configuration file for ax25ipd mode digi socket ip device /dev/ttya speed 9600 mycall KA9WSB-7 myalias ILSUN beacon every 540 btext ax25ipd -- digi ka9wsb-7 -- Experimental IP encapsulator loglevel 2 route W1AW-13 handbook.arrl.com route WZ9ZZZ last.one.edu param 1 20 The mode command selects the behavior of ax25ipd. The device line specifies the tty device to use, and the speed line selects the baud rate. Baud rates up to 34800 can be used, but 9600 is probably the fastest safe speed for serial cables of any distance (remember, KISS has no error detection). The mycall command is required only if you are in digi mode. The loglevel command selects the amount of verbosity you want, ranging from 0 (no output) to 4 (streams of data). The route command adds entries to a static routing table, currently 128 entries deep. In the example, AX.25 frames destined for W1AW-13 are forwarded to handbook.arrl.com, frames for WZ9ZZZ are sent to last.one.edu, and all others go nowhere. Specifying the word "default" in place of a callsign sets up a default route, used when the destination callsign cannot be found in the routing table. Finally, in digi mode, the device on the other end of the KISS interface is probably a TNC. The param command allows you to set the KISS parameters (txdelay, slottime, etc). As many param commands as required can be specified. The myalias command allows you to specify an alias for this digipeater. If you do this, you should probably use the beacon command to ensure that you ID regularly. The beacon every 540 command forces an ID message to be sent out the KISS interface every 9 minutes. Specifying beacon after 540 will send the ID packet only if the channel has been idle for 9 minutes. Sending a hang-up signal to the ax25ipd process will cause it to reread the configuration file and re-initialize itself (kill -HUP ). Sample Configuration - NOS-to-NOS --------------------------------- This example shows how to connect a pair of NOS-based PCs together using ax25ipd to carry AX.25 traffic. KA9WSB-3 one.two.com .----------. .----------. | | | | | | KISS | UNIX | | | NOS |------| host |---| | on | | | | TCP/IP network | PC | | | | `----------' `----------' | | (vast distance) | | fish.sea.com W1AW-5 | .----------. .----------. | | | | | | | UNIX | KISS | | |---| host |------| NOS | | | | 1200 | on | | | baud | PC | `----------' `----------' Config file: Config file: mode tnc mode tnc device /dev/ttya device /dev/ttya speed 9600 speed 1200 route W1AW-5 fish.sea.com route KA9WSB-3 one.two.com Using this setup, a user could run AX.25 connections between the two PCs. Note that while NOS can digipeat, NOS will not automatically allow users near W1AW-5 to digipeat through it to reach KA9WSB-3 (i.e. the command "C KA9WSB-3 V W1AW-5" will not have the desired result). This may or may not be a problem. Sample Configuration - digi-to-digi ----------------------------------- This example shows how to configure ax25ipd as a pair of digipeaters. (KA9WSB-3) one.two.com .----------. .----------. | | | | | Standard | KISS | UNIX | | | KISS |------| host |---| | TNC | | | | TCP/IP network | & radio | | | | `----------' `----------' | | (vast distance) | | (W1AW-5) | fish.sea.com W1AW-5 | .----------. .----------. | | | | | | | UNIX | KISS | Standard | |---| host |------| KISS | | | | 9600 | TNC | | | baud | & radio | `----------' `----------' Config file: Config file: mode digi mode digi mycall KA9WSB-3 mycall W1AW-5 device /dev/ttya device /dev/ttya speed 9600 speed 9600 route W1AW-5 fish.sea.com route KA9WSB-3 one.two.com param 1 20 param 1 33 In this setup, the UNIX hosts are "on the air". No PCs or additional hardware are required. A user near W1AW-5 could read a user near KA9WSB-3 simply by providing the digipeater path: "c KA9WSB V W1AW-5 KA9WSB-3" would work just fine. --------- We have added dual port capibility in DIGI MODE ONLY to allow us to use it with a Dataengine. Very little needed changing, but we did not document the changes. If you really need to know, diff the package against the original, which was/is at sunsite.unc.edu. To specify the second port, do something like this... #to use the second port, we need to be in digi mode mode digi # #port 0 on my tnc goes to vhf mycall ve3djf-12 myalias vhfdjf # #port 1 on my tnc goes to uhf. #The callsign and alias must be different for this to work right... mycall2 ve3djf-13 myalias2 uhfdjf # Everything else is the same. Note that if you leave out the mycall2 and myalias2 defines, it works just like the original in single port mode. For recieving on the KISS (dual) port, packets are checked against both callsigns and aliases. Whoever is on the other end of the AXIP link can find out what port it came from by looking at the who it got digi'd through, although I can't think of any reason to do that... For transmiting, you go out the port with the callsign matching the digi address. For example, to go out port 0 in the above example, digi through ve3djf-12 or vhfdjf. To go out port 1, digi through ve3djf-13 or uhfdjf. Deficencies: If you specify the same callsign on both ports, no error is generated, but the way the logic in the code works you always go out the upper port (port 1), not port 0. In TNC mode, there is really no way to specify which port to transmit on that I can think of, so there is no multiplexing there. Everything goes out port 0 in TNC mode still. Lastly, none of the dump routines know anything about ports on the KISS interface, so they look the same, with both ports lumped into one. Let us know if you like this hack, BTW. In retrospect, we should have written a KISSPlexor (tm:-) HIHI. I think we will do that next. 73! de Jeff / VE3DJF Jeff@lnx_rpi.ee.ryerson.ca VE3DJF@bbs.VE3RPI.ampr.org AXIP (IP encapsulation of AX.25 frames) daemon by Michael Westerhof. Network Working Group B. Kantor Request for Comments: 1226 Univ. of Calif San Diego May 1991 Internet Protocol Encapsulation of AX.25 Frames Status of this Memo This memo describes a method for the encapsulation of AX.25 (the Amateur Packet-Radio Link-Layer Protocol) frames within IP packets. This technique is an Experimental Protocol for the Internet community. Discussion and suggestions for improvement are requested. Please refer to the current edition of the "IAB Official Protocol Standards" for the standardization state and status of this protocol. Distribution of this memo is unlimited. The AX.25 Amateur Packet-Radio Link-Layer Protocol The AX.25 Amateur Packet-Radio Link-Layer Protocol is described in the publication by that name [1], incorporated here by reference. Each AX.25 packet ("frame") is encapsulated in one IP datagram. Normally no AX.25 frame will exceed 330 octets, so fragmentation at the IP layer should not be necessary. However, experiments with larger AX.25 frame sizes may require the use of standard IP fragmentation and reassembly procedures. When an AX.25 frame is encapsulated within an IP packet, HDLC framing elements (flags and zero-stuffing) are omitted, as the IP datagram adequately delimits the beginning and end of each AX.25 frame. The 16-bit CRC-CCITT frame check sequence (normally generated by the HDLC transmission hardware) is included. In all other respects, AX.25 frames are encapsulated unaltered. Each such IP datagram shall have a protocol number of 93. Reference [1] AX.25 Amateur Packet-Radio Link-Layer Protocol Version 2.0 October 1984. Available from the American Radio Relay League, Newington CT USA 06111, and other sources. Security Considerations Security issues are not discussed in this memo. Kantor [Page 1] RFC 1226 IP Encapsulation of AX.25 Frames May 1991 Author's Address Brian Kantor University of California at San Diego Network Operations C-024 La Jolla, CA 92093-0214 Phone: (619) 534-6865 EMail: brian@UCSD.EDU Kantor [Page 2] /etc/ax25/ax25ipd.conf for vk5xxx # # ax25ipd configuration file for station floyd.vk5xxx.ampr.org # # Select axip transport. 'ip' is what you want for compatibility # with most other gates ... # socket ip # # Set ax25ipd mode of operation. (digi or tnc) # mode tnc # # If you selected digi, you must define a callsign. If you selected # tnc mode, the callsign is currently optional, but this may change # in the future! (2 calls if using dual port kiss) # #mycall vk5xxx-4 #mycall2 vk5xxx-5 # # In digi mode, you may use an alias. (2 for dual port) # #myalias svwdns #myalias2 svwdn2 # # Send an ident every 540 seconds ... # beacon after 540 btext ax25ip -- tncmode rob/vk5xxx -- Experimental AXIP gateway # # Serial port, or pipe connected to a kissattach in my case # device /dev/ttyqd # # Set the device speed # speed 9600 # # loglevel 0 - no output # loglevel 1 - config info only # loglevel 2 - major events and errors # loglevel 3 - major events, errors, and AX25 frame trace # loglevel 4 - all events # log 0 for the moment, syslog not working yet ... # loglevel 0 # # If we are in digi mode, we might have a real tnc here, so use param to # set the tnc parameters ... # #param 1 20 # # ax.25 route definition, define as many as you need (< 128?) # format is route (call/wildcard) (ip host at destination) # route vk5zeu* 44.136.202.3 route vk5wsr-1 44.136.202.241 route vk5wsr-0 44.136.202.241 route vk5dj-15 44.136.202.241 route vk5ham* 44.136.202.241 # # a default route. Carefull here, all undefined traffic end's up here. # route default 44.136.202.241 # another ax25ipd.cfg # Sample ax25ipd configuration file # # First select the mode of operation. (digi or tnc) # socket ip # mode tnc # # If you selected digi, you must define a callsign. If you selected # tnc mode, the callsign is currently optional, but this may change # in the future! # mycall ve3rpi-14 mycall2 ve3rpi-15 # # In digi mode, you may use an alias. # myalias ryeha1 myalias2 ryeha2 # # ID every 10 minutes... # beacon after 540 btext ax25ipd -- digi jeff -- Experimental IP gateway # # The tnc or host system must be connected to a serial port. # device /dev/ttyS1 # # The line speed is set here # speed 9600 # # loglevel 0 - no output # loglevel 1 - config info only # loglevel 2 - major events and errors # loglevel 3 - major events, errors, and AX25 frame trace # loglevel 4 - all events # loglevel 4 # # If digi mode, we probably have a tnc on the other end of the serial # port. Use the param command to set the KISS parameters (like txdelay!) # You can specify as many as you need. # param 1 20 # # Define some routes. This example routes all traffic for callsign ka9wsb-7 # to a host named waveguide.central.sun.com. You can define as many as # required. # route ve3rpi bbs.ve3rpi.ampr.org route ve3rpi-2 ryeham.ee.ryerson.ca route qst ryeham.ee.ryerson.ca # # A catch-all is provided: this line sends all calls not specifically # noted in the routing tables to sunbird.central.sun.com. Use this feature # with great care -- the host on the other end may not appreciate all the # traffic! # #route default sunbird.central.sun.com # ax25ipd.cfg.n8qlb # Sample ax25ipd configuration file (0.4.2 version) # # First select the mode of operation. (digi or tnc) # mode tnc # # The normal mode of operation is using IP datagrams: # socket ip # # But you can also use UDP datagrams. If you don't specify a port number # to use, the default will be chosen (usually a Good Idea). # socket udp # # If you selected digi, you must define a callsign. If you selected # tnc mode, the callsign is currently optional, but this may change # in the future! # mycall N8QLB-1 # # In digi mode, you may use an alias. # myalias LINUXX # # Note that if you use an alias, in order to guarantee you will properly # ID every 10 minutes, you MUST specify "beacon every 540" (or so -- # don't beacon too often, but setting it to 600 might end up squeaking # past the 10 minute time if the channel is busy) # A possible future enhancement may allow an ID only if necessary (i.e. # a packet has been digipeated using the "myalias" id)... # beacon every 540 btext ax25ipd -- digi n8qlb -- Experimental IP gateway # # The tnc or host system must be connected to a serial port. # device /dev/tnc # # The line speed is set here # speed 9600 # # loglevel 0 - no output # loglevel 1 - config info only # loglevel 2 - major events and errors # loglevel 3 - major events, errors, and AX25 frame trace # loglevel 4 - all events # loglevel 0 # # If digi mode, we probably have a tnc on the other end of the serial # port. Use the param command to set the KISS parameters (like txdelay!) # You can specify as many as you need. # param 1 20 # # Define some routes. This example routes all traffic for callsign ka9wsb-6 # to a host named sales.central.sun.com, using udp instead of raw ip. The # next line sends all traffic for e3abc-5 to a host at ip address 11.22.33.44, # sending the datagrams to udp port 12345. The last example sends frames # addressed to y4xyz-9 and sends them to flim.flam.com, using the ip # encapsulation. You can define as many as required. # #route n8qlb linux.n8qlb #route e3abc-5 11.22.33.44 udp 12345 #route y4xyz-9 flim.flam.com # # A catch-all is provided: this line sends all calls not specifically # noted in the routing tables to fred.central.sun.com. Use this feature # with great care -- the host on the other end may not appreciate all the # traffic! # route default linux.n8qlb # ax25ipd.cfg.stock # Sample ax25ipd configuration file # # First select the mode of operation. (digi or tnc) # mode digi # # If you selected digi, you must define a callsign. If you selected # tnc mode, the callsign is currently optional, but this may change # in the future! # mycall ka9wsb-7 # # In digi mode, you may use an alias. # myalias sunord # # ID every 10 minutes... # beacon after 540 btext ax25ipd -- digi ka9wsb-7 -- Experimental IP gateway # # The tnc or host system must be connected to a serial port. # device /dev/ttyb # # The line speed is set here # speed 9600 # # loglevel 0 - no output # loglevel 1 - config info only # loglevel 2 - major events and errors # loglevel 3 - major events, errors, and AX25 frame trace # loglevel 4 - all events # loglevel 3 # # If digi mode, we probably have a tnc on the other end of the serial # port. Use the param command to set the KISS parameters (like txdelay!) # You can specify as many as you need. # param 1 20 # # Define some routes. This example routes all traffic for callsign ka9wsb-7 # to a host named waveguide.central.sun.com. You can define as many as # required. # route ka9wsb-6 salespuke.central.sun.com # # A catch-all is provided: this line sends all calls not specifically # noted in the routing tables to sunbird.central.sun.com. Use this feature # with great care -- the host on the other end may not appreciate all the # traffic! # #route default sunbird.central.sun.com # Outstanding Issues ------------------ There are many outstanding issues with this software. A partial list, in no particular order, appears below. - Portability! io.c currently uses BSD-style select but sys5-style termios! - ICMP messages relating to ax25ipd are ignored. - Performance of the routing lookups is probably horrid. Also, a static table is a crock. Feedback from evaluations and test sites may help determine how this should work. - Statistics should be added. - A few more comments in the code would be nice. Comments, Criticism, Enhancements, Problems, Bugs ------------------------------------------------- You can reach the person responsible for ax25ipd at any of the following addresses: Mike.Westerhof@Central.Sun.COM KA9WSB@WB9YAE Mike Westerhof, 17 N Main St, Mt Prospect, IL 60056 ------------------------------------------------------------------------- Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. This software may be freely used, distributed, or modified, providing this footer is not removed. ------------------------------------------------------------------------- ax25-apps/ax25ipd/routing.c0000644000175000017500000001305613521356721014055 0ustar irlirl/* routing.c Routing table manipulation routines * * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. * This software may be freely used, distributed, or modified, providing * this header is not removed. * */ #include #include #include #include #include #include #include "ax25ipd.h" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif /* The routing table structure is not visible outside this module. */ struct route_table_entry { unsigned char callsign[7]; /* the callsign and ssid */ unsigned char padcall; /* always set to zero */ union { unsigned char ip_addr[4]; /* the IP address */ struct in_addr ip_addr_in; }; unsigned short udp_port; /* the port number if udp */ unsigned char pad1; unsigned char pad2; unsigned int flags; /* route flags */ struct route_table_entry *next; }; static struct route_table_entry *route_tbl; static struct route_table_entry *default_route; /* The Broadcast address structure is not visible outside this module either */ struct bcast_table_entry { unsigned char callsign[7]; /* The broadcast address */ struct bcast_table_entry *next; }; static struct bcast_table_entry *bcast_tbl; /* Initialize the routing module */ void route_init(void) { route_tbl = NULL; default_route = NULL; bcast_tbl = NULL; } /* Add a new route entry */ void route_add(unsigned char *ip, unsigned char *call, int udpport, unsigned int flags) { struct route_table_entry *rl, *rn; int i; /* Check we have an IP address */ if (ip == NULL) return; /* Check we have a callsign */ if (call == NULL) return; /* Find the last entry in the list */ rl = route_tbl; if (route_tbl) while (rl->next) rl = rl->next; rn = (struct route_table_entry *) malloc(sizeof(struct route_table_entry)); /* Build this entry ... */ for (i = 0; i < 6; i++) rn->callsign[i] = call[i] & 0xfe; rn->callsign[6] = (call[6] & 0x1e) | 0x60; rn->padcall = 0; memcpy(rn->ip_addr, ip, 4); rn->udp_port = htons(udpport); rn->pad1 = 0; rn->pad2 = 0; rn->flags = flags; rn->next = NULL; /* Update the default_route pointer if this is a default route */ if (flags & AXRT_DEFAULT) default_route = rn; if (rl) /* ... the list is already started add the new route */ rl->next = rn; else /* ... start the list off */ route_tbl = rn; /* Log this entry ... */ LOGL4("added route: %s %s %s %d %d\n", call_to_a(rn->callsign), inet_ntoa(rn->ip_addr_in), rn->udp_port ? "udp" : "ip", ntohs(rn->udp_port), flags); } /* Add a new broadcast address entry */ void bcast_add(unsigned char *call) { struct bcast_table_entry *bl, *bn; int i; /* Check we have a callsign */ if (call == NULL) return; /* Find the last entry in the list */ bl = bcast_tbl; if (bcast_tbl) while (bl->next) bl = bl->next; bn = (struct bcast_table_entry *) malloc(sizeof(struct bcast_table_entry)); /* Build this entry ... */ for (i = 0; i < 6; i++) bn->callsign[i] = call[i] & 0xfe; bn->callsign[6] = (call[6] & 0x1e) | 0x60; bn->next = NULL; if (bl) /* ... the list is already started add the new route */ bl->next = bn; else /* ... start the list off */ bcast_tbl = bn; /* Log this entry ... */ LOGL4("added broadcast address: %s\n", call_to_a(bn->callsign)); } /* * Return an IP address and port number given a callsign. * We return a pointer to the address; the port number can be found * immediately following the IP address. (UGLY coding; to be fixed later!) */ unsigned char *call_to_ip(unsigned char *call) { struct route_table_entry *rp; unsigned char mycall[7]; int i; if (call == NULL) return NULL; for (i = 0; i < 6; i++) mycall[i] = call[i] & 0xfe; mycall[6] = (call[6] & 0x1e) | 0x60; LOGL4("lookup call %s ", call_to_a(mycall)); rp = route_tbl; while (rp) { if (addrmatch(mycall, rp->callsign)) { LOGL4("found ip addr %s\n", inet_ntoa(rp->ip_addr_in)); return rp->ip_addr; } rp = rp->next; } /* * No match found in the routing table, use the default route if * we have one defined. */ if (default_route) { LOGL4("failed, using default ip addr %s\n", inet_ntoa(default_route->ip_addr_in)); return default_route->ip_addr; } LOGL4("failed.\n"); return NULL; } /* * Accept a callsign and return true if it is a broadcast address, or false * if it is not found on the list */ int is_call_bcast(unsigned char *call) { struct bcast_table_entry *bp; unsigned char bccall[7]; int i; if (call == NULL) return FALSE; for (i = 0; i < 6; i++) bccall[i] = call[i] & 0xfe; bccall[6] = (call[6] & 0x1e) | 0x60; LOGL4("lookup broadcast %s ", call_to_a(bccall)); bp = bcast_tbl; while (bp) { if (addrmatch(bccall, bp->callsign)) { LOGL4("found broadcast %s\n", call_to_a(bp->callsign)); return TRUE; } bp = bp->next; } return FALSE; } /* Traverse the routing table, transmitting the packet to each bcast route */ void send_broadcast(unsigned char *buf, int l) { struct route_table_entry *rp; rp = route_tbl; while (rp) { if (rp->flags & AXRT_BCAST) { send_ip(buf, l, rp->ip_addr); } rp = rp->next; } } /* print out the list of routes */ void dump_routes(void) { struct route_table_entry *rp; int i; for (rp = route_tbl, i = 0; rp; rp = rp->next) i++; LOGL1("\n%d active routes.\n", i); rp = route_tbl; while (rp) { LOGL1(" %s\t%s\t%s\t%d\t%d\n", call_to_a(rp->callsign), inet_ntoa(rp->ip_addr_in), rp->udp_port ? "udp" : "ip", ntohs(rp->udp_port), rp->flags); rp = rp->next; } fflush(stdout); } ax25-apps/ax25ipd/ax25ipd.h0000644000175000017500000001211313521356721013640 0ustar irlirl/* ax25ipd.h general configuration info * * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. * This software may be freely used, distributed, or modified, providing * this header is not removed. * */ /* * Modifications added for dual port kiss TNC * by Michael Durrant VE3PNX and D. Jeff Dionne Feb 4, 1995 */ /* * cleaned up and prototyped for inclusion into the standard linux ax25 * toolset in january 1997 by rob mayfield, vk5xxx/vk5zeu */ /* * added route flags, it's a little ugly, but is extensible fairly easily. * provided a mechanism for handling broadcast traffic * Terry Dawson, VK2KTJ, July 1997. * * Minor bug fixes. * Terry Dawson, VK2KTJ, September 2001. */ /* Define the current version number * * The first digit represents the major release (0 is a prototype release) * * The second represents major changes that might affect configuration * file formats or compilation sequences, or anything that may make * existing setups change. * * The last digit(s) marks simple bug fixes. * */ #define IPPROTO_AX25 93 #define DEFAULT_UDP_PORT 10093 #include extern int udp_mode; /* true if we need a UDP socket */ extern int ip_mode; /* true if we need the raw IP socket */ extern unsigned short my_udp; /* the UDP port to use (network byte order) */ extern char ttydevice[PATH_MAX]; /* the tty device for serial comms */ extern int ttyspeed; /* The baud rate on the tty device */ extern unsigned char mycallsign[7]; /* My callsign, shifted ASCII with SSID */ extern unsigned char mycallsign2[7]; /* My seconds port callsign, shifted ASCII with SSID */ extern unsigned char myalias[7]; /* An alias to use */ extern unsigned char myalias2[7]; /* An alias for second port */ extern char bc_text[128]; /* The text for beacon messages */ extern int bc_interval; /* The interval, in seconds, between beacons */ extern int bc_every; /* true=every, false=after */ extern int digi; /* True if we are connected to a TNC */ extern int loglevel; /* Verbosity level */ /* addition for dual port flag */ extern int dual_port; struct ax25ipd_stats { int kiss_in; /* # packets received */ int kiss_toobig; /* packet too large */ int kiss_badtype; /* control byte non-zero */ int kiss_out; /* # packets sent */ int kiss_beacon_outs; /* # of beacons sent */ int kiss_tooshort; /* packet too short to be a valid frame */ int kiss_not_for_me; /* packet not for me (in digi mode) */ int kiss_i_am_dest; /* I am destination (in digi mode) */ int kiss_no_ip_addr; /* Couldn't find an IP addr for this call */ int udp_in; /* # packets received */ int udp_out; /* # packets sent */ int ip_in; /* # packets received */ int ip_out; /* # packets sent */ int ip_failed_crc; /* from ip, but failed CRC check */ int ip_tooshort; /* packet too short to be a valid frame */ int ip_not_for_me; /* packet not for me (in digi mode) */ int ip_i_am_dest; /* I am destination (in digi mode) */ }; extern struct ax25ipd_stats stats; #define MAX_FRAME 2048 extern void LOGLn(int level, const char *str, ...); #define LOGL1(arg...) LOGLn(1, ##arg) #define LOGL2(arg...) LOGLn(2, ##arg) #define LOGL3(arg...) LOGLn(3, ##arg) #define LOGL4(arg...) LOGLn(4, ##arg) #define AXRT_BCAST 1 #define AXRT_DEFAULT 2 /* start external prototypes */ /* end external prototypes */ /* kiss.c */ void kiss_init(void); void assemble_kiss(unsigned char *, int); void send_kiss(unsigned char, unsigned char *, int); void param_add(int, int); void dump_params(void); void send_params(void); /* void do_beacon(void); not here it isnt !! xxx */ /* routing.c */ void route_init(void); void route_add(unsigned char *, unsigned char *, int, unsigned int); void bcast_add(unsigned char *); unsigned char *call_to_ip(unsigned char *); int is_call_bcast(unsigned char *); void send_broadcast(unsigned char *, int); void dump_routes(void); /* config.c */ void config_init(void); void config_read(char *); int parse_line(char *); int a_to_call(char *, unsigned char *); char *call_to_a(unsigned char *); void dump_config(void); /* process.c */ void process_init(void); void from_kiss(unsigned char *, int); void from_ip(unsigned char *, int); /* void do_broadcast(void); where did this go ?? xxx */ void do_beacon(void); int addrmatch(unsigned char *, unsigned char *); unsigned char *next_addr(unsigned char *); void add_crc(unsigned char *, int); void dump_ax25frame(char *, unsigned char *, int); /* io.c */ void io_init(void); void io_open(void); void io_start(void); void send_ip(unsigned char *, int, unsigned char *); void send_tty(unsigned char *, int); /* crc.c */ unsigned short int compute_crc(unsigned char *, int); unsigned short int pppfcs(unsigned short, unsigned char *, int); unsigned short int compute_crc(unsigned char *, int); int ok_crc(unsigned char *, int); /* io.c */ extern int ttyfd_bpq; extern int ttyfd; /* bpqether.c */ int send_bpq(unsigned char *buf, int len); int receive_bpq(unsigned char *buf, int l); int open_ethertap(char *ifname); int set_bpq_dev_call_and_up(char *ethertap_name); /* * end */ ax25-apps/ax25ipd/syslog.c0000644000175000017500000000035713521356721013706 0ustar irlirl#include #include #include "ax25ipd.h" void LOGLn(int level, const char *format, ...) { va_list va; va_start(va, format); if (loglevel >= level) vsyslog(LOG_DAEMON | LOG_WARNING, format, va); va_end(va); } ax25-apps/ax25ipd/bpqether.c0000644000175000017500000001777613521356721014215 0ustar irlirl/* * this is a port of wampes ethertap whith my extension for bpqether * * (c) 20020630 Thomas Osterried dl9sau * License: GPL */ #include #include #include #include #include #include #include #include #include #include #include #ifdef linux #include #ifndef KERNEL_VERSION #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) #endif #ifndef LINUX_VERSION_CODE #define LINUX_VERSION_CODE KERNEL_VERSION(2,4,0)-1 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #define TRY_TUNTAP 1 #include #include #else #include #endif #endif /* linux */ #include "ax25ipd.h" #define ETAP_MTU_MIN 256 #define ETAP_MTU 1024 #define ETAP_MTU_MAX 1500 #ifndef MAX_FRAME #define MAX_FRAME 2048 #endif #define ETHERTAP_HEADER_LEN_TUN 18 #define ETHERTAP_HEADER_LEN_ETHERTAP 16 #define ETHERTAP_HEADER_LEN_MAX ETHERTAP_HEADER_LEN_TUN struct ethertap_packet { char ethernet_header[18]; char data[MAX_FRAME]; }; static unsigned char hwaddr_remote[6]; static int ethertap_header_len; /*---------------------------------------------------------------------------*/ int send_bpq(unsigned char *buf, int l) { struct ethertap_packet ethertap_packet; void *addr = ðertap_packet; int offset = ETHERTAP_HEADER_LEN_MAX - ethertap_header_len; static const unsigned char ethernet_header[18] = { 0x00, 0x00, 0x00, 0x02, /* ??? ??? ETH_P_AX25 (16bit) */ 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, /* Destination address (kernel et hertap module) */ 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, /* Source address (WAMPES etherta p module) */ 0x08, 0xff /* Protocol (bpqether) */ }; if (l <= 0) return -1; if (l > (sizeof(ethertap_packet.data) - 2)) l = sizeof(ethertap_packet.data); memcpy(ethertap_packet.data + 2, buf, l); memcpy(ethertap_packet.ethernet_header, (const char *) ethernet_header, sizeof(ethernet_header)); memcpy(ethertap_packet.ethernet_header + 4, hwaddr_remote, 6); memcpy(ethertap_packet.ethernet_header + 4 + 6 + 2, hwaddr_remote +2, 6 -2); ethertap_packet.data[0] = (l + 5) % 256; ethertap_packet.data[1] = (l + 5) / 256; l += 2; /*send_tty(addr + offset, l + sizeof(ethertap_packet.ethernet_header) - offset);*/ write(ttyfd, addr + offset, l + sizeof(ethertap_packet.ethernet_header) - offset); return l; } /*---------------------------------------------------------------------------*/ int receive_bpq(unsigned char *buf, int l) { if ((l -= ethertap_header_len) <= 0 || (buf[ethertap_header_len-2] & 0xff) != 0x08 || (buf[ethertap_header_len-1] & 0xff) != 0xff) { /* not a bpqether packet. * drop silently - ethernet.ax25 is not the only protocol spoken * on ethernet ;) */ return -1; } l -= 2; if (l <= 0 && buf[ethertap_header_len] + buf[ethertap_header_len] * 256 - 5 != l) { /* length error in bpqether packet */ return 0; } from_kiss(buf + ethertap_header_len + 2, l); return l; } /*---------------------------------------------------------------------------*/ /* TUN/TAP support for linux. ethertap is obsolete */ #ifdef TRY_TUNTAP static int tun_alloc(char *dev) { struct ifreq ifr; int fd, err; fd = open("/dev/net/tun", O_RDWR); if (fd < 0) return -1; memset(&ifr, 0, sizeof(ifr)); /* Flags: IFF_TUN - TUN device (no Ethernet headers) * IFF_TAP - TAP device * * IFF_NO_PI - Do not provide packet information */ ifr.ifr_flags = IFF_TAP; if (*dev) { strncpy(ifr.ifr_name, dev, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = 0; } err = ioctl(fd, TUNSETIFF, (void *)&ifr); if (err < 0) { close(fd); return err; } strcpy(dev, ifr.ifr_name); /* persist mode */ #if 0 if (ioctl(fd, TUNSETPERSIST, 1) < 0) perror("TUNSETPERSIST"); #endif /* don't checksum */ #if 0 if (ioctl(fd, TUNSETNOCSUM, 1) < 0) perror("TUNSETNOCSUM"); #endif return fd; } #endif /*---------------------------------------------------------------------------*/ int open_ethertap(char *ifname) { int fd; char devname[PATH_MAX]; struct ifreq ifr; struct stat statbuf; int tuntap = 0; int mtu = 0; int skfd; strcpy(devname, "/dev/"); strncpy(devname + 5, ifname, sizeof(devname) - 5 -1); devname[sizeof(devname) -1] = 0; /* tuntap == 0: original ethertap. works on a real character device */ if (!stat(devname, &statbuf)) { fd = open(devname, O_RDWR); if (fd < 0) { LOGL2("%s: %s\n", devname, strerror(errno)); return -1; } #ifdef TRY_TUNTAP } else { strcpy(devname, ifname); fd = tun_alloc(devname); if (fd < 0) { LOGL2("%s: %s\n", devname, strerror(errno)); return -1; } ifname = devname; tuntap = 1; #endif } ethertap_header_len = (tuntap ? ETHERTAP_HEADER_LEN_TUN : ETHERTAP_HEADER_LEN_ETHERTAP); skfd = socket(AF_INET, SOCK_DGRAM, 0); if (skfd < 0) { perror("socket()"); close(fd); return -1; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = 0; if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) perror("SIOCGIFFLAGS"); ifr.ifr_flags |= (IFF_UP | IFF_NOARP); if (ioctl(skfd, SIOCSIFFLAGS, &ifr) < 0) perror("SIOCSIFFLAGS"); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = 0; if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) { perror("SIOCGIFHWADDR"); hwaddr_remote[0] = 0xfe; hwaddr_remote[1] = 0xfd; hwaddr_remote[2] = 0x0; hwaddr_remote[3] = 0x0; hwaddr_remote[4] = 0x0; hwaddr_remote[5] = 0x0; } else memcpy(hwaddr_remote, ifr.ifr_hwaddr.sa_data, 6); if (ttyspeed > ETAP_MTU_MIN && ttyspeed != 9600) { mtu = ttyspeed; } else { strncpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = 0; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) perror("SIOCGIFMTU"); else mtu = ifr.ifr_mtu; } if (mtu < ETAP_MTU_MIN || mtu > ETAP_MTU_MAX) mtu = ETAP_MTU; strncpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = 0; ifr.ifr_mtu = mtu; if (ioctl(skfd, SIOCSIFMTU, &ifr) < 0) perror("SIOSGIFMTU"); close(skfd); return fd; } /*---------------------------------------------------------------------------*/ int set_bpq_dev_call_and_up(char *ifname) { FILE * fp; char buf[1024]; char bpq_name[IFNAMSIZ]; char dev_name[IFNAMSIZ]; struct ifreq ifr; int drop; int skfd; int err = -1; /* * 1. find correspondent bpqether device in /proc (i.e. bpq0 for tap0) * 2. set the ax25 call of bpq0 (in this example) * 3. ifup bpq0 */ if (!(fp = fopen("/proc/net/bpqether", "r"))) { perror("/proc/net/bpqether"); return -1; } /* * look up bpqether devce in /proc: * dev ether destination accept from * bpq0 tap0 FF:FF:FF:FF:FF:FF * */ drop = 1; *bpq_name = *dev_name = 0; while (fgets(buf, sizeof(buf), fp)) { if (drop) { /* header line */ drop = 0; continue; } sscanf(buf, "%s %s ", bpq_name, dev_name); if (!strcmp(dev_name, ifname)) break; *bpq_name = *dev_name = 0; } fclose(fp); if (!*bpq_name) { LOGL2("Could not find bpqether device for %s in " "/proc/net/bpqether: %s", ifr.ifr_name, strerror(errno)); return -1; } LOGL1("found bpq device %s for %s\n", bpq_name, dev_name); skfd = socket(AF_INET, SOCK_DGRAM, 0); if (skfd < 0) { perror("socket()"); return -1; } memset(&ifr, 0, sizeof(ifr)); memcpy(ifr.ifr_name, bpq_name, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = 0; ifr.ifr_hwaddr.sa_family = PF_AX25; memcpy(ifr.ifr_hwaddr.sa_data, mycallsign, 7); if (ioctl(skfd, SIOCSIFHWADDR, &ifr) < 0) { perror("SIOCSIFHWADDR"); close(skfd); return -1; } strncpy(ifr.ifr_name, bpq_name, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = 0; ifr.ifr_mtu = 256; if (ioctl(skfd, SIOCSIFMTU, &ifr) < 0) perror("SIOSGIFMTU"); strncpy(ifr.ifr_name, bpq_name, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = 0; if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) { perror("SIOCGIFFLAGS"); ifr.ifr_flags = 0; } ifr.ifr_flags |= (IFF_UP); if (ioctl(skfd, SIOCSIFFLAGS, &ifr) < 0) { perror("SIOCSIFFLAGS"); err = -1; } close(skfd); return err; } ax25-apps/COPYING0000644000175000017500000004325413521356721012004 0ustar irlirl GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ax25-apps/pathnames.h0000644000175000017500000000234213521356721013073 0ustar irlirl #define CONF_AX25IPD_FILE AX25_SYSCONFDIR"/ax25ipd.conf" #define CONF_AX25ROUTED_FILE AX25_SYSCONFDIR"/ax25rtd.conf" #define DATA_AX25ROUTED_CTL_SOCK AX25_LOCALSTATEDIR"/ax25rtd/control" #define PROC_AX25_FILE "/proc/net/ax25" #define DATA_AX25ROUTED_AXRT_FILE AX25_LOCALSTATEDIR"/ax25rtd/ax25_route" #define DATA_AX25ROUTED_IPRT_FILE AX25_LOCALSTATEDIR"/ax25rtd/ip_route" #define PROC_IP_ROUTE_FILE "/proc/net/route" #define PROC_NR_NODES_FILE "/proc/net/nr_nodes" #define CONF_NODE_MOTD_FILE AX25_SYSCONFDIR"/node.motd" #define CONF_NODE_PERMS_FILE AX25_SYSCONFDIR"/node.perms" #define CONF_NODE_FILE AX25_SYSCONFDIR"/node.conf" #define DATA_MHEARD_FILE AX25_LOCALSTATEDIR"/mheard/mheard.dat" #define DATA_NODE_HELP_DIR "/usr/lib/ax25/node/help/" #define CONF_NODE_INFO_FILE AX25_SYSCONFDIR"/node.info" #define DATA_NODE_LOGIN_FILE AX25_LOCALSTATEDIR"/node/loggedin" #define PROC_AX25_CALLS_FILE "/proc/net/ax25_calls" #define MY_TALK "/usr/sbin/ttylinkd" #define MAIL_DELIVERY_AGENT "/usr/sbin/sendmail %s" #define DATA_PMS_LOGIN_FILE AX25_LOCALSTATEDIR"/pms/loggedin" #define PROC_AX25_ROUTE_FILE "/proc/net/ax25_route" #define CONF_PMS_MOTD_FILE AX25_SYSCONFDIR"/pms.motd" #define CONF_PMS_INFO_FILE AX25_SYSCONFDIR"/pms.info" ax25-apps/AUTHORS0000644000175000017500000000143413521356721012013 0ustar irlirlax25ipd: Michael Westerhof Michael Durrant VE3PNX D. Jeff Dionne VE3DJF Rob Mayfield VK5XXX/VK5ZEU Terry Dawson VK2KTJ ax25rtd: Joerg Reuter Llaus Kudielka OE1KIB listen: Phil Karn KA9Q Jeff White N0POY Jonathan Naylor G4KLX Heikki Hannikainen OH7LZB Alan Cox GW4PTS Jean-Paul for rose decoding Jeroen Vreeken for INP decoding call: Alexander Tietzel DG6XA Alan Cox GW4PTS Jonathan Naylor G4KLX Joerg Reuter DL1BKE Steve Henson G6IXS Mark Wahl Werner Almesberger ax25-apps/ax25mond/0000755000175000017500000000000013521356721012376 5ustar irlirlax25-apps/ax25mond/.gitignore0000644000175000017500000000004413521356721014364 0ustar irlirlax25mond ax25mond.8 ax25mond.conf.5 ax25-apps/ax25mond/Makefile.am0000644000175000017500000000325513521356721014437 0ustar irlirl sbin_PROGRAMS = ax25mond man_MANS = ax25mond.8 ax25mond.conf.5 EXTRA_DIST = ax25mond.man ax25mond.conf.man $(etcfiles) CLEANFILES = ax25mond.8 ax25mond.8.tmp ax25mond.conf.5 ax25mond.conf.5.tmp ax25mond.8: ax25mond.man name_ax25mond=$$(echo ax25mond | sed -e '$(transform)') \ name_Ax25mond=$$(echo $$name_ax25mond | sed -r 's@^(.)@\U\1\E@') && \ name_AX25MOND=$$(echo $$name_ax25mond | sed -r 's@^(.*)@\U\1\E@') && \ sed -e "s/@@@ax25mond@@@/$$name_ax25mond/g" \ -e "s/@@@Ax25mond@@@/$$name_Ax25mond/g" \ -e "s/@@@AX25MOND@@@/$$name_AX25MOND/g" \ $(srcdir)/ax25mond.man > ax25mond.8.tmp && \ mv ax25mond.8.tmp ax25mond.8; ax25mond.conf.5: ax25mond.conf.man name_ax25mond=$$(echo ax25mond | sed -e '$(transform)') \ name_Ax25mond=$$(echo $$name_ax25mond | sed -r 's@^(.)@\U\1\E@') && \ name_AX25MOND=$$(echo $$name_ax25mond | sed -r 's@^(.*)@\U\1\E@') && \ sed -e "s/@@@ax25mond@@@/$$name_ax25mond/g" \ -e "s/@@@Ax25mond@@@/$$name_Ax25mond/g" \ -e "s/@@@AX25MOND@@@/$$name_AX25MOND/g" \ $(srcdir)/ax25mond.conf.man > ax25mond.conf.5.tmp && \ mv ax25mond.conf.5.tmp ax25mond.conf.5; ax25mond_LDADD = $(AX25_LIB) ax25mond_SOURCES = \ ax25mond.c # Needed so that install is optional etcfiles = ax25mond.conf installconf: $(mkinstalldirs) $(DESTDIR)$(AX25_SYSCONFDIR) @list='$(etcfiles)'; for p in $$list; do \ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p"; \ $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p; \ done AX25_SYSCONFDIR=$(sysconfdir)/ax25 AX25_LOCALSTATEDIR=$(localstatedir)/ax25 AM_CPPFLAGS = -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" ax25-apps/ax25mond/ax25mond.conf.man0000644000175000017500000000136213521356721015456 0ustar irlirl.TH AX25MOND.CONF 5 "30 January 1999" Linux "Linux Programmer's Manual" .SH NAME ax25mond.conf \- Specify the sockets which @@@ax25mond@@@ will provide. .SH DESCRIPTION .LP The .B ax25mond.conf file specifies the internet- and unix-sockets which @@@ax25mond@@@(8) will provide. Example: .LP # Create an internet socket on port 1234 where all the .br # AX.25 traffic will be transmitted .br rxtx *:1234 .br # Create an unix socket where only the received AX.25 .br # traffic will be transmitted .br rx unix:/var/spool/ax25/monitor .br .LP You may specify up to 5 sockets (definable in ax25mond.c) and @@@ax25mond@@@ will accept up to 50 connects (also definable in ax25mond.c). .SH FILES .LP /etc/ax25/ax25mond.conf .SH "SEE ALSO" .BR @@@ax25mond@@@ (8). ax25-apps/ax25mond/ax25mond.c0000644000175000017500000002542713521356721014211 0ustar irlirl/* * ax25mond.c - Wrapper for offering AX.25 traffic to non-root processes * * Copyright (C) 1998-1999 Johann Hanne, DH3MB. All rights reserved. * * 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 received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA * 02110-1301, USA. */ /*--------------------------------------------------------------------------*/ #define CONFFILE "/etc/ax25/ax25mond.conf" #define MAX_SOCKETS 5 #define MAX_CONNECTS 50 /*--------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For older kernels */ #ifndef PF_PACKET #define PF_PACKET PF_INET #endif /*--------------------------------------------------------------------------*/ static union { struct sockaddr sa; struct sockaddr_in si; struct sockaddr_un su; } addr; /*--------------------------------------------------------------------------*/ static int sock_list[MAX_SOCKETS]; static char sock_monmode[MAX_SOCKETS]; static char sock_filename[MAX_SOCKETS][100]; static int sock_num = 0; static int conn_list[MAX_CONNECTS]; static struct sockaddr conn_addr[MAX_CONNECTS]; static socklen_t conn_addrlen[MAX_CONNECTS]; static char conn_monmode[MAX_CONNECTS]; static int conn_num = 0; static int highest_sock_fd; static int end = 0; /*--------------------------------------------------------------------------*/ /* from buildsaddr.c */ static struct sockaddr *build_sockaddr(const char *name, int *addrlen) { char *host_name; char *serv_name; char buf[1024]; memset(&addr, 0, sizeof(addr)); *addrlen = 0; host_name = strcpy(buf, name); serv_name = strchr(buf, ':'); if (!serv_name) return NULL; *serv_name++ = 0; if (!*host_name || !*serv_name) return NULL; if (!strcmp(host_name, "local") || !strcmp(host_name, "unix")) { addr.su.sun_family = AF_UNIX; *addr.su.sun_path = 0; strcat(addr.su.sun_path, serv_name); *addrlen = sizeof(struct sockaddr_un); return &addr.sa; } addr.si.sin_family = AF_INET; if (!strcmp(host_name, "*")) { addr.si.sin_addr.s_addr = INADDR_ANY; } else if (!strcmp(host_name, "loopback")) { addr.si.sin_addr.s_addr = inet_addr("127.0.0.1"); } else {addr.si.sin_addr.s_addr = inet_addr(host_name); if (addr.si.sin_addr.s_addr == -1) { struct hostent *hp = gethostbyname(host_name); endhostent(); if (!hp) return NULL; addr.si.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr; } } if (isdigit(*serv_name & 0xff)) { addr.si.sin_port = htons(atoi(serv_name)); } else { struct servent *sp = getservbyname(serv_name, NULL); endservent(); if (!sp) return NULL; addr.si.sin_port = sp->s_port; } *addrlen = sizeof(struct sockaddr_in); return &addr.sa; } /*--------------------------------------------------------------------------*/ static void add_socket(char *sockname, char monmode) { struct sockaddr *saddr; int saddrlen; if (sock_num == MAX_SOCKETS - 1) { fprintf(stderr, "WARNING: Too many sockets defined - only %d are " "allowed\n", MAX_SOCKETS); return; } if (!(saddr = build_sockaddr(sockname, &saddrlen))) { fprintf(stderr, "WARNING: Invalid socket name: \"%s\"\n", sockname); return; } if (saddr->sa_family == AF_UNIX) strcpy(sock_filename[sock_num], strchr(sockname, ':') + 1); else sock_filename[sock_num][0] = 0; sock_list[sock_num] = socket(saddr->sa_family, SOCK_STREAM, 0); if (sock_list[sock_num] < 0) { fprintf(stderr, "WARNING: Error opening socket \"%s\": %s\n", sockname, strerror(errno)); return; } if (bind(sock_list[sock_num], saddr, saddrlen) < 0) { fprintf(stderr, "WARNING: Error binding socket \"%s\": %s\n", sockname, strerror(errno)); return; } if (listen(sock_list[sock_num], 5) < 0) { fprintf(stderr, "WARNING: Error listening on socket \"%s\": %s\n", sockname, strerror(errno)); return; } fcntl(sock_list[sock_num], F_SETFL, O_NONBLOCK); if (sock_list[sock_num] > highest_sock_fd) highest_sock_fd = sock_list[sock_num]; sock_monmode[sock_num] = monmode; sock_num++; } /*--------------------------------------------------------------------------*/ static void close_sockets(void) { int i; for (i = 0; i < sock_num; i++) { close(sock_list[i]); if (sock_filename[i][0]) unlink(sock_filename[i]); } } /*--------------------------------------------------------------------------*/ static void quit_handler(int dummy) { end = 1; } /*--------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { fd_set monavail; int monrx_fd, monrxtx_fd; struct sockaddr monfrom; socklen_t monfromlen; fd_set conn_request; struct timeval tv; char buf[500]; int size; int i; struct ifreq ifr; FILE *conffile; char confline[100]; char *mode; char *sockname; if (argc > 1) { if (argc == 2 && (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0)) { printf("%s: Version " VERSION "\n\n", argv[0]); printf ("Copyright (C) 1998-1999 Johann Hanne, DH3MB. All rights reserved.\n" "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation; either version 2 of the License, or\n" "(at your option) any later version.\n\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); exit(1); } else { printf("Usage: %s [-v|--version]\n", argv[0]); exit(0); } } /* At first, read the configuration file */ if (!(conffile = fopen(CONFFILE, "r"))) { fprintf(stderr, "Unable to open " CONFFILE ".\n"); exit(1); } while (fgets(confline, 100, conffile)) { if (confline[0] == '#') /* Comment */ continue; confline[strlen(confline) - 1] = 0; /* Cut the LF */ if (!(sockname = strchr(confline, ' '))) { fprintf(stderr, "WARNING: The following configuration line includes " "one or more errors:\n%s\n", confline); continue; } *(sockname++) = 0; mode = confline; if (strcasecmp(mode, "RX") == 0) add_socket(sockname, 0); else if (strcasecmp(mode, "RXTX") == 0) add_socket(sockname, 1); else fprintf(stderr, "WARNING: Mode \"%s\" not supported\n", mode); } fclose(conffile); if (sock_num == 0) { fprintf(stderr, "FATAL: No usable socket found\n"); exit(1); } /* Fork into background */ if (fork()) exit(0); /* Close stdout, stderr and stdin */ fclose(stdout); fclose(stderr); fclose(stdin); /* Set some signal handlers */ signal(SIGPIPE, SIG_IGN); signal(SIGTERM, quit_handler); openlog("ax25mond", LOG_PID, LOG_DAEMON); /* Open AX.25 socket for monitoring RX traffic only */ if ((monrx_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_AX25))) < 0) { syslog(LOG_ERR, "Error opening monitor socket: %s\n", strerror(errno)); exit(1); } fcntl(monrx_fd, F_SETFL, O_NONBLOCK); /* Open AX.25 socket for monitoring RX and TX traffic */ if ((monrxtx_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) { syslog(LOG_ERR, "Error opening monitor socket: %s\n", strerror(errno)); exit(1); } fcntl(monrxtx_fd, F_SETFL, O_NONBLOCK); while (!end) { /* Look for incoming connects on all open sockets */ FD_ZERO(&conn_request); for (i = 0; i < sock_num; i++) FD_SET(sock_list[i], &conn_request); tv.tv_sec = 0; tv.tv_usec = 0; select(highest_sock_fd + 1, &conn_request, NULL, NULL, &tv); for (i = 0; i < sock_num; i++) if (FD_ISSET(sock_list[i], &conn_request)) { conn_list[conn_num] = accept(sock_list[i], &conn_addr [conn_num], &conn_addrlen [conn_num]); conn_monmode[conn_num] = sock_monmode[i]; conn_num++; } /* Check if there is new data on the RX-only monitor socket */ FD_ZERO(&monavail); FD_SET(monrx_fd, &monavail); tv.tv_sec = 0; tv.tv_usec = 10; select(monrx_fd + 1, &monavail, NULL, NULL, &tv); if (FD_ISSET(monrx_fd, &monavail)) { monfromlen = sizeof(monfrom); size = recvfrom(monrx_fd, buf, sizeof(buf), 0, &monfrom, &monfromlen); /* Send the packet to all connected sockets */ for (i = 0; i < conn_num; i++) { if (conn_monmode[i] == 0) if (send (conn_list[i], buf, size, 0) < 0) if (errno != EAGAIN) { syslog(LOG_ERR, "Error sending monitor data: %s\n", strerror (errno)); close(conn_list [i]); conn_list[i] = conn_list [conn_num - 1]; conn_num--; } } } /* Check if there is new data on the RX+TX monitor socket */ FD_ZERO(&monavail); FD_SET(monrxtx_fd, &monavail); tv.tv_sec = 0; tv.tv_usec = 10; select(monrxtx_fd + 1, &monavail, NULL, NULL, &tv); if (FD_ISSET(monrxtx_fd, &monavail)) { monfromlen = sizeof(monfrom); size = recvfrom(monrxtx_fd, buf, sizeof(buf), 0, &monfrom, &monfromlen); /* Check, if we have received a AX.25-packet */ strcpy(ifr.ifr_name, monfrom.sa_data); ioctl(monrxtx_fd, SIOCGIFHWADDR, &ifr); if (ifr.ifr_hwaddr.sa_family == AF_AX25) /* Send the packet to all connected sockets */ for (i = 0; i < conn_num; i++) { if (conn_monmode[i] == 1) if (send (conn_list[i], buf, size, 0) < 0) if (errno != EAGAIN) { syslog (LOG_ERR, "Error sending monitor data: %s\n", strerror (errno)); close (conn_list [i]); conn_list [i] = conn_list [conn_num - 1]; conn_num--; } } } } for (i = 0; i < conn_num; i++) close(conn_list[i]); close_sockets(); close(monrx_fd); close(monrxtx_fd); closelog(); return 0; } ax25-apps/ax25mond/ax25mond.man0000644000175000017500000000122613521356721014531 0ustar irlirl.TH @@@AX25MOND@@@ 8 "30 January 1999" Linux "Linux System Managers Manual" .SH NAME @@@ax25mond@@@ \- dump the AX.25 network traffic and and provide sockets where the received data will be retransmitted .SH SYNOPSIS .B @@@ax25mond@@@ [-v|--version] .SH DESCRIPTION .LP .B Ax25mond will first read it's configuration file /etc/ax25/ax25mond.conf. It will then wait for connects on the sockets specified in this file. When connected, it will transmit all data which it has received on the AX.25 monitor socket. .SH OPTIONS .TP 15 .BI "\-v, \-\-version" Displays the version number and some copyright information. .SH AUTHOR Johann Hanne, DH3MB ax25-apps/ax25mond/ax25mond.conf0000644000175000017500000000013413521356721014700 0ustar irlirl# Create an internet socket on port 1234 and transmit the whole # AX.25 traffic rxtx *:1234 ax25-apps/listen/0000755000175000017500000000000013521356721012237 5ustar irlirlax25-apps/listen/listen.c0000644000175000017500000002675313521356721013716 0ustar irlirl#include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GLIBC__ #include #else #include #endif #include #include #include #include "listen.h" static struct timeval t_recv; static int tflag = 0; static int32_t thiszone; /* seconds offset from gmt to local time */ static int sigint; static int sock; static void display_port(char *dev) { char *port; port = ax25_config_get_name(dev); if (port == NULL) port = dev; lprintf(T_PORT, "%s: ", port); } /* from tcpdump util.c */ /* * Format the timestamp */ static char * ts_format(unsigned int sec, unsigned int usec) { static char buf[sizeof("00:00:00.000000")]; unsigned int hours, minutes, seconds; hours = sec / 3600; minutes = (sec % 3600) / 60; seconds = sec % 60; /* * The real purpose of these checks is to let GCC figure out the * value range of all variables thus avoid bogus warnings. For any * halfway modern GCC the checks will be optimized away. */ if (hours >= 60) unreachable(); if (minutes >= 60) unreachable(); if (seconds >= 60) unreachable(); if (usec >= 1000000) unreachable(); snprintf(buf, sizeof(buf), "%02d:%02d:%02d.%06u", hours, minutes, seconds, usec); return buf; } /* * Print the timestamp */ static void ts_print(const struct timeval *tvp) { int s; struct tm *tm; time_t Time; static unsigned b_sec; static unsigned b_usec; int d_usec; int d_sec; switch (tflag) { case 0: /* Default */ s = (tvp->tv_sec + thiszone) % 86400; (void)lprintf(T_TIMESTAMP, "%s ", ts_format(s, tvp->tv_usec)); break; case 1: /* No time stamp */ break; case 2: /* Unix timeval style */ (void)lprintf(T_TIMESTAMP, "%u.%06u ", (unsigned)tvp->tv_sec, (unsigned)tvp->tv_usec); break; case 3: /* Microseconds since previous packet */ case 5: /* Microseconds since first packet */ if (b_sec == 0) { /* init timestamp for first packet */ b_usec = tvp->tv_usec; b_sec = tvp->tv_sec; } d_usec = tvp->tv_usec - b_usec; d_sec = tvp->tv_sec - b_sec; while (d_usec < 0) { d_usec += 1000000; d_sec--; } (void)lprintf(T_TIMESTAMP, "%s ", ts_format(d_sec, d_usec)); if (tflag == 3) { /* set timestamp for last packet */ b_sec = tvp->tv_sec; b_usec = tvp->tv_usec; } break; case 4: /* Default + Date*/ s = (tvp->tv_sec + thiszone) % 86400; Time = (tvp->tv_sec + thiszone) - s; tm = gmtime (&Time); if (!tm) lprintf(T_TIMESTAMP, "Date fail "); else lprintf(T_TIMESTAMP, "%04d-%02d-%02d %s ", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, ts_format(s, tvp->tv_usec)); break; } } void display_timestamp(void) { ts_print(&t_recv); } /* from tcpdump gmtlocal.c */ static int32_t gmt2local(time_t t) { int dt, dir; struct tm *gmt, *loc; struct tm sgmt; if (t == 0) t = time(NULL); gmt = &sgmt; *gmt = *gmtime(&t); loc = localtime(&t); dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + (loc->tm_min - gmt->tm_min) * 60; /* * If the year or julian day is different, we span 00:00 GMT * and must add or subtract a day. Check the year first to * avoid problems when the julian day wraps. */ dir = loc->tm_year - gmt->tm_year; if (dir == 0) dir = loc->tm_yday - gmt->tm_yday; dt += dir * 24 * 60 * 60; return dt; } static void handle_sigint(int signal) { sigint++; close(sock); /* disturb blocking recvfrom */ sock = -1; } #define ASCII 0 #define HEX 1 #define READABLE 2 #define BUFSIZE 1500 int main(int argc, char **argv) { unsigned char buffer[BUFSIZE]; int dumpstyle = ASCII; int size; int s; char *port = NULL, *dev = NULL; struct sockaddr sa; socklen_t asize = sizeof(sa); struct ifreq ifr; int proto = ETH_P_AX25; int exit_code = EXIT_SUCCESS; while ((s = getopt(argc, argv, "8achip:rtv")) != -1) { switch (s) { case '8': sevenbit = 0; break; case 'a': proto = ETH_P_ALL; break; case 'c': color = 1; break; case 'h': dumpstyle = HEX; break; case 'i': ibmhack = 1; break; case 'p': port = optarg; break; case 'r': dumpstyle = READABLE; break; case 't': tflag++; break; case 'v': printf("listen: %s\n", VERSION); return 0; case ':': fprintf(stderr, "listen: option -p needs a port name\n"); return 1; case '?': fprintf(stderr, "Usage: listen [-8] [-a] [-c] [-h] [-i] [-p port] [-r] [-t..] [-v]\n"); return 1; } } switch (tflag) { case 0: /* Default */ case 4: /* Default + Date*/ thiszone = gmt2local(0); break; case 1: /* No time stamp */ case 2: /* Unix timeval style */ case 3: /* Microseconds since previous packet */ case 5: /* Microseconds since first packet */ break; default: /* Not supported */ fprintf(stderr, "listen: only -t, -tt, -ttt, -tttt and -ttttt are supported\n"); return 1; } if (ax25_config_load_ports() == 0) fprintf(stderr, "listen: no AX.25 port data configured\n"); if (port != NULL) { dev = ax25_config_get_dev(port); if (dev == NULL) { fprintf(stderr, "listen: invalid port name - %s\n", port); return 1; } } sock = socket(PF_PACKET, SOCK_PACKET, htons(proto)); if (sock == -1) { perror("socket"); return 1; } if (color) { color = initcolor(); /* Initialize color support */ if (!color) printf("Could not initialize color support.\n"); } setservent(1); while (!sigint) { asize = sizeof(sa); signal(SIGINT, handle_sigint); signal(SIGTERM, handle_sigint); size = recvfrom(sock, buffer, sizeof(buffer), 0, &sa, &asize); if (size == -1) { /* * Signals are cared for by the handler, and we * don't want to abort on SIGWINCH */ if (errno == EINTR) { refresh(); continue; } else if (!(errno == EBADF && sigint)) { perror("recv"); exit_code = errno; } break; } gettimeofday(&t_recv, NULL); signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); if (sock == -1 || sigint) break; if (dev != NULL && strcmp(dev, sa.sa_data) != 0) continue; if (proto == ETH_P_ALL) { strcpy(ifr.ifr_name, sa.sa_data); signal(SIGINT, handle_sigint); signal(SIGTERM, handle_sigint); if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { if (!(errno == EBADF && sigint)) { perror("SIOCGIFHWADDR"); exit_code = errno; break; } } signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); if (sock == -1 || sigint) break; if (ifr.ifr_hwaddr.sa_family == AF_AX25) { if (size > 2 && *buffer == 0xcc) { /* IP packets from the ax25 de-segmenter are seen on socket "PF_PACKET, SOCK_PACKET, ETH_P_ALL" without AX.25 header (just the IP-frame), prefixed by 0xcc (AX25_P_IP). It's unclear why in the kernel code this happens (unsegmentet AX25 PID AX25_P_IP have not this behavior). We have already displayed all the segments and like to ignore this data. AX.25 packets start with a kiss byte (buffer[0]); ax25_dump() looks for it. There's no kiss command 0xcc defined; kiss bytes are checked against & 0xf (= 0x0c), which is also not defined. Kiss commands may have one argument. => We can make safely make the assumption for first byte == 0xcc and length > 2, that we safeley can detect those IP frames, and then ignore it. */ continue; } display_port(sa.sa_data); ki_dump(buffer, size, dumpstyle); /* lprintf(T_DATA, "\n"); */ } } else { display_port(sa.sa_data); ki_dump(buffer, size, dumpstyle); /* lprintf(T_DATA, "\n"); */ } if (color) refresh(); } if (color) endwin(); return exit_code; } static void ascii_dump(unsigned char *data, int length) { char c; int i, j; char buf[100]; for (i = 0; length > 0; i += 64) { sprintf(buf, "%04X ", i); for (j = 0; j < 64 && length > 0; j++) { c = *data++; length--; if ((c != '\0') && (c != '\n')) strncat(buf, &c, 1); else strcat(buf, "."); } lprintf(T_DATA, "%s\n", buf); } } static void readable_dump(unsigned char *data, int length) { unsigned char c; int i; int cr = 1; char buf[BUFSIZE]; for (i = 0; length > 0; i++) { c = *data++; length--; switch (c) { case 0x00: buf[i] = ' '; case 0x0A: /* hum... */ case 0x0D: if (cr) buf[i] = '\n'; else i--; break; default: buf[i] = c; } cr = (buf[i] != '\n'); } if (cr) buf[i++] = '\n'; buf[i] = '\0'; lprintf(T_DATA, "%s", buf); } static void hex_dump(unsigned char *data, int length) { unsigned char *data2; int i, j, length2; unsigned char c; char buf[4], hexd[49], ascd[17]; length2 = length; data2 = data; for (i = 0; length > 0; i += 16) { hexd[0] = '\0'; for (j = 0; j < 16; j++) { c = *data2++; length2--; if (length2 >= 0) sprintf(buf, "%2.2X ", c); else strcpy(buf, " "); strcat(hexd, buf); } ascd[0] = '\0'; for (j = 0; j < 16 && length > 0; j++) { c = *data++; length--; sprintf(buf, "%c", ((c != '\0') && (c != '\n')) ? c : '.'); strcat(ascd, buf); } lprintf(T_DATA, "%04X %s | %s\n", i, hexd, ascd); } } void data_dump(void *data, int length, int dumpstyle) { switch (dumpstyle) { case READABLE: readable_dump(data, length); break; case HEX: hex_dump(data, length); break; default: ascii_dump(data, length); } } int get16(unsigned char *cp) { int x; x = *cp++; x <<= 8; x |= *cp++; return x; } int get32(unsigned char *cp) { int x; x = *cp++; x <<= 8; x |= *cp++; x <<= 8; x |= *cp++; x <<= 8; x |= *cp; return x; } ax25-apps/listen/.gitignore0000644000175000017500000000002013521356721014217 0ustar irlirllisten listen.1 ax25-apps/listen/rspfdump.c0000644000175000017500000000325413521356721014247 0ustar irlirl#include #include #include #include #include #include "listen.h" void rspf_dump(unsigned char *data, int length) { int bptr, nodes, links, adjs; lprintf(T_IPHDR, "RSPF: version %u ", data[0]); switch (data[1]) { case 3: /* RRH */ lprintf(T_IPHDR, "type RRH seq %#04x flags %d\n", ntohs(*((u_short *) (&data[8]))), data[10]); bptr = 11; while (bptr < length) lprintf(T_IPHDR, "%c", data[bptr++]); lprintf(T_IPHDR, "\n"); break; case 1: /*Routing update */ lprintf(T_IPHDR, "type ROUTING UPDATE "); lprintf(T_IPHDR, "fragment %u frag total %u sync %u #nodes %u env_id %u\n", data[2], data[3], data[6], data[7], ntohs(*((u_short *) (&data[8])))); bptr = data[6] + 6; nodes = data[7]; while (nodes-- && (length - bptr) > 7) { lprintf(T_DATA, " Reporting Router: %s Seq %u Subseq %u #links %u\n", inet_ntoa(* ((struct in_addr *) (&data[bptr]))), ntohs(*((u_short *) (&data[bptr + 4]))), data[bptr + 6], data[bptr + 7]); links = data[bptr + 7]; bptr += 8; while (links-- && (length - bptr) > 4) { lprintf(T_DATA, " horizon %u ERP factor %u cost %u #adjacencies %u\n", data[bptr], data[bptr + 1], data[bptr + 2], data[bptr + 3]); adjs = data[bptr + 3]; bptr += 4; while (adjs-- && (length - bptr) > 4) { lprintf(T_DATA, " %s/%d \n", inet_ntoa(* ((struct in_addr *) (&data[bptr + 1]))), data[bptr] & 0x3f); bptr += 5; } } } break; default: lprintf(T_ERROR, "Unknown packet type %d\n", data[1]); break; } } ax25-apps/listen/tcpdump.c0000644000175000017500000000450613521356721014064 0ustar irlirl/* TCP header tracing routines * Copyright 1991 Phil Karn, KA9Q */ #include #include #include "listen.h" #define FIN 0x01 #define SYN 0x02 #define RST 0x04 #define PSH 0x08 #define ACK 0x10 #define URG 0x20 #define CE 0x40 /* TCP options */ #define EOL_KIND 0 #define NOOP_KIND 1 #define MSS_KIND 2 #define MSS_LENGTH 4 #define TCPLEN 20 #define max(a,b) ((a) > (b) ? (a) : (b)) /* Dump a TCP segment header. Assumed to be in network byte order */ void tcp_dump(unsigned char *data, int length, int hexdump) { int source, dest; int seq; int ack; int flags; int wnd; int up; int hdrlen; int mss = 0; source = get16(data + 0); dest = get16(data + 2); seq = get32(data + 4); ack = get32(data + 8); hdrlen = (data[12] & 0xF0) >> 2; flags = data[13]; wnd = get16(data + 14); up = get16(data + 18); lprintf(T_PROTOCOL, "TCP:"); lprintf(T_TCPHDR, " %s->", servname(source, "tcp")); lprintf(T_TCPHDR, "%s Seq x%x", servname(dest, "tcp"), seq); if (flags & ACK) lprintf(T_TCPHDR, " Ack x%x", ack); if (flags & CE) lprintf(T_TCPHDR, " CE"); if (flags & URG) lprintf(T_TCPHDR, " URG"); if (flags & ACK) lprintf(T_TCPHDR, " ACK"); if (flags & PSH) lprintf(T_TCPHDR, " PSH"); if (flags & RST) lprintf(T_TCPHDR, " RST"); if (flags & SYN) lprintf(T_TCPHDR, " SYN"); if (flags & FIN) lprintf(T_TCPHDR, " FIN"); lprintf(T_TCPHDR, " Wnd %d", wnd); if (flags & URG) lprintf(T_TCPHDR, " UP x%x", up); /* Process options, if any */ if (hdrlen > TCPLEN && length >= hdrlen) { unsigned char *cp = data + TCPLEN; int i = hdrlen - TCPLEN; int kind, optlen; while (i > 0) { kind = *cp++; /* Process single-byte options */ switch (kind) { case EOL_KIND: i--; cp++; break; case NOOP_KIND: i--; cp++; continue; } /* All other options have a length field */ optlen = *cp++; /* Process valid multi-byte options */ switch (kind) { case MSS_KIND: if (optlen == MSS_LENGTH) mss = get16(cp); break; } optlen = max(2, optlen); /* Enforce legal minimum */ i -= optlen; cp += optlen - 2; } } if (mss != 0) lprintf(T_TCPHDR, " MSS %d", mss); length -= hdrlen; data += hdrlen; if (length > 0) { lprintf(T_TCPHDR, " Data %d\n", length); data_dump(data, length, hexdump); return; } lprintf(T_TCPHDR, "\n"); } ax25-apps/listen/ripdump.c0000644000175000017500000001122413521356721014063 0ustar irlirl/* RIP packet tracing * Copyright 1991 Phil Karn, KA9Q * * Changes Copyright (c) 1993 Jeff White - N0POY, All Rights Reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Rehack for RIP-2 (RFC1388) by N0POY 4/1993 * * Beta release 11/10/93 V0.91 * * 2/19/94 release V1.0 * */ #include #include "listen.h" #define RIP_VERSION_2 2 #define RIP_VERSION_98 98 #define RIP_HEADER 4 #define RIP_ENTRY 20 #define RIP_AF_INET 2 /* IP Family */ #define RIPCMD_REQUEST 1 /* want info */ #define RIPCMD_RESPONSE 2 /* responding to request */ #define RIP98_ENTRY 6 static struct mask_struct { unsigned int mask; unsigned int width; } 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 unsigned int mask2width(unsigned int mask) { struct mask_struct *t; for (t = mask_table; t->mask != 0; t++) if (mask == t->mask) return t->width; return 0; } void rip_dump(unsigned char *data, int length) { int i; int cmd; int version; int domain; char ipaddmask[25]; lprintf(T_PROTOCOL, "RIP: "); cmd = data[0]; version = data[1]; domain = get16(data + 2); length -= RIP_HEADER; data += RIP_HEADER; switch (cmd) { case RIPCMD_REQUEST: lprintf(T_IPHDR, "REQUEST"); break; case RIPCMD_RESPONSE: lprintf(T_IPHDR, "RESPONSE"); break; default: lprintf(T_IPHDR, " cmd %u", cmd); break; } switch (version) { case RIP_VERSION_98: /* IPGATE * */ lprintf(T_IPHDR, " vers %u entries %u\n", version, length / RIP98_ENTRY); i = 0; while (length >= RIP98_ENTRY) { sprintf(ipaddmask, "%u.%u.%u.%u/%-2u", data[0], data[1], data[2], data[3], data[4]); lprintf(T_ADDR, "%-16s %-3u ", ipaddmask, data[5]); if ((++i % 3) == 0) lprintf(T_IPHDR, "\n"); length -= RIP98_ENTRY; data += RIP98_ENTRY; } if ((i % 3) != 0) lprintf(T_IPHDR, "\n"); break; default: lprintf(T_IPHDR, " vers %u entries %u domain %u:\n", version, length / RIP_ENTRY, domain); i = 0; while (length >= RIP_ENTRY) { if (get16(data + 0) != RIP_AF_INET) { /* Skip non-IP addresses */ length -= RIP_ENTRY; data += RIP_ENTRY; continue; } if (version >= RIP_VERSION_2) { sprintf(ipaddmask, "%u.%u.%u.%u/%-4d", data[4], data[5], data[6], data[7], mask2width(get32(data + 8))); } else { sprintf(ipaddmask, "%u.%u.%u.%u/??", data[4], data[5], data[6], data[7]); } lprintf(T_ADDR, "%-20s%-3u", ipaddmask, get32(data + 16)); if ((++i % 3) == 0) lprintf(T_IPHDR, "\n"); length -= RIP_ENTRY; data += RIP_ENTRY; } if ((i % 3) != 0) lprintf(T_IPHDR, "\n"); break; } } ax25-apps/listen/utils.c0000644000175000017500000000632213521356721013546 0ustar irlirl/* * Copyright 1996, 1997 Heikki Hannikainen OH7LZB * * Portions and ideas (like the ibm character mapping) from * Tomi Manninen OH2BNS */ #include #include #include #include #include #include #include #include "listen.h" int color = 0; /* Colorized? */ int sevenbit = 1; /* Are we on a 7-bit terminal? */ int ibmhack = 0; /* IBM mapping? */ /* mapping of IBM codepage 437 chars 128-159 to ISO latin1 equivalents * (158 and 159 are mapped to space) */ static unsigned char ibm_map[32] = { 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 162, 163, 165, 32, 32 }; /* * Printf in Technicolor (TM) (available in selected theatres only) */ void lprintf(int dtype, char *fmt, ...) { va_list args; char str[1024]; chtype ch; char *p; va_start(args, fmt); vsnprintf(str, 1024, fmt, args); va_end(args); if (color) { for (p = str; *p != '\0'; p++) { ch = *p; if (sevenbit && ch > 127) ch = '.'; if ((ch > 127 && ch < 160) && ibmhack) ch = ibm_map[ch - 128] | A_BOLD; else if ((ch < 32) && (ch != '\n')) ch = (ch + 64) | A_REVERSE; if ((dtype == T_ADDR) || (dtype == T_PROTOCOL) || (dtype == T_AXHDR) || (dtype == T_IPHDR) || (dtype == T_ROSEHDR) || (dtype == T_PORT) || (dtype == T_TIMESTAMP)) ch |= A_BOLD; ch |= COLOR_PAIR(dtype); addch(ch); } } else { for (p = str; *p != '\0'; p++) if ((*p < 32 && *p != '\n') || (*p > 126 && (unsigned char) *p < 160 && sevenbit)) *p = '.'; if (fputs(str, stdout) == -1) exit(1); if (fflush(stdout) == -1) exit(1); } } int initcolor(void) { initscr(); /* Start ncurses */ if (!has_colors()) { endwin(); fprintf(stderr, "Your terminal does not support color\n"); exit(1); } start_color(); /* Initialize color support */ refresh(); /* Clear screen */ noecho(); /* Don't echo */ wattrset(stdscr, 0); /* Clear attributes */ scrollok(stdscr, TRUE); /* Like a scrolling Stone... */ leaveok(stdscr, TRUE); /* Cursor position doesn't really matter */ idlok(stdscr, TRUE); /* Use hardware ins/del of the terminal */ nodelay(stdscr, TRUE); /* Make getch() nonblocking */ /* Pick colors for each type */ init_pair(T_PORT, COLOR_GREEN, COLOR_BLACK); init_pair(T_DATA, COLOR_WHITE, COLOR_BLACK); init_pair(T_ERROR, COLOR_RED, COLOR_BLACK); init_pair(T_PROTOCOL, COLOR_CYAN, COLOR_BLACK); init_pair(T_AXHDR, COLOR_WHITE, COLOR_BLACK); init_pair(T_IPHDR, COLOR_WHITE, COLOR_BLACK); init_pair(T_ADDR, COLOR_GREEN, COLOR_BLACK); init_pair(T_ROSEHDR, COLOR_WHITE, COLOR_BLACK); init_pair(T_TIMESTAMP, COLOR_YELLOW, COLOR_BLACK); init_pair(T_KISS, COLOR_MAGENTA, COLOR_BLACK); init_pair(T_BPQ, COLOR_MAGENTA, COLOR_BLACK); init_pair(T_TCPHDR, COLOR_BLUE, COLOR_BLACK); init_pair(T_FLEXNET, COLOR_BLUE, COLOR_BLACK); init_pair(T_OPENTRAC, COLOR_YELLOW, COLOR_BLACK); return 1; } char *servname(int port, char *proto) { struct servent *serv; static char str[16]; if ((serv = getservbyport(htons(port), proto))) strncpy(str, serv->s_name, 16); else snprintf(str, 16, "%i", port); return str; } ax25-apps/listen/Makefile.am0000644000175000017500000000214113521356721014271 0ustar irlirl installconf: bin_PROGRAMS = listen man_MANS = listen.1 EXTRA_DIST = listen.man CLEANFILES = listen.1 listen.1.tmp listen.1 : listen.man name_call=$$(echo call | sed -e '$(transform)') \ name_Call=$$(echo $$name_call | sed -r 's@^(.)@\U\1\E@') && \ name_CALL=$$(echo $$name_call | sed -r 's@^(.*)@\U\1\E@') && \ name_listen=$$(echo listen | sed -e '$(transform)') \ name_Listen=$$(echo $$name_listen | sed -r 's@^(.)@\U\1\E@') && \ name_LISTEN=$$(echo $$name_listen | sed -r 's@^(.*)@\U\1\E@') &&\ sed -e "s/@@@call@@@/$$name_call/g" \ -e "s/@@@Call@@@/$$name_Call/g" \ -e "s/@@@CALL@@@/$$name_CALL/g" \ -e "s/@@@listen@@@/$$name_listen/g" \ -e "s/@@@Listen@@@/$$name_Listen/g" \ -e "s/@@@LISTEN@@@/$$name_LISTEN/g" \ $(srcdir)/listen.man > listen.1.tmp && \ mv listen.1.tmp listen.1; LDADD = $(AX25_LIB) $(NCURSES_LIB) listen_SOURCES = \ listen.c \ listen.h \ kissdump.c \ ax25dump.c \ nrdump.c \ arpdump.c \ ipdump.c \ icmpdump.c \ udpdump.c \ tcpdump.c \ rspfdump.c \ ripdump.c \ rosedump.c \ flexnetdump.c \ opentracdump.c \ utils.c ax25-apps/listen/udpdump.c0000644000175000017500000000142313521356721014061 0ustar irlirl/* UDP packet tracing * Copyright 1991 Phil Karn, KA9Q */ #include #include "listen.h" #define RIP_PORT 520 #define UDPHDR 8 /* Dump a UDP header */ void udp_dump(unsigned char *data, int length, int hexdump) { int hdr_length; int source; int dest; hdr_length = get16(data + 4); source = get16(data + 0); dest = get16(data + 2); lprintf(T_PROTOCOL, "UDP:"); lprintf(T_TCPHDR, " len %d %s->", hdr_length, servname(source, "udp")); lprintf(T_TCPHDR, "%s", servname(dest, "udp")); if (hdr_length > UDPHDR) { length -= UDPHDR; data += UDPHDR; switch (dest) { case RIP_PORT: lprintf(T_TCPHDR, "\n"); rip_dump(data, length); break; default: lprintf(T_TCPHDR, " Data %d\n", length); data_dump(data, length, hexdump); break; } } } ax25-apps/listen/rosedump.c0000644000175000017500000002472413521356721014252 0ustar irlirl/* * Copyright 1996 Jonathan Naylor G4KLX * * Added ROSE new facilities : Jean-Paul ROUBELAT 04-1998 * Added ROSE multi-digi : Jean-Paul ROUBELAT 05-1998 * Added ROSE PID transport : Jean-Paul ROUBELAT 07-1998 */ #include #include #include "listen.h" #define PID_SEGMENT 0x08 #define PID_ARP 0xCD #define PID_NETROM 0xCF #define PID_IP 0xCC #define PID_X25 0x01 #define PID_TEXNET 0xC3 #define PID_FLEXNET 0xCE #define PID_NO_L3 0xF0 #define ROSE_ADDR_LEN 5 #define CALL_REQUEST 0x0B #define CALL_ACCEPTED 0x0F #define CLEAR_REQUEST 0x13 #define CLEAR_CONFIRMATION 0x17 #define INTERRUPT 0x23 #define INTERRUPT_CONFIRMATION 0x27 #define RESET_REQUEST 0x1B #define RESET_CONFIRMATION 0x1F #define RESTART_REQUEST 0xFB #define RESTART_CONFIRMATION 0xFF #define REGISTRATION_REQUEST 0xF3 #define REGISTRATION_CONFIRMATION 0xF7 #define DIAGNOSTIC 0xF1 #define RR 0x01 #define RNR 0x05 #define REJ 0x09 #define DATA 0x00 #define QBIT 0x80 #define DBIT 0x40 #define MBIT 0x10 #define AX25_HBIT 0x80 static char *dump_x25_addr(unsigned char *); static char *clear_code(unsigned char); static char *reset_code(unsigned char); static char *restart_code(unsigned char); static void facility(unsigned char *, int len); void rose_dump(unsigned char *data, int length, int hexdump) { unsigned int len, hlen; unsigned int lci = ((unsigned) (data[0] & 0x0F) << 8) + data[1]; lprintf(T_ROSEHDR, "X.25: LCI %3.3X : ", lci); switch (data[2]) { case CALL_REQUEST: len = 4; hlen = (((data[3] >> 4) & 0x0F) + 1) / 2; hlen += (((data[3] >> 0) & 0x0F) + 1) / 2; len += hlen; data += len; length -= len; lprintf(T_ROSEHDR, "CALL REQUEST - "); if (length) { unsigned int flen = data[0] + 1; facility(data, length); length -= flen; data += flen; if (length > 0) data_dump(data, length, 1); } else { lprintf(T_ROSEHDR, "\n"); } return; case CALL_ACCEPTED: lprintf(T_ROSEHDR, "CALL ACCEPTED\n"); return; case CLEAR_REQUEST: lprintf(T_ROSEHDR, "CLEAR REQUEST - Cause %s - Diag %d\n", clear_code(data[3]), data[4]); if (length > 6) { facility(data + 6, length - 6); } return; case CLEAR_CONFIRMATION: lprintf(T_ROSEHDR, "CLEAR CONFIRMATION\n"); return; case DIAGNOSTIC: lprintf(T_ROSEHDR, "DIAGNOSTIC - Diag %d\n", data[3]); return; case INTERRUPT: lprintf(T_ROSEHDR, "INTERRUPT\n"); data_dump(data + 3, length - 3, hexdump); return; case INTERRUPT_CONFIRMATION: lprintf(T_ROSEHDR, "INTERRUPT CONFIRMATION\n"); return; case RESET_REQUEST: lprintf(T_ROSEHDR, "RESET REQUEST - Cause %s - Diag %d\n", reset_code(data[3]), data[4]); return; case RESET_CONFIRMATION: lprintf(T_ROSEHDR, "RESET CONFIRMATION\n"); return; case RESTART_REQUEST: lprintf(T_ROSEHDR, "RESTART REQUEST - Cause %s - Diag %d\n", restart_code(data[3]), data[4]); return; case RESTART_CONFIRMATION: lprintf(T_ROSEHDR, "RESTART CONFIRMATION\n"); return; case REGISTRATION_REQUEST: lprintf(T_ROSEHDR, "REGISTRATION REQUEST\n"); return; case REGISTRATION_CONFIRMATION: lprintf(T_ROSEHDR, "REGISTRATION CONFIRMATION\n"); return; } if ((data[2] & 0x01) == DATA) { lprintf(T_ROSEHDR, "DATA R%d S%d %s%s%s", (data[2] >> 5) & 0x07, (data[2] >> 1) & 0x07, (data[0] & QBIT) ? "Q" : "", (data[0] & DBIT) ? "D" : "", (data[2] & MBIT) ? "M" : ""); if ((length >= 5) && (data[0] & QBIT) && (data[3] == 0x7f)) { /* pid transport */ int pid = data[4]; data += 5; length -= 5; switch (pid) { case PID_SEGMENT: lprintf(T_ROSEHDR," len %d\n", length - 5); data_dump(data, length, hexdump); break; case PID_ARP: lprintf(T_ROSEHDR," pid=ARP len %d\n", length - 5); arp_dump(data, length); break; case PID_NETROM: lprintf(T_ROSEHDR," pid=NET/ROM len %d\n", length - 5); netrom_dump(data, length, hexdump, 0); break; case PID_IP: lprintf(T_ROSEHDR," pid=IP len %d\n", length - 5); ip_dump(data, length, hexdump); break; case PID_X25: lprintf(T_ROSEHDR, " pid=X.25 len %d\n", length - 5); rose_dump(data, length, hexdump); break; case PID_TEXNET: lprintf(T_ROSEHDR, " pid=TEXNET len %d\n", length - 5); data_dump(data, length, hexdump); break; case PID_FLEXNET: lprintf(T_ROSEHDR, " pid=FLEXNET len %d\n", length - 5); flexnet_dump(data, length, hexdump); break; case PID_NO_L3: lprintf(T_ROSEHDR, " pid=Text len %d\n", length - 5); data_dump(data, length, hexdump); break; default: lprintf(T_ROSEHDR, " pid=0x%x len %d\n", pid, length - 5); data_dump(data, length, hexdump); break; } } else { lprintf(T_ROSEHDR, " len %d\n", length - 3); data_dump(data + 3, length - 3, hexdump); } return; } switch (data[2] & 0x1F) { case RR: lprintf(T_ROSEHDR, "RR R%d\n", (data[2] >> 5) & 0x07); return; case RNR: lprintf(T_ROSEHDR, "RNR R%d\n", (data[2] >> 5) & 0x07); return; case REJ: lprintf(T_ROSEHDR, "REJ R%d\n", (data[2] >> 5) & 0x07); return; } lprintf(T_ROSEHDR, "UNKNOWN\n"); data_dump(data, length, 1); } static char *clear_code(unsigned char code) { static char buffer[25]; if (code == 0x00 || (code & 0x80) == 0x80) return "DTE Originated"; if (code == 0x01) return "Number Busy"; if (code == 0x09) return "Out Of Order"; if (code == 0x11) return "Remote Procedure Error"; if (code == 0x19) return "Reverse Charging Acceptance Not Subscribed"; if (code == 0x21) return "Incompatible Destination"; if (code == 0x29) return "Fast Select Acceptance Not Subscribed"; if (code == 0x39) return "Destination Absent"; if (code == 0x03) return "Invalid Facility Requested"; if (code == 0x0B) return "Access Barred"; if (code == 0x13) return "Local Procedure Error"; if (code == 0x05) return "Network Congestion"; if (code == 0x0D) return "Not Obtainable"; if (code == 0x15) return "RPOA Out Of Order"; sprintf(buffer, "Unknown %02X", code); return buffer; } static char *reset_code(unsigned char code) { static char buffer[25]; if (code == 0x00 || (code & 0x80) == 0x80) return "DTE Originated"; if (code == 0x03) return "Remote Procedure Error"; if (code == 0x11) return "Incompatible Destination"; if (code == 0x05) return "Local Procedure Error"; if (code == 0x07) return "Network Congestion"; sprintf(buffer, "Unknown %02X", code); return buffer; } static char *restart_code(unsigned char code) { static char buffer[25]; if (code == 0x00 || (code & 0x80) == 0x80) return "DTE Originated"; if (code == 0x01) return "Local Procedure Error"; if (code == 0x03) return "Network Congestion"; if (code == 0x07) return "Network Operational"; sprintf(buffer, "Unknown %02X", code); return buffer; } static char *dump_x25_addr(unsigned char *data) { static char buffer[25]; sprintf(buffer, "%02X%02X,%02X%02X%02X", data[0], data[1], data[2], data[3], data[4]); return buffer; } static char *dump_ax25_call(unsigned char *data, int l_data) { static char buffer[25]; char *ptr = buffer; int ssid; while (l_data-- > 1) { *ptr = *data++ >> 1; if (*ptr != ' ') ++ptr; } *ptr++ = '-'; ssid = (*data & 0x1F) >> 1; if (ssid >= 10) { *ptr++ = '1'; ssid -= 10; } *ptr++ = ssid + '0'; *ptr = 0; return buffer; } static void facility(unsigned char *data, int lgtot) { int lgfac, l, lg, fct, lgdigi, lgaddcall; int lgad, lgadind, digi_fac; char digis[80], digid[80]; char indorig[10], inddest[10]; char addstorig[20], addstdest[20]; unsigned char *d, *factot; char buf[512]; char *result = buf; factot = data; lgfac = *data++; lg = lgfac; digi_fac = 0; digid[0] = digis[0] = '\0'; indorig[0] = inddest[0] = '\0'; while (lg > 0) { fct = *data++; lg--; switch (fct) { case 0: /* Marker=0 National Fac ou Marker=15 CCITT */ data++; lg--; break; case 0x3F: /* Used if call request via L2 digi instead of L3 */ lprintf(T_ROSEHDR, "Facility 3F%2.2X\n", *data++); lg--; break; case 0x7F: /* Random number to avoid loops */ lprintf(T_ROSEHDR, "NbAlea: %2.2X%2.2X\n", *data, *(data + 1)); data += 2; lg -= 2; break; case 0xE9: /* Destination digi (for compatibility) */ lgdigi = *data++; if (!digi_fac) strcpy(digid, dump_ax25_call(data, lgdigi)); data += lgdigi; lg -= 1 + lgdigi; break; case 0xEB: /* Origin digi (for compatibility) */ lgdigi = *data++; if (!digi_fac) strcpy(digis, dump_ax25_call(data, lgdigi)); data += lgdigi; lg -= 1 + lgdigi; break; case 0xED: /* Out of order : callsign */ lgaddcall = *data++; lprintf(T_ROSEHDR, "at %s", dump_ax25_call(data, lgaddcall)); data += lgaddcall; lg -= 1 + lgaddcall; break; case 0xEE: /* Out of order : address */ lgaddcall = *data++; ++data; /* Don't know... */ lprintf(T_ROSEHDR, " @%s\n", dump_x25_addr(data)); /* data_dump(data, lgaddcall, 1); */ data += lgaddcall; lg -= 1 + lgaddcall; break; case 0xEF: lgaddcall = *data++; for (d = data, l = 0; l < lgaddcall; l += 7, d += 7) { if (l > (6 * 7)) { /* 6 digis maximum */ break; } if (d[6] & AX25_HBIT) { strcat(digis, dump_ax25_call(d, 7)); strcat(digis, " "); } else { strcat(digid, dump_ax25_call(d, 7)); strcat(digid, " "); } } data += lgaddcall; lg -= 1 + lgaddcall; digi_fac = 1; break; case 0xC9: /* Address and callsign of the remote node */ case 0xCB: /* Address and callsign of the local node */ lgaddcall = *data++; data++; data += 3; lgad = *data++; lg -= 6; if (fct == 0xCB) strcpy(addstorig, dump_x25_addr(data)); else strcpy(addstdest, dump_x25_addr(data)); data += (lgad + 1) / 2; lg -= (lgad + 1) / 2; lgadind = lgaddcall - (lgad + 1) / 2 - 5; if (fct == 0xCB) { strncpy(indorig, (char *)data, lgadind); indorig[lgadind] = '\0'; } else { strncpy(inddest, (char *)data, lgadind); inddest[lgadind] = '\0'; } data += lgadind; lg -= lgadind; break; default: lprintf(T_ROSEHDR, "Unknown Facility Type %2.2X\n", fct); data_dump(factot, lgtot, 1); lg = 0; break; } result += strlen(result); } if (*indorig && *inddest) { /* Build the displayed string */ lprintf(T_ROSEHDR, "fm %-9s @%s", indorig, addstorig); if (*digis) lprintf(T_ROSEHDR, " via %s", digis); lprintf(T_ROSEHDR, "\n"); lprintf(T_ROSEHDR, "to %-9s @%s", inddest, addstdest); if (*digid) lprintf(T_ROSEHDR, " via %s", digid); lprintf(T_ROSEHDR, "\n"); } } ax25-apps/listen/listen.h0000644000175000017500000000407113521356721013710 0ustar irlirl#define GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) #if GCC_VERSION >= 40500 /* * Mark a position in code as unreachable. This can be used to * suppress control flow warnings after asm blocks that transfer * control elsewhere. * * Early snapshots of gcc 4.5 don't support this and we can't detect * this in the preprocessor, but we can live with this because they're * unreleased. */ #define unreachable() \ do { __builtin_unreachable(); } while (0) #else #define unreachable() do { } while (1) #endif #define T_ERROR 1 #define T_PORT 2 #define T_KISS 3 #define T_BPQ 4 #define T_DATA 5 #define T_PROTOCOL 6 #define T_AXHDR 7 #define T_ADDR 8 #define T_IPHDR 9 #define T_TCPHDR 10 #define T_ROSEHDR 11 #define T_TIMESTAMP 12 #define T_FLEXNET 13 #define T_OPENTRAC 14 /* In utils.c */ extern int color; /* Colorized mode */ extern int sevenbit; /* Are we on a 7-bit terminal? */ extern int ibmhack; /* IBM mapping? */ void display_timestamp(void); void lprintf(int dtype, char *fmt, ...); int initcolor(void); char *servname(int port, char *proto); /* In listen.c */ void data_dump(void *, int, int); int get16(unsigned char *); int get32(unsigned char *); /* In kissdump.c */ void ki_dump(unsigned char *, int, int); /* ax25dump.c */ void ax25_dump(unsigned char *, int, int); char *pax25(char *, unsigned char *); /* In nrdump.c */ void netrom_dump(unsigned char *, int, int, int); /* In arpdump.c */ void arp_dump(unsigned char *, int); /* In ipdump.c */ void ip_dump(unsigned char *, int, int); /* In icmpdump.c */ void icmp_dump(unsigned char *, int, int); /* In udpdump.c */ void udp_dump(unsigned char *, int, int); /* In tcpdump.c */ void tcp_dump(unsigned char *, int, int); /* In rspfdump.c */ void rspf_dump(unsigned char *, int); /* In ripdump.c */ void rip_dump(unsigned char *, int); /* In rosedump.c */ void rose_dump(unsigned char *, int, int); /* In flexnetdump.c */ void flexnet_dump(unsigned char *, int, int); /* In opentracdump.c */ void opentrac_dump(unsigned char *, int, int); ax25-apps/listen/nrdump.c0000644000175000017500000001120413521356721013706 0ustar irlirl/* NET/ROM header tracing routines * Copyright 1991 Phil Karn, KA9Q * * Added decoding of INP route information frames, Jeroen (PE1RXQ) */ #include #include #include "listen.h" #define AXLEN 7 #define ALEN 6 #define NRPROTO_IP 0x0C #define NR4OPCODE 0x0F #define NR4OPPID 0 #define NR4OPCONRQ 1 #define NR4OPCONAK 2 #define NR4OPDISRQ 3 #define NR4OPDISAK 4 #define NR4OPINFO 5 #define NR4OPACK 6 #define NR4MORE 0x20 #define NR4NAK 0x40 #define NR4CHOKE 0x80 #define NRDESTPERPACK 11 #define NR3NODESIG 0xFF #define NR3POLLSIG 0xFE #define UI 0x03 static void netrom_flags(int); /* Display INP route information frames */ static void netrom_inp_dump(unsigned char *data, int length) { char node[10]; char alias[7]; int hops; int tt; int alen; int i; if (data[0]==0xff) { lprintf(T_AXHDR, "INP Route Information Frame:\n"); i=1; while (i", pax25(tmp, data)); lprintf(T_ADDR, "%s", pax25(tmp, data + AXLEN)); lprintf(T_AXHDR, " ttl %d\n", data[AXLEN + AXLEN]); data += (AXLEN + AXLEN + 1); length -= (AXLEN + AXLEN + 1); switch (data[4] & NR4OPCODE) { case NR4OPPID: /* network PID extension */ if (data[0] == NRPROTO_IP && data[1] == NRPROTO_IP) { ip_dump(data + 5, length - 5, hexdump); return; } else { lprintf(T_AXHDR, " protocol family %x, proto %x", data[0], data[1]); } break; case NR4OPCONRQ: /* Connect request */ lprintf(T_AXHDR, " conn rqst: my ckt %02X/%02X", data[0], data[1]); lprintf(T_AXHDR, " wnd %d", data[5]); lprintf(T_ADDR, " %s", pax25(tmp, data + 6)); lprintf(T_ADDR, "@%s", pax25(tmp, data + 6 + AXLEN)); data += AXLEN + AXLEN + 6; length -= AXLEN + AXLEN + 6; if (length > 0) lprintf(T_AXHDR, " timeout %d", data[1] * 256 + data[0]); lprintf(T_AXHDR, "\n"); break; case NR4OPCONAK: /* Connect acknowledgement */ lprintf(T_AXHDR, " conn ack: ur ckt %02X/%02X my ckt %02X/%02X", data[0], data[1], data[2], data[3]); lprintf(T_AXHDR, " wnd %d", data[5]); netrom_flags(data[4]); break; case NR4OPDISRQ: /* Disconnect request */ lprintf(T_AXHDR, " disc: ur ckt %02X/%02X\n", data[0], data[1]); break; case NR4OPDISAK: /* Disconnect acknowledgement */ lprintf(T_AXHDR, " disc ack: ur ckt %02X/%02X\n", data[0], data[1]); break; case NR4OPINFO: /* Information (data) */ lprintf(T_AXHDR, " info: ur ckt %02X/%02X", data[0], data[1]); lprintf(T_AXHDR, " txseq %d rxseq %d", data[2], data[3]); netrom_flags(data[4]); data_dump(data + 5, length - 5, hexdump); break; case NR4OPACK: /* Information acknowledgement */ lprintf(T_AXHDR, " info ack: ur ckt %02X/%02X", data[0], data[1]); lprintf(T_AXHDR, " rxseq %d", data[3]); netrom_flags(data[4]); break; default: lprintf(T_AXHDR, " unknown transport type %d\n", data[4] & 0x0f); break; } } static void netrom_flags(int flags) { if (flags & NR4CHOKE) lprintf(T_AXHDR, " CHOKE"); if (flags & NR4NAK) lprintf(T_AXHDR, " NAK"); if (flags & NR4MORE) lprintf(T_AXHDR, " MORE"); lprintf(T_AXHDR, "\n"); } ax25-apps/listen/listen.man0000644000175000017500000000603013521356721014231 0ustar irlirl.TH @@@LISTEN@@@ 1 "27 August 1996" Linux "Linux Programmer's Manual" .SH NAME @@@listen@@@ \- monitor AX.25 traffic .SH SYNOPSIS .B @@@listen@@@ [-8] [-a] [-c] [-h] [-i] [-p port] [-r] [-t..] [-v] .SH DESCRIPTION .LP .B @@@Listen@@@ uses SOCK_PACKET facilities to provide a network monitor of all AX.25 traffic heard by the system. Since KISS is implicitly promiscuous no special driver configurations are needed. .LP This version displays standard AX.25, PE1CHL extended AX.25, NET/ROM, Rose, ARP, IP, ICMP, TCP and UDP. It also displays IP, TCP, ICMP, TCP and UDP encapsulated within NET/ROM frames. The program also displays AX.25 and IP encapsulated within an IP frame, but see RFC1326 for reasons not to do so. .LP .B @@@Listen@@@ makes an attempt at decoding some of the more common routing protocols. RSPF and RIP (both \(lqnormal\(rq and G8BPQs RIP98) are traced. JNOS style NET/ROM node polls are also displayed. .LP .B @@@Listen@@@ can use colors to make the output more readable. The ncurses library is used to accomplish this. .B Color support defaults to being disabled, and the .BR -c parameter is used to enable it. .SH OPTIONS .TP 10 .BI \-8 Indicates that the terminal is capable of printing 8-bit characters. This parameter is required for the -i parameter to work. .TP 10 .BI \-a Allow for the monitoring of outgoing frames as well as incoming ones. .TP 10 .BI \-c Enable color support. .TP 10 .BI \-h Dump the data portion of the packet in both hexadecimal and ASCII. The default is to display data as ASCII only. .TP 10 .BI \-i Map IBM codepage 437 characters 128-158 to their ISO-Latin-1 equivalents. This is a hack for scandinavian users. This parameter is only valid when used with the -8 and -c parameters. .TP 10 .BI "\-p port" Monitor only those frames received on a particular port, by default all AX.25 devices are monitored. .TP 10 .BI \-r Dump the data portion in a "readable" fashion, which is more suitable for tracing plaintext AX.25 traffic. CR-LF conversion is done. .TP 10 .BI \-t Don't print a timestamp on each dump line. .TP 10 .BI -tt Print an unformatted timestamp on each dump line. .TP 10 .BI -ttt Print a delta (micro-second resolution) between current and previous line on each dump line. .TP 10 .BI -tttt Print a timestamp in default format proceeded by date on each dump line. .TP 10 .BI -ttttt Print a delta (micro-second resolution) between current and first line on each dump line. .TP 10 .BI \-v Display the version. .SH FILES /etc/ax25/axports .SH "SEE ALSO" .BR @@@call@@@ (1), .BR mheard (1), .BR beacon (1), .BR ax25 (4), .BR netrom (4), .BR rose (4), .BR kissattach (8). .LP .SH BUGS .B @@@Listen@@@ does not validate the checksums of frames that support them (ie IP), therefore corrupt frames will be displayed with bogus values. The MSS of a TCP frame that contains that option is not displayed. .SH AUTHORS .nf Alan Cox GW4PTS .br Jonathan Naylor G4KLX .br Phil Karn KA9Q .br Heikki Hannikainen OH7LZB .br Scott Miller N1VG .fi ax25-apps/listen/opentracdump.c0000644000175000017500000003206313521356721015110 0ustar irlirl/* OpenTRAC packet decoding by Scott Miller, N1VG Copyright (c) 2003 Scott Miller Protocol v1.0 draft, Modified 19 Jun 2003 Released under the Modified BSD license */ #include #include #include #include "listen.h" #define MAX_UNIT_INDEX 28 static const char *units[] ={ "Volts","Amperes","Watts","Kelvins","Meters","Seconds", "Meters/Second","Liters","Kilograms","Bits/Second","Bytes","Radians", "Radians/Second","Square Meters","Joules","Newtons","Pascals","Hertz", "Meters/Sec^2","Grays","Lumens","Cubic Meters/Second", "Pascal Seconds","Kilograms/Meter^3","Radians/Second^2","Coulombs", "Farads","Siemens","Count" }; static char origin_call[7]; /* Who's talking */ static unsigned char origin_ssid; static char entity_call[7]; /* What they're talking about */ static unsigned char entity_ssid; static unsigned int entity_serial; static unsigned int entity_sequence; static int extract_ssid(char *call) { /* Strip the SSID from the callsign and return it */ int c, ssid; for (c=ssid=0;c<6;c++) { ssid |= (call[c] & 0x80) >> (c+2); call[c] &= 0x7f; } return ssid; } /* Return values: 0 = OK, -1 = Couldn't Decode, -2 = Invalid Data */ static int decode_sequence(unsigned char *element, int element_len) { /* 0x00 Sequence number - 16 bit integer */ if (element_len != 2 && element_len != 0) return -1; if (!element_len) { entity_sequence++; } else { entity_sequence = get16(element); } lprintf(T_OPENTRAC,"Sequence: %d\r\n",entity_sequence); return 0; } static int decode_origination(unsigned char *element, int element_len) { /* 0x01 Originating Station - Callsign, SSID, and Sequence */ memcpy(origin_call, element, 6); origin_call[6]=0; origin_ssid = extract_ssid(origin_call); if (element_len == 8) entity_sequence = get16(element+6); else entity_sequence = 0; strcpy(entity_call, origin_call); entity_ssid = origin_ssid; entity_serial = 0; lprintf(T_OPENTRAC, "Origin: %s-%d seq %d\r\n",origin_call,origin_ssid,entity_sequence); return 0; } static int decode_entityid(unsigned char *element, int element_len) { /* 0x02 Entity ID */ if (element_len > 5) { memcpy(entity_call, element, 6); entity_call[6]=0; entity_ssid = extract_ssid(entity_call); } else { strcpy(entity_call, origin_call); } switch (element_len) { case 0: entity_serial++; entity_sequence = 0; break; case 2: entity_serial = get16(element); entity_sequence = 0; break; case 4: entity_serial = get16(element); entity_sequence = get16(element+2); break; case 6: entity_serial = 0; break; case 8: entity_serial = get16(element+6); entity_sequence = 0; break; case 10: entity_serial = get16(element+6); entity_sequence = get16(element+8); break; default: return -1; } lprintf(T_OPENTRAC, "Entity %s-%d:%04x #%d\r\n", entity_call, entity_ssid, entity_serial, entity_sequence); return 0; } static int decode_position(unsigned char *element, int element_len) { /* * 0x10 Position Report - Lat/Lon/ * Lat/Lon is WGS84, 180/2^31 degrees, Alt is 1/100 meter */ const double semicircles = 11930464.71111111111; double lat, lon; float alt = 0; lat = get32(element) / semicircles; lon = get32(element+4) / semicircles; if (lat >= 90 || lat <= -90 || lon >= 180 || lon <= -180) return -2; if (element_len == 11) { alt = ((*(element+8))<<16)+get16(element+9); alt = (alt/100)-10000; lprintf(T_OPENTRAC, "Position: Lat %f Lon %f Alt %f meters\r\n", lat,lon,alt); } else { lprintf(T_OPENTRAC, "Position: Lat %f Lon %f\r\n",lat,lon); } return 0; } static int decode_timestamp(unsigned char *element, int element_len) { /* 0x11 Timestamp - Unix format time (unsigned) */ long rawtime = 0; rawtime = get32(element); lprintf(T_OPENTRAC, "Time: %s", ctime(&rawtime)); return 0; } static int decode_comment(unsigned char *element, int element_len) { /* 0x12 Freeform Comment - ASCII text */ char comment[127]; strncpy(comment, (char *)element, element_len); comment[element_len] = 0; lprintf(T_OPENTRAC, "Text: %s\r\n", comment); return 0; } static int decode_courseandspeed(unsigned char *element, int element_len) { /* 0x13 Course and Speed - Course in degrees, speed in 1/50 m/s */ unsigned int course; unsigned int rawspeed; float speed; course = (*element<<1) | ((*(element+1)&0x80) >> 7); rawspeed = get16(element+1) & 0x7fff; speed = (float)rawspeed*0.072; /* km/h */ if (course >= 360) return -2; lprintf(T_OPENTRAC, "Course: %d Speed: %f kph\r\n", course, speed); return 0; } static int decode_ambiguity(unsigned char *element, int element_len) { /* 0x14 Positional Ambiguity - 16 bits, in meters */ int ambiguity; ambiguity = get16(element); lprintf(T_OPENTRAC, "Position +/- %d meters\r\n", ambiguity); return 0; } static int decode_country(unsigned char *element, int element_len) { /* 0x15 Country Code - ISO 3166-1 and optionally -2 */ char country[3]; char subdivision[4]; strncpy(country, (char *)element, 2); country[2] = 0; if (element_len > 2) { strncpy(subdivision, (char *)element+2, element_len-2); subdivision[element_len-2] = 0; lprintf(T_OPENTRAC, "Country Code %s-%s\r\n", country, subdivision); } else { lprintf(T_OPENTRAC, "Country Code %s\r\n", country); } return 0; } static int decode_displayname(unsigned char *element, int element_len) { char displayname[31]; /* 0x16 - Display Name (UTF-8 text) */ strncpy(displayname, (char *)element, element_len); displayname[element_len] = 0; lprintf(T_OPENTRAC, "Display Name: %s\r\n", displayname); return 0; } static int decode_waypoint(unsigned char *element, int element_len) { char waypoint[7]; /* 0x17 - Waypoint Name (up to 6 chars, uppercase) */ strncpy(waypoint, (char *)element, element_len); waypoint[element_len] = 0; lprintf(T_OPENTRAC, "Waypoint Name: %s\r\n", waypoint); return 0; } static int decode_symbol(unsigned char *element, int element_len) { int c; /* 0x18 Map Symbol - Packed 4-bit integers */ lprintf(T_OPENTRAC, "Symbol: "); for (c=0;c0) lprintf(T_OPENTRAC, "."); lprintf(T_OPENTRAC, "%d", element[c] >> 4); if (element[c] & 0x0f) lprintf(T_OPENTRAC, ".%d", element[c] & 0x0f); } lprintf(T_OPENTRAC, "\r\n"); return 0; } static int decode_pathtrace(unsigned char *element, int element_len) { char callsign[7]; /* 0x20 Path Trace - Call/SSID, Network */ int ssid, c, network; if (element_len % 7) return -1; /* Must be multiple of 7 octets */ if (!element_len) { lprintf(T_OPENTRAC, "Empty Path\r\n"); return 0; } lprintf(T_OPENTRAC, "Path: "); for (c=0; c> 6; validity = (element[0] & 0x30) >> 4; sats = (element[0] & 0x0f); lprintf(T_OPENTRAC, "GPS: %s %s, %d sats", fixstr[fixtype], validstr[validity], sats); if (element_len > 1) lprintf(T_OPENTRAC, " HDOP=%.1f", (float)element[1]/10); if (element_len > 2) lprintf(T_OPENTRAC, " PDOP=%.1f", (float)element[2]/10); if (element_len > 3) lprintf(T_OPENTRAC, " VDOP=%.1f", (float)element[3]/10); lprintf(T_OPENTRAC, "\r\n"); return 0; } static int decode_acreg(unsigned char *element, int element_len) { char nnumber[9]; /* 0x35 Aircraft Registration - ASCII text */ strncpy(nnumber, (char *)element, element_len); nnumber[element_len]=0; lprintf(T_OPENTRAC, "Aircraft ID: %s\r\n", nnumber); return 0; } static int decode_rivergauge(unsigned char *element, int element_len) { unsigned int flow; /* 0x42 River Flow Gauge - 1/64 m^3/sec */ unsigned int height; /* centimeters */ float flowm; float heightm; flow = get16(element); height = get16(element+2); flowm = (float)flow / 64; heightm = (float)height / 100; lprintf(T_OPENTRAC, "River flow rate: %f Cu M/Sec Height: %f M\r\n", flowm, heightm); return 0; } static int decode_units(unsigned int unitnum, unsigned char *element, int element_len) { /* * 0x0500 to 0x05ff Generic Measurement Elements * Values may be 8-bit int, 16-bit int, single float, or double float */ union measurement { char c; float f; double d; } *mval; int ival; /* too much variation in byte order and size for union */ if (unitnum > MAX_UNIT_INDEX) return -2; /* Invalid unit name */ mval = (void *)element; switch (element_len) { case 1: lprintf(T_OPENTRAC, "%d %s\r\n", mval->c, units[unitnum]); break; case 2: ival = get16(element); lprintf(T_OPENTRAC, "%d %s\r\n", ival, units[unitnum]); break; case 4: lprintf(T_OPENTRAC, "%f %s\r\n", mval->f, units[unitnum]); break; case 8: lprintf(T_OPENTRAC, "%f %s\r\n", mval->d, units[unitnum]); break; default: return -1; } return 0; } static int flag_emergency(unsigned char *element, int element_len) { /* 0x0100 - Emergency / Distress Call */ lprintf(T_ERROR, "* * * EMERGENCY * * *\r\n"); return 0; } static int flag_attention(unsigned char *element, int element_len) { /* 0x0101 - Attention / Ident */ lprintf(T_PROTOCOL, " - ATTENTION - \r\n"); return 0; } static int decode_hazmat(unsigned char *element, int element_len) { /* 0x0300 - HAZMAT (UN ID in lower 14 bits) */ int un_id; if (element_len < 2) { lprintf(T_OPENTRAC, "HAZMAT: Unknown Material\r\n"); } else { un_id = get16(element) & 0x3fff; lprintf(T_OPENTRAC, "HAZMAT: UN%04d\r\n", un_id); } return 0; } static int decode_maidenhead(unsigned char *element, int element_len) { /* 0x32 - Maidenhead Locator (4 or 6 chars) */ char maidenhead[7]; if (element_len > 6 || !element_len) return -1; strncpy(maidenhead, (char *)element, element_len); maidenhead[element_len] = 0; lprintf(T_OPENTRAC, "Grid ID: %s\r\n", maidenhead); return 0; } static struct { unsigned int element_id; unsigned char min_size; unsigned char max_size; int (*pDecode)(unsigned char *element, int element_len); } element_decode[] = {{0x00, 0, 2, decode_sequence}, {0x01, 6, 8, decode_origination}, {0x02, 0, 10, decode_entityid}, {0x10, 8, 11, decode_position}, {0x11, 4, 4, decode_timestamp}, {0x12, 0, 127, decode_comment}, {0x13, 3, 3, decode_courseandspeed}, {0x14, 2, 2, decode_ambiguity}, {0x15, 2, 5, decode_country}, {0x16, 1, 30, decode_displayname}, {0x17, 1, 6, decode_waypoint}, {0x18, 1, 4, decode_symbol}, {0x20, 0, 127, decode_pathtrace}, {0x21, 0, 127, decode_heardby}, {0x22, 0, 127, decode_availablenets}, {0x32, 4, 6, decode_maidenhead}, {0x34, 1, 4, decode_gpsquality}, {0x35, 1, 8, decode_acreg}, {0x42, 4, 4, decode_rivergauge}, {0x100, 0, 127, flag_emergency}, {0x101, 0, 127, flag_attention}, {0x300, 0, 2, decode_hazmat}, {0, 0, 0, NULL}}, *element_decode_ptr; /* Dump an OpenTRAC packet */ void opentrac_dump(unsigned char *data, int length, int hexdump) { int elen; int etype; int decoded = 0; lprintf(T_PROTOCOL, "OpenTRAC decode (%d bytes):\r\n", length); strcpy(origin_call, "SENDER"); /* Listen doesn't tell us the sender */ origin_ssid = 0; entity_serial = 0; entity_sequence = 0; while (decoded < length) { elen = (int)*data; decoded += (elen & 0x7f)+1; if (elen & 0x80) { /* See if it's got a 16-bit ID */ elen = (elen & 0x7f) - 2; /* Strip the extid flag */ etype = get16(++data); } else { elen--; /* Don't count the type byte */ etype = (int)*(data+1); } data+=2; /* Skip to the body */ lprintf(T_OPENTRAC, "EID 0x%0x len %d: ", etype, elen); for (element_decode_ptr = element_decode; element_decode_ptr->pDecode != NULL; element_decode_ptr++) { if (etype == element_decode_ptr->element_id) { if (elen < element_decode_ptr->min_size || elen > element_decode_ptr->max_size) { lprintf(T_OPENTRAC, "Invalid element length\r\n"); } else { if (element_decode_ptr->pDecode(data, elen)) { lprintf(T_OPENTRAC, "Unable to decode\r\n"); } } break; } } if (element_decode_ptr->pDecode == NULL) { if ((etype & 0xff00) == 0x500) { decode_units(etype & 0x00ff, data, elen); } else { lprintf(T_OPENTRAC, "Unknown element type - unable to decode.\r\n"); } } data+=elen; } } ax25-apps/listen/kissdump.c0000644000175000017500000000225413521356721014245 0ustar irlirl/* Tracing routines for KISS TNC * Copyright 1991 Phil Karn, KA9Q */ #include #include "listen.h" #define PARAM_DATA 0 #define PARAM_TXDELAY 1 #define PARAM_PERSIST 2 #define PARAM_SLOTTIME 3 #define PARAM_TXTAIL 4 #define PARAM_FULLDUP 5 #define PARAM_HW 6 #define PARAM_RETURN 15 /* Should be 255, but is ANDed with 0x0F */ void ki_dump(unsigned char *data, int length, int hexdump) { int type; int val; type = data[0] & 0xf; if (type == PARAM_DATA) { ax25_dump(data + 1, length - 1, hexdump); return; } val = data[1]; switch (type) { case PARAM_TXDELAY: lprintf(T_KISS, "TX Delay: %lu ms\n", val * 10L); break; case PARAM_PERSIST: lprintf(T_KISS, "Persistence: %u/256\n", val + 1); break; case PARAM_SLOTTIME: lprintf(T_KISS, "Slot time: %lu ms\n", val * 10L); break; case PARAM_TXTAIL: lprintf(T_KISS, "TX Tail time: %lu ms\n", val * 10L); break; case PARAM_FULLDUP: lprintf(T_KISS, "Duplex: %s\n", val == 0 ? "Half" : "Full"); break; case PARAM_HW: lprintf(T_KISS, "Hardware %u\n", val); break; case PARAM_RETURN: lprintf(T_KISS, "RETURN\n"); break; default: lprintf(T_KISS, "code %u arg %u\n", type, val); break; } } ax25-apps/listen/icmpdump.c0000644000175000017500000000763313521356721014232 0ustar irlirl/* ICMP header tracing * Copyright 1991 Phil Karn, KA9Q */ #include #include "listen.h" #define ICMP_DEST_UNREACH 3 #define ICMP_REDIRECT 5 #define ICMP_TIME_EXCEED 11 #define ICMP_PARAM_PROB 12 #define ICMP_ECHO 8 #define ICMP_ECHO_REPLY 0 #define ICMP_INFO_RQST 15 #define ICMP_INFO_REPLY 16 #define ICMP_TIMESTAMP 13 #define ICMP_TIME_REPLY 14 #define ICMP_QUENCH 4 #define ICMPLEN 8 /* Dump an ICMP header */ void icmp_dump(unsigned char *data, int length, int hexdump) { int type; int code; int pointer; unsigned char *address; int id, seq; type = data[0]; code = data[1]; pointer = data[4]; address = data + 4; id = get16(data + 4); seq = get16(data + 6); data += ICMPLEN; length -= ICMPLEN; lprintf(T_IPHDR, "ICMP: type "); switch (type) { case ICMP_DEST_UNREACH: lprintf(T_ERROR, "Unreachable "); lprintf(T_IPHDR, "code "); switch (code) { case 0: lprintf(T_ERROR, "Network"); break; case 1: lprintf(T_ERROR, "Host"); break; case 2: lprintf(T_ERROR, "Protocol"); break; case 3: lprintf(T_ERROR, "Port"); break; case 4: lprintf(T_ERROR, "Fragmentation"); break; case 5: lprintf(T_ERROR, "Source route"); break; case 6: lprintf(T_ERROR, "Dest net unknown"); break; case 7: lprintf(T_ERROR, "Dest host unknown"); break; case 8: lprintf(T_ERROR, "Source host isolated"); break; case 9: lprintf(T_ERROR, "Net prohibited"); break; case 10: lprintf(T_ERROR, "Host prohibited"); break; case 11: lprintf(T_ERROR, "Net TOS"); break; case 12: lprintf(T_ERROR, "Host TOS"); break; case 13: lprintf(T_ERROR, "Administratively Prohibited"); break; default: lprintf(T_ERROR, "%d", code); break; } lprintf(T_IPHDR, "\nReturned "); ip_dump(data, length, hexdump); break; case ICMP_REDIRECT: lprintf(T_ERROR, "Redirect "); lprintf(T_IPHDR, "code "); switch (code) { case 0: lprintf(T_ERROR, "Network"); break; case 1: lprintf(T_ERROR, "Host"); break; case 2: lprintf(T_ERROR, "TOS & Network"); break; case 3: lprintf(T_ERROR, "TOS & Host"); break; default: lprintf(T_ERROR, "%d", code); break; } lprintf(T_IPHDR, " new gateway %d.%d.%d.%d", address[0], address[1], address[2], address[3]); lprintf(T_IPHDR, "\nReturned "); ip_dump(data, length, hexdump); break; case ICMP_TIME_EXCEED: lprintf(T_ERROR, "Time Exceeded code "); lprintf(T_ERROR, "Time Exceeded "); lprintf(T_IPHDR, "code "); switch (code) { case 0: lprintf(T_ERROR, "Time-to-live"); break; case 1: lprintf(T_ERROR, "Fragment reassembly"); break; default: lprintf(T_ERROR, "%d", code); break; } lprintf(T_IPHDR, "\nReturned "); ip_dump(data, length, hexdump); break; case ICMP_PARAM_PROB: lprintf(T_ERROR, "Parameter Problem pointer %d", pointer); lprintf(T_IPHDR, "\nReturned "); ip_dump(data, length, hexdump); break; case ICMP_QUENCH: lprintf(T_ERROR, "Source Quench"); lprintf(T_IPHDR, "\nReturned "); ip_dump(data, length, hexdump); break; case ICMP_ECHO: lprintf(T_IPHDR, "Echo Request id %d seq %d\n", id, seq); data_dump(data, length, hexdump); break; case ICMP_ECHO_REPLY: lprintf(T_IPHDR, "Echo Reply id %d seq %d\n", id, seq); data_dump(data, length, hexdump); break; case ICMP_INFO_RQST: lprintf(T_IPHDR, "Information Request id %d seq %d\n", id, seq); data_dump(data, length, hexdump); break; case ICMP_INFO_REPLY: lprintf(T_IPHDR, "Information Reply id %d seq %d\n", id, seq); data_dump(data, length, hexdump); break; case ICMP_TIMESTAMP: lprintf(T_IPHDR, "Timestamp Request id %d seq %d\n", id, seq); data_dump(data, length, hexdump); break; case ICMP_TIME_REPLY: lprintf(T_IPHDR, "Timestamp Reply id %d seq %d\n", id, seq); data_dump(data, length, hexdump); break; default: lprintf(T_IPHDR, "%d\n", type); data_dump(data, length, hexdump); break; } } ax25-apps/listen/ipdump.c0000644000175000017500000000445413521356721013710 0ustar irlirl/* IP header tracing routines * Copyright 1991 Phil Karn, KA9Q */ #include #include "listen.h" #define IPLEN 20 #define MF 0x2000 #define DF 0x4000 #define CE 0x8000 #define IPIPNEW_PTCL 4 #define IPIPOLD_PTCL 94 #define TCP_PTCL 6 #define UDP_PTCL 17 #define ICMP_PTCL 1 #define AX25_PTCL 93 #define RSPF_PTCL 73 void ip_dump(unsigned char *data, int length, int hexdump) { int hdr_length; int tos; int ip_length; int id; int fl_offs; int flags; int offset; int ttl; int protocol; unsigned char *source, *dest; lprintf(T_PROTOCOL, "IP:"); /* Sneak peek at IP header and find length */ hdr_length = (data[0] & 0xf) << 2; if (hdr_length < IPLEN) { lprintf(T_ERROR, " bad header\n"); return; } tos = data[1]; ip_length = get16(data + 2); id = get16(data + 4); fl_offs = get16(data + 6); flags = fl_offs & 0xE000; offset = (fl_offs & 0x1FFF) << 3; ttl = data[8]; protocol = data[9]; source = data + 12; dest = data + 16; lprintf(T_IPHDR, " len %d", ip_length); lprintf(T_ADDR, " %d.%d.%d.%d->%d.%d.%d.%d", source[0], source[1], source[2], source[3], dest[0], dest[1], dest[2], dest[3]); lprintf(T_IPHDR, " ihl %d ttl %d", hdr_length, ttl); if (tos != 0) lprintf(T_IPHDR, " tos %d", tos); if (offset != 0 || (flags & MF)) lprintf(T_IPHDR, " id %d offs %d", id, offset); if (flags & DF) lprintf(T_IPHDR, " DF"); if (flags & MF) lprintf(T_IPHDR, " MF"); if (flags & CE) lprintf(T_IPHDR, " CE"); data += hdr_length; length -= hdr_length; if (offset != 0) { lprintf(T_IPHDR, "\n"); if (length > 0) data_dump(data, length, hexdump); return; } switch (protocol) { case IPIPOLD_PTCL: case IPIPNEW_PTCL: lprintf(T_IPHDR, " prot IP\n"); ip_dump(data, length, hexdump); break; case TCP_PTCL: lprintf(T_IPHDR, " prot TCP\n"); tcp_dump(data, length, hexdump); break; case UDP_PTCL: lprintf(T_IPHDR, " prot UDP\n"); udp_dump(data, length, hexdump); break; case ICMP_PTCL: lprintf(T_IPHDR, " prot ICMP\n"); icmp_dump(data, length, hexdump); break; case AX25_PTCL: lprintf(T_IPHDR, " prot AX25\n"); ax25_dump(data, length, hexdump); break; case RSPF_PTCL: lprintf(T_IPHDR, " prot RSPF\n"); rspf_dump(data, length); break; default: lprintf(T_IPHDR, " prot %d\n", protocol); data_dump(data, length, hexdump); break; } } ax25-apps/listen/ax25dump.c0000644000175000017500000002054113521356721014052 0ustar irlirl/* AX25 header tracing * Copyright 1991 Phil Karn, KA9Q */ #include #include #include #include "listen.h" #define LAPB_UNKNOWN 0 #define LAPB_COMMAND 1 #define LAPB_RESPONSE 2 #define SEG_FIRST 0x80 #define SEG_REM 0x7F #define PID_SEGMENT 0x08 #define PID_ARP 0xCD #define PID_NETROM 0xCF #define PID_IP 0xCC #define PID_X25 0x01 #define PID_TEXNET 0xC3 #define PID_FLEXNET 0xCE #define PID_OPENTRAC 0x77 #define PID_NO_L3 0xF0 #define I 0x00 #define S 0x01 #define RR 0x01 #define RNR 0x05 #define REJ 0x09 #define U 0x03 #define SABM 0x2F #define SABME 0x6F #define DISC 0x43 #define DM 0x0F #define UA 0x63 #define FRMR 0x87 #define UI 0x03 #define PF 0x10 #define EPF 0x01 #define MMASK 7 #define HDLCAEB 0x01 #define SSID 0x1E #define REPEATED 0x80 #define C 0x80 #define SSSID_SPARE 0x40 #define ESSID_SPARE 0x20 #define ALEN 6 #define AXLEN 7 #define W 1 #define X 2 #define Y 4 #define Z 8 static int ftype(unsigned char *, int *, int *, int *, int *, int); static char *decode_type(int); #define NDAMA_STRING "" #define DAMA_STRING " [DAMA]" /* FlexNet header compression display by Thomas Sailer t.sailer@alumni.ethz.ch */ /* Dump an AX.25 packet header */ void ax25_dump(unsigned char *data, int length, int hexdump) { char tmp[15]; int ctlen, nr, ns, pf, pid, seg, type, end, cmdrsp, extseq; char *dama; /* check for FlexNet compressed header first; FlexNet header compressed packets are at least 8 bytes long */ if (length < 8) { /* Something wrong with the header */ lprintf(T_ERROR, "AX25: bad header!\n"); return; } if (data[1] & HDLCAEB) { /* this is a FlexNet compressed header */ lprintf(T_PROTOCOL, " "); tmp[6] = tmp[7] = extseq = 0; tmp[0] = ' ' + (data[2] >> 2); tmp[1] = ' ' + ((data[2] << 4) & 0x30) + (data[3] >> 4); tmp[2] = ' ' + ((data[3] << 2) & 0x3c) + (data[4] >> 6); tmp[3] = ' ' + (data[4] & 0x3f); tmp[4] = ' ' + (data[5] >> 2); tmp[5] = ' ' + ((data[5] << 4) & 0x30) + (data[6] >> 4); if (data[6] & 0xf) sprintf(tmp + 7, "-%d", data[6] & 0xf); lprintf(T_ADDR, "%d->%s%s", (data[0] << 6) | ((data[1] >> 2) & 0x3f), strtok(tmp, " "), tmp + 7); cmdrsp = (data[1] & 2) ? LAPB_COMMAND : LAPB_RESPONSE; dama = NDAMA_STRING; data += 7; length -= 7; } else { /* Extract the address header */ if (length < (AXLEN + AXLEN + 1)) { /* Something wrong with the header */ lprintf(T_ERROR, "AX25: bad header!\n"); return; } if ((data[AXLEN + ALEN] & SSSID_SPARE) == SSSID_SPARE) { extseq = 0; /* lprintf(T_PROTOCOL, " "); */ } else { extseq = 1; lprintf(T_PROTOCOL, "EAX25: "); } if ((data[AXLEN + ALEN] & ESSID_SPARE) == ESSID_SPARE) dama = NDAMA_STRING; else dama = DAMA_STRING; lprintf(T_AXHDR, "fm "); lprintf(T_ADDR, "%s", pax25(tmp, data + AXLEN)); lprintf(T_AXHDR, " to "); lprintf(T_ADDR, "%s", pax25(tmp, data)); cmdrsp = LAPB_UNKNOWN; if ((data[ALEN] & C) && !(data[AXLEN + ALEN] & C)) cmdrsp = LAPB_COMMAND; if ((data[AXLEN + ALEN] & C) && !(data[ALEN] & C)) cmdrsp = LAPB_RESPONSE; end = (data[AXLEN + ALEN] & HDLCAEB); data += (AXLEN + AXLEN); length -= (AXLEN + AXLEN); if (!end) { lprintf(T_AXHDR, " via"); while (!end) { /* Print digi string */ lprintf(T_ADDR, " %s%s", pax25(tmp, data), (data[ALEN] & REPEATED) ? "*" : ""); end = (data[ALEN] & HDLCAEB); data += AXLEN; length -= AXLEN; } } } if (length == 0) return; ctlen = ftype(data, &type, &ns, &nr, &pf, extseq); data += ctlen; length -= ctlen; lprintf(T_AXHDR, " ctl %s", decode_type(type)); if ((type & 0x3) != U) /* I or S frame? */ lprintf(T_AXHDR, "%d", nr); if (type == I) lprintf(T_AXHDR, "%d", ns); switch (cmdrsp) { case LAPB_COMMAND: if (pf) lprintf(T_AXHDR, "+"); else lprintf(T_AXHDR, "^"); break; case LAPB_RESPONSE: if (pf) lprintf(T_AXHDR, "-"); else lprintf(T_AXHDR, "v"); break; default: break; } if (type == I || type == UI) { /* Decode I field */ if (length > 0) { /* Get pid */ pid = *data++; length--; lprintf(T_AXHDR, " pid=%X", pid); switch (pid) { case PID_SEGMENT: lprintf(T_AXHDR, "(segment)"); break; case PID_ARP: lprintf(T_AXHDR, "(ARP)"); break; case PID_NETROM: lprintf(T_AXHDR, "(NET/ROM)"); break; case PID_IP: lprintf(T_AXHDR, "(IP)"); break; case PID_X25: lprintf(T_AXHDR, "(X.25)"); break; case PID_TEXNET: lprintf(T_AXHDR, "(TEXNET)"); break; case PID_FLEXNET: lprintf(T_AXHDR, "(FLEXNET)"); break; case PID_OPENTRAC: lprintf(T_AXHDR, "(OPENTRAC)"); break; case PID_NO_L3: lprintf(T_AXHDR, "(Text)"); break; } lprintf(T_AXHDR, "%s len %d ", dama, length); display_timestamp(); if (pid == PID_SEGMENT) { seg = *data++; length--; lprintf(T_AXHDR, "%s remain %u", seg & SEG_FIRST ? " First seg;" : "", seg & SEG_REM); if (seg & SEG_FIRST) { pid = *data++; length--; } } lprintf(T_AXHDR, "\n"); switch (pid) { case PID_SEGMENT: data_dump(data, length, hexdump); break; case PID_ARP: arp_dump(data, length); break; case PID_NETROM: netrom_dump(data, length, hexdump, type); break; case PID_IP: ip_dump(data, length, hexdump); break; case PID_X25: rose_dump(data, length, hexdump); break; case PID_TEXNET: data_dump(data, length, hexdump); break; case PID_FLEXNET: flexnet_dump(data, length, hexdump); break; case PID_OPENTRAC: opentrac_dump(data, length, hexdump); break; case PID_NO_L3: data_dump(data, length, hexdump); break; default: data_dump(data, length, hexdump); break; } } } else if (type == FRMR && length >= 3) { /* FIX ME XXX lprintf(T_AXHDR, ": %s", decode_type(ftype(data[0]))); */ lprintf(T_AXHDR, ": %02X", data[0]); lprintf(T_AXHDR, " Vr = %d Vs = %d", (data[1] >> 5) & MMASK, (data[1] >> 1) & MMASK); if (data[2] & W) lprintf(T_ERROR, " Invalid control field"); if (data[2] & X) lprintf(T_ERROR, " Illegal I-field"); if (data[2] & Y) lprintf(T_ERROR, " Too-long I-field"); if (data[2] & Z) lprintf(T_ERROR, " Invalid seq number"); lprintf(T_AXHDR, "%s ", dama); display_timestamp(); lprintf(T_AXHDR, "\n"); } else if ((type == SABM || type == UA) && length >= 2) { /* FlexNet transmits the QSO "handle" for header * compression in SABM and UA frame data fields */ lprintf(T_AXHDR, " [%d]%s ", (data[0] << 8) | data[1], dama); display_timestamp(); lprintf(T_AXHDR, "\n"); } else { lprintf(T_AXHDR, "%s ", dama); display_timestamp(); lprintf(T_AXHDR, "\n"); } } static char *decode_type(int type) { switch (type) { case I: return "I"; case SABM: return "SABM"; case SABME: return "SABME"; case DISC: return "DISC"; case DM: return "DM"; case UA: return "UA"; case RR: return "RR"; case RNR: return "RNR"; case REJ: return "REJ"; case FRMR: return "FRMR"; case UI: return "UI"; default: return "[invalid]"; } } char *pax25(char *buf, unsigned char *data) { int i, ssid; char *s; char c; s = buf; for (i = 0; i < ALEN; i++) { c = (data[i] >> 1) & 0x7F; if (!isalnum(c) && c != ' ') { strcpy(buf, "[invalid]"); return buf; } if (c != ' ') *s++ = c; } if ((ssid = (data[ALEN] & SSID)) != 0) sprintf(s, "-%d", ssid >> 1); else *s = '\0'; return buf; } static int ftype(unsigned char *data, int *type, int *ns, int *nr, int *pf, int extseq) { *ns = *nr = 0; /* To avoid warnings */ if (extseq) { if ((*data & 0x01) == 0) { /* An I frame is an I-frame ... */ *type = I; *ns = (*data >> 1) & 127; data++; *nr = (*data >> 1) & 127; *pf = *data & EPF; return 2; } if (*data & 0x02) { *type = *data & ~PF; *pf = *data & PF; return 1; } else { *type = *data; data++; *nr = (*data >> 1) & 127; *pf = *data & EPF; return 2; } } else { if ((*data & 0x01) == 0) { /* An I frame is an I-frame ... */ *type = I; *ns = (*data >> 1) & 7; *nr = (*data >> 5) & 7; *pf = *data & PF; return 1; } if (*data & 0x02) { /* U-frames use all except P/F bit for type */ *type = *data & ~PF; *pf = *data & PF; return 1; } else { /* S-frames use low order 4 bits for type */ *type = *data & 0x0F; *nr = (*data >> 5) & 7; *pf = *data & PF; return 1; } } } ax25-apps/listen/arpdump.c0000644000175000017500000000406313521356721014056 0ustar irlirl/* ARP packet tracing routines * Copyright 1991 Phil Karn, KA9Q */ #include #include "listen.h" #define ARP_REQUEST 1 #define ARP_REPLY 2 #define REVARP_REQUEST 3 #define REVARP_REPLY 4 #define ARP_AX25 3 #define AXALEN 7 #define PID_IP 0xCC void arp_dump(unsigned char *data, int length) { int is_ip = 0; int hardware; int protocol; int hwlen; int pralen; int operation; unsigned char *shwaddr, *sprotaddr; unsigned char *thwaddr, *tprotaddr; char tmp[25]; lprintf(T_PROTOCOL, "ARP: "); lprintf(T_IPHDR, "len %d", length); if (length < 16) { lprintf(T_ERROR, " bad packet\n"); return; } hardware = get16(data + 0); protocol = get16(data + 2); hwlen = data[4]; pralen = data[5]; operation = get16(data + 6); if (hardware != ARP_AX25) { lprintf(T_IPHDR, " non-AX25 ARP packet\n"); return; } lprintf(T_IPHDR, " hwtype AX25"); /* Print hardware length only if it doesn't match * the length in the known types table */ if (hwlen != AXALEN) lprintf(T_IPHDR, " hwlen %d", hwlen); if (protocol == PID_IP) { lprintf(T_IPHDR, " prot IP"); is_ip = 1; } else { lprintf(T_IPHDR, " prot 0x%x prlen %d", protocol, pralen); } switch (operation) { case ARP_REQUEST: lprintf(T_IPHDR, " op REQUEST"); break; case ARP_REPLY: lprintf(T_IPHDR, " op REPLY"); break; case REVARP_REQUEST: lprintf(T_IPHDR, " op REVERSE REQUEST"); break; case REVARP_REPLY: lprintf(T_IPHDR, " op REVERSE REPLY"); break; default: lprintf(T_IPHDR, " op %d", operation); break; } shwaddr = data + 8; sprotaddr = shwaddr + hwlen; thwaddr = sprotaddr + pralen; tprotaddr = thwaddr + hwlen; lprintf(T_IPHDR, "\nsender"); if (is_ip) lprintf(T_ADDR, " IPaddr %d.%d.%d.%d", sprotaddr[0], sprotaddr[1], sprotaddr[2], sprotaddr[3]); lprintf(T_IPHDR, " hwaddr %s\n", pax25(tmp, shwaddr)); lprintf(T_IPHDR, "target"); if (is_ip) lprintf(T_ADDR, " IPaddr %d.%d.%d.%d", tprotaddr[0], tprotaddr[1], tprotaddr[2], tprotaddr[3]); if (*thwaddr != 0) lprintf(T_ADDR, " hwaddr %s", pax25(tmp, thwaddr)); lprintf(T_IPHDR, "\n"); } ax25-apps/listen/flexnetdump.c0000644000175000017500000000634613521356721014747 0ustar irlirl/* * FlexNet internode communication dump */ #include #include #include #include "listen.h" /* * This was hacked by Thomas Sailer, t.sailer@alumni.ethz.ch, HB9JNX/AE4WA, with * some bits stolen from Dieter Deyke (Wampes), such as these defines :-) */ #define FLEX_INIT '0' /* Link initialization */ #define FLEX_RPRT '1' /* Poll answer */ #define FLEX_POLL '2' /* Poll */ #define FLEX_ROUT '3' /* Routing information */ #define FLEX_QURY '6' /* Path query */ #define FLEX_RSLT '7' /* Query result */ static int flx_get_number(unsigned char **data, int *length) { int l = *length; unsigned char *d = *data; int res = 0; if (l <= 0 || *d < '0' || *d > '9') return -1; while (l > 0 && *d >= '0' && *d <= '9') { res = res * 10 + (*d++) - '0'; l--; } *data = d; *length = l; return res; } static void dump_end(unsigned char *data, int length) { if (length <= 0) return; for (; length > 0; length--, data++) lprintf(T_FLEXNET, " %02x", *data); lprintf(T_FLEXNET, "\n"); } void flexnet_dump(unsigned char *data, int length, int hexdump) { int i; int hopcnt, qsonr; unsigned char *cp; lprintf(T_PROTOCOL, "FlexNet:"); if (length < 1) { lprintf(T_ERROR, " bad packet\n"); return; } switch (data[0]) { default: lprintf(T_ERROR, " unknown packet type\n"); data_dump(data, length, hexdump); return; case FLEX_INIT: if (length < 2) { lprintf(T_ERROR, " bad packet\n"); dump_end(data + 1, length - 1); return; } lprintf(T_FLEXNET, " Link setup - max SSID %d ", data[1] & 0xf); dump_end(data + 1, length - 1); return; case FLEX_RPRT: lprintf(T_FLEXNET, " Poll response -"); data++; length--; i = flx_get_number(&data, &length); data++; length--; if (i < 0) goto too_short; lprintf(T_FLEXNET, " delay: %d\n", i); dump_end(data, length); return; case FLEX_POLL: lprintf(T_FLEXNET, " Poll\n"); return; case FLEX_ROUT: data++; length--; lprintf(T_FLEXNET, " Routing\n"); while (length > 0) { if (isdigit(*data) || isupper(*data)) { if (length < 10) goto too_short; lprintf(T_FLEXNET, " %.6s %2d-%2d ", data, data[6] - '0', data[7] - '0'); data += 8; length -= 8; i = flx_get_number(&data, &length); data++; length--; if (!i) lprintf(T_FLEXNET, "link down\n"); else lprintf(T_FLEXNET, "delay: %d\n", i); } else { if (*data == '+') lprintf(T_FLEXNET, " Request token\n"); else if (*data == '-') lprintf(T_FLEXNET, " Release token\n"); else if (*data != '\r') lprintf(T_FLEXNET, " invalid char: %02x\n", *data); data++; length--; } } return; case FLEX_QURY: case FLEX_RSLT: lprintf(T_FLEXNET, " Route query"); if (*data == FLEX_RSLT) lprintf(T_FLEXNET, " response"); if (length < 2) goto too_short; hopcnt = data[1] - ' '; data += 2; length -= 2; qsonr = flx_get_number(&data, &length); data++; length--; lprintf(T_FLEXNET, " - hop count: %d QSO number: %d\n", hopcnt, qsonr); cp = memchr(data, '\r', length); if (cp) *cp = 0; lprintf(T_FLEXNET, " data\n"); return; } too_short: lprintf(T_ERROR, " packet too short\n"); dump_end(data, length); } ax25-apps/NEWS0000644000175000017500000000024013521356721011434 0ustar irlirlNEWS for ax25-apps ================== These programs are still being tested, you should expect some quirks especially with compiling for a little while longer ax25-apps/README0000644000175000017500000000064213521356721011623 0ustar irlirlAX25 Apps ======== Listen now by default is NOT setuid root which means that normal users will not be able to run it out of the box. If you want everyone to run listen then after installing the binaries as root type chmod u+s /usr/bin/listen This version includes support for Mat DG2FEF's new AX.25 subsystem. Binaries are not interchangeable but require recompile. - Craig Small ax25-apps/call/0000755000175000017500000000000013521356721011654 5ustar irlirlax25-apps/call/.gitignore0000644000175000017500000000001413521356721013637 0ustar irlirlcall call.1 ax25-apps/call/call.man0000644000175000017500000001236313521356721013271 0ustar irlirl.TH @@@CALL@@@ 1 "27 August 1996" Linux "Linux Programmer's Manual" .SH NAME @@@call@@@ \- make an AX.25, NET/ROM or Rose connection. .SH SYNOPSIS For AX.25 .br .B @@@call@@@ [-b l|e] [-d] [-h] [-m s|e] [-p paclen] [-s mycall] [-r] [-t] [-T timeout] [-v] [-w window] [-W] port callsign [[via] digipeaters.....] .sp 1 For NET/ROM .br .B @@@call@@@ [-d] [-h] [-p paclen] [-r] [-t] [-T timeout] [-v] port callsign .sp 1 For Rose .br .B @@@call@@@ [-d] [-h] [-r] [-t] [-T timeout] [-v] port callsign address [[via] digipeater] .br .SH DESCRIPTION .LP .B @@@Call@@@ is the general purpose AX.25, NET/ROM and Rose connection program. As is normal in AX.25 it runs entirely in line mode. All CR/LF translation is done transparently. The program provides ASCII, YAPP, YAPP-C and 7+ file transfer facilities. Some of the options are only valid in AX.25 mode, the program will quietly ignore them if given in NET/ROM mode or in Rose mode. .LP This version of .B @@@call@@@ incorporates many changes that include a much improved user interface. The port name is mandatory and is the name of the port which the connection will be made on. The port name may be either an AX.25, a NET/ROM port or a Rose port, .B @@@call@@@ will determine which automatically. .LP When using NET/ROM the use of digipeaters is meaningless and is not supported. However when using Rose up to one digipeater may be specified, as well as the Rose address of the distant node and the callsign of the remote station. The Rose address must be ten digits long and it must exist in the Rose node tables. For Rose connects to work .B axparms must have been used to set a callsign/uid mapping for the userid in use. .SH OPTIONS .TP 10 .BI "\-8" UTF-8 encoding (default) .TP 10 .BI "\-b l|e" Sets the method of backoff to use with AX.25 connections. The default is taken from the port specification, but can be overridden by this option. Valid values are .B l for linear backoff, and .B e for exponential backoff. .TP 10 .BI \-d Turn on socket level debugging. .TP 10 .BI \-h Selects Slave mode. .TP 10 .BI \-i Use IBM850 encoding .BI "\-m s|e" Sets the AX.25 mode to use. The default is taken from the port specification, but can be overridden by this option. Valid values are .B s for normal (modulus 8) AX.25 operation, or .B e for extended (modulus 128) AX.25 operation. .TP 10 .BI "\-p paclen" Specify a specific maximum amount of data to be sent in each AX.25 packet. .TP 10 .BI \-r Selects Raw mode. .TP 10 .BI "\-s mycall" Connect using the specified source @@@call@@@ (you may need to be root). .TP 10 .BI \-t Selects Talk mode. .TP 10 .BI \-v Display the version. .TP 10 .BI "\-w window" Specify a specific AX.25 window for this connection. Only valid in AX.25 mode. .TP 10 .BI \-S Be silent. Useful for using @@@call@@@ in shellscripts (together with option \-r) in order to be really transparent. .TP 10 .BI \-R Disable all remote commands like autobin (#BIN#), 7plus downloads, //echo, etc. This is useful for scripting where you just don't like the remote site to trigger uncaught conditions here. .TP 10 .BI "\-T timeout" Set idle timeout seconds after a connection will be closed automatically when there's no data being transferred (in- and outbound). If timeout is set to 3600, the connection will close after 1h inactivity. A value of 0.5 is 500ms. .TP 10 .BI "\-W" Wait for remote disconnect even if stdin is closed. Normaly, "echo q | @@@call@@@ ax0 db0fhn" closes immediately after "echo q" terminates; thus we never see the greeting from db0fhn. With the -W option, @@@call@@@ waits until the ax25-connection is disconnected (i.e. db0fhn disconnects after "q" command). Very useful for scripting. You might like to combine it with the -T option. .LP The @@@call@@@ program interprets lines beginning with a '~' specially. The following '~' escapes are available. .TP 16 .BI ~? List escapes .TP 16 .BI ~~ A ~ symbol .TP 16 .BI ~. Close connection .TP 16 .BI "~! [command]" Run a command .TP 16 .BI ~0 Change to Raw mode .TP 16 .BI ~1 Change to Slave mode .TP 16 .BI ~2 Change to Talk mode .TP 16 .BI ~8 UTF-8 encoding .TP 16 .BI ~a Start a file transfer (Autobin) .TP 16 .BI ~b Start a file transfer (Binary) .TP 16 .BI ~c Close the logfile .TP 16 .BI ~h List escapes .TP 16 .BI ~i IBM850 encoding .TP 16 .BI "~o [filename]" Open a logfile (default 'logfile.txt') .TP 16 .BI ~r Reconnect to remote station .TP 16 .BI ~s Stop an upload .TP 16 .BI "~u [filename]" Upload a file (ASCII upload) .TP 16 .BI "~yu [filename]" Upload a file (YAPP upload) .TP 16 .BI "~yd [filename]" Download a file (YAPP download) .TP 16 .BI ~z Suspend program .LP The program provides no terminal emulation features. These are left up to the console facilities of the terminal in use. The program is however '8 bit clean'. .SH FILES .nf /proc/net/nr_nodes .br /proc/net/rose_nodes .br /etc/ax25/axports .br /etc/ax25/nrports .br /etc/ax25/rsports .fi .SH "SEE ALSO" .BR @@@listen@@@ (1), .BR mheard (1), .BR ax25 (4), .BR netrom (4), .BR rose (4), .BR axports (5), .BR nrports (5), .BR rsports (5), .BR axparms (8), .BR nrparms (8), .BR rsparms (8). .SH AUTHORS .nf Alexander Tietzel DG6XA .br Joerg Reuter DL1BKE .br Alan Cox GW4PTS .br Jonathan Naylor G4KLX .br Steve Henson G6IXS .fi ax25-apps/call/crc.c0000644000175000017500000000161613521356721012573 0ustar irlirl /* tnt: Hostmode Terminal for TNC Copyright (C) 1993 by Mark Wahl For license details see documentation Procedures for autobin-checksum (crc.c) created: Mark Wahl DL4YBG 94/01/17 updated: Mark Wahl DL4YBG 94/01/17 */ #define _DEFAULT_SOURCE #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED #include "crc.h" static int crcbit[8] = { 0x9188, 0x48c4, 0x2462, 0x1231, 0x8108, 0x4084, 0x2042, 0x1021 }; static int bittab[8] = { 128, 64, 32, 16, 8, 4, 2, 1 }; static int crctab[256]; void init_crc(void) { int i, j; for (i = 0; i < 256; i++) { crctab[i] = 0; for (j = 0; j < 8; j++) { if ((bittab[j] & i) != 0) { crctab[i] = crctab[i] ^ crcbit[j]; } } } } /* calculate checksum for autobin-protocol */ unsigned int calc_crc(unsigned char *buf, int n, unsigned crc) { while (--n >= 0) crc = (crctab[(crc >> 8)] ^ ((crc << 8) | *buf++)) & 0xffff; return crc; } ax25-apps/call/call.h0000644000175000017500000000071413521356721012742 0ustar irlirl#ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif extern int fd; extern volatile int interrupted; extern int paclen; /* In call.c */ extern void convert_crlf(char *, int); extern void convert_lfcr(char *, int); /* In yapp.c */ extern void cmd_yapp(char *, int); /* In dostime.c */ extern void date_unix2dos(time_t, unsigned short*, unsigned short*); extern int yapp2unix(char *); extern void unix2yapp(time_t unix_date, char *buffer); ax25-apps/call/Makefile.am0000644000175000017500000000202313521356721013705 0ustar irlirl vardir = $(sysconfdir)/ax25 installconf: $(mkinstalldirs) $(DESTDIR)$(etcdir) bin_PROGRAMS = call man_MANS = call.1 CLEANFILES = call.1 call.1.tmp EXTRA_DIST = call.man call.1 : call.man name_call=$$(echo call | sed -e '$(transform)') \ name_Call=$$(echo $$name_call | sed -r 's@^(.)@\U\1\E@') && \ name_CALL=$$(echo $$name_call | sed -r 's@^(.*)@\U\1\E@') && \ name_listen=$$(echo listen | sed -e '$(transform)') \ name_Listen=$$(echo $$name_listen | sed -r 's@^(.)@\U\1\E@') && \ name_LISTEN=$$(echo $$name_listen | sed -r 's@^(.*)@\U\1\E@') &&\ sed -e "s/@@@call@@@/$$name_call/g" \ -e "s/@@@Call@@@/$$name_Call/g" \ -e "s/@@@CALL@@@/$$name_CALL/g" \ -e "s/@@@listen@@@/$$name_listen/g" \ -e "s/@@@Listen@@@/$$name_Listen/g" \ -e "s/@@@LISTEN@@@/$$name_LISTEN/g" \ $(srcdir)/call.man > call.1.tmp && \ mv call.1.tmp call.1; call_LDADD = $(NCURSES_LIB) $(AX25_LIB) call_SOURCES = \ call.c \ call.h \ menu.c \ menu.h \ crc.c \ crc.h \ yapp.c \ dostime.c ax25-apps/call/crc.h0000644000175000017500000000011313521356721012567 0ustar irlirlvoid init_crc(void); unsigned int calc_crc(unsigned char*, int, unsigned); ax25-apps/call/dostime.c0000644000175000017500000000443713521356721013474 0ustar irlirl#define _DEFAULT_SOURCE #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED #include #include #include #include #include "call.h" /* MS-DOS time/date conversion routines derived from: */ /* * linux/fs/msdos/misc.c * * Written 1992,1993 by Werner Almesberger */ /* Linear day numbers of the respective 1sts in non-leap years. */ static int day_n[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 }; /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ static int date_dos2unix(unsigned short time, unsigned short date) { int month, year, secs; month = ((date >> 5) & 15) - 1; year = date >> 9; secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653); /* days since 1.1.70 plus 80's leap day */ return secs; } /* Convert linear UNIX date to a MS-DOS time/date pair. */ void date_unix2dos(time_t unix_date, unsigned short *time, unsigned short *date) { int day, year, nl_day, month; *time = (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) + (((unix_date / 3600) % 24) << 11); day = unix_date / 86400 - 3652; year = day / 365; if ((year + 3) / 4 + 365 * year > day) year--; day -= (year + 3) / 4 + 365 * year; if (day == 59 && !(year & 3)) { nl_day = day; month = 2; } else { nl_day = (year & 3) || day <= 59 ? day : day - 1; for (month = 0; month < 12; month++) if (day_n[month] > nl_day) break; } *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9); } /* Convert yapp format 8 hex characters into Unix time */ int yapp2unix(char *ytime) { int i; unsigned short time, date; if (strlen(ytime) != 8) return 0; for (i = 0; i < 8; i++) if (!isxdigit(ytime[i])) return 0; time = strtoul(ytime + 4, (char **) NULL, 16); ytime[4] = 0; date = strtoul(ytime, (char **) NULL, 16); return date_dos2unix(time, date); } /* Convert unix time to 8 character yapp hex format */ void unix2yapp(time_t unix_date, char *buffer) { unsigned short time, date; date_unix2dos(unix_date, &time, &date); sprintf(buffer, "%04X%04X", date, time); } ax25-apps/call/yapp.c0000644000175000017500000003553013521356721012777 0ustar irlirl/* yapp.c * * Copyright (C) 1994 by Jonathan Naylor * * This module implements the YAPP file transfer protocol as defined by Jeff * Jacobsen WA7MBL in the files yappxfer.doc and yappxfer.pas. * * * 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. */ #define _DEFAULT_SOURCE #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED /* * Yapp C and Resume support added by S N Henson. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "call.h" #define TIMEOUT 300 /* 5 Minutes */ #define NUL 0x00 #define SOH 0x01 #define STX 0x02 #define ETX 0x03 #define EOT 0x04 #define ENQ 0x05 #define ACK 0x06 #define DLE 0x10 #define NAK 0x15 #define CAN 0x18 #define STATE_S 0 #define STATE_SH 1 #define STATE_SD 2 #define STATE_SE 3 #define STATE_ST 4 #define STATE_R 5 #define STATE_RH 6 #define STATE_RD 7 static int state; static int total = 0; static int readlen = 0; static int outlen = 0; static int outbufptr = 0; static unsigned char outbuffer[512]; static char yappc; /* Nonzero if using YAPP C */ static void Write_Status(char *s) { fprintf(stdout, "State: %-60s\r", s); fflush(stdout); } static void Send_RR(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = 0x01; write(fd, buffer, 2); } static void Send_RF(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = 0x02; write(fd, buffer, 2); } static void Send_RT(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = ACK; write(fd, buffer, 2); } static void Send_AF(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = 0x03; write(fd, buffer, 2); } static void Send_AT(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = 0x04; write(fd, buffer, 2); } static void Send_NR(char *reason) { char buffer[257]; int length; length = strlen(reason); if (length > 255) length = 255; buffer[0] = NAK; buffer[1] = length; memcpy(buffer + 2, reason, length); write(fd, buffer, length + 2); } /* Send a Resume Sequence */ static void Send_RS(int length) { char buffer[256]; int len; buffer[0] = NAK; buffer[2] = 'R'; buffer[3] = 0; len = sprintf(buffer + 4, "%d", length) + 5; buffer[len] = 'C'; buffer[len + 1] = 0; buffer[1] = len; write(fd, buffer, len + 2); } static void Send_SI(void) { char buffer[2]; buffer[0] = ENQ; buffer[1] = 0x01; write(fd, buffer, 2); } static void Send_CN(char *reason) { char buffer[257]; int length; length = strlen(reason); if (length > 255) length = 255; buffer[0] = CAN; buffer[1] = length; memcpy(buffer + 2, reason, length); write(fd, buffer, length + 2); } static void Send_HD(char *filename, long length) { char buffer[257]; char size_buffer[12]; int len_filename; int len_size; int len; struct stat sb; sprintf(size_buffer, "%ld", length); len_filename = strlen(filename) + 1; /* Include the NUL */ len_size = strlen(size_buffer) + 1; /* Include the NUL */ len = len_filename + len_size; if (!stat(filename, &sb)) { unix2yapp(sb.st_mtime, buffer + len + 2); len += 9; } buffer[0] = SOH; buffer[1] = len; memcpy(buffer + 2, filename, len_filename); memcpy(buffer + len_filename + 2, size_buffer, len_size); write(fd, buffer, len + 2); } static void Send_ET(void) { char buffer[2]; buffer[0] = EOT; buffer[1] = 0x01; write(fd, buffer, 2); } static void Send_DT(int length) { char buffer[2]; if (length > 255) length = 0; buffer[0] = STX; buffer[1] = length; write(fd, buffer, 2); } static void Send_EF(void) { char buffer[2]; buffer[0] = ETX; buffer[1] = 0x01; write(fd, buffer, 2); } static unsigned char checksum(unsigned char *buf, int len) { int i; unsigned char sum = 0; for (i = 0; i < len; i++) sum += buf[i]; return sum; } static int yapp_download_data(int *filefd, unsigned char *buffer) { int length, file_time; char Message[50]; if (buffer[0] == CAN || buffer[0] == NAK) { Write_Status("RcdABORT"); return FALSE; } switch (state) { case STATE_R: if (buffer[0] == ENQ && buffer[1] == 0x01) { Send_RR(); Write_Status("RcvHeader"); state = STATE_RH; break; } Send_CN("Unknown code"); Write_Status("SndABORT"); return FALSE; case STATE_RH: if (buffer[0] == SOH) { /* Parse header: 3 fields == YAPP C */ unsigned char *hptr, *hfield[3]; length = buffer[1]; if (length == 0) length = 256; hptr = buffer + 2; while (length > 0) { int hlen; hlen = strlen((char *)hptr) + 1; hfield[(int) yappc++] = hptr; hptr += hlen; length -= hlen; } if (yappc < 3) { yappc = 0; } else { file_time = yapp2unix((char *)hfield[2]); yappc = 1; } if (*filefd == -1) { *filefd = open((char *)hfield[0], O_RDWR | O_APPEND | O_CREAT, 0666); if (*filefd == -1) { printf("\n[Unable to open %s]\n", hfield[0]); Send_NR("Invalid filename"); return FALSE; } } printf("Receiving %s %s %s", hfield[0], hfield[1], yappc ? ctime((time_t *) & file_time) : " "); if (yappc) { struct stat sb; if (!fstat(*filefd, &sb) && sb.st_size) Send_RS(sb.st_size); else Send_RT(); } else { Send_RF(); } state = STATE_RD; break; } if (buffer[0] == ENQ && buffer[1] == 0x01) { break; } if (buffer[0] == EOT && buffer[1] == 0x01) { Send_AT(); Write_Status("RcvEOT"); return FALSE; } Send_CN("Unknown code"); Write_Status("SndABORT"); return FALSE; case STATE_RD: if (buffer[0] == STX) { length = buffer[1]; if (length == 0) length = 256; total += length; sprintf(Message, "RcvData %5d bytes received", total); Write_Status(Message); if (yappc) { int i; unsigned char checksum = 0; for (i = 0; i < length; i++) checksum += buffer[i + 2]; if (checksum != buffer[length + 2]) { Send_CN("Bad Checksum"); Write_Status ("SndABORT: Bad Checksum"); return FALSE; } } write(*filefd, buffer + 2, length); break; } if (buffer[0] == ETX && buffer[1] == 0x01) { Send_AF(); Write_Status("RcvEof"); state = STATE_RH; close(*filefd); *filefd = -1; break; } Send_CN("Unknown code"); Write_Status("SndABORT"); return FALSE; } return TRUE; } static void yapp_download(int filefd) { struct timeval timeval; fd_set sock_read; int n; int buflen = 0; int length; int used; unsigned char buffer[1024]; Write_Status("RcvWait"); state = STATE_R; total = 0; yappc = 0; while (TRUE) { FD_ZERO(&sock_read); FD_SET(STDIN_FILENO, &sock_read); FD_SET(fd, &sock_read); timeval.tv_usec = 0; timeval.tv_sec = TIMEOUT; n = select(fd + 1, &sock_read, NULL, NULL, &timeval); if (n == -1) { if (!interrupted && errno == EAGAIN) continue; if (!interrupted) perror("select"); Send_CN("Internal error"); Write_Status("SndABORT"); return; } if (n == 0) { /* Timeout */ Send_CN("Timeout"); Write_Status("SndABORT"); return; } if (FD_ISSET(STDIN_FILENO, &sock_read)) { Send_CN("Cancelled by user"); Write_Status("SndABORT"); return; } if (FD_ISSET(fd, &sock_read)) { length = read(fd, buffer + buflen, 511); if (length > 0) { buflen += length; do { used = FALSE; switch (buffer[0]) { case ACK: case ENQ: case ETX: case EOT: if (buflen >= 2) { if (!yapp_download_data(&filefd, buffer)) return; buflen -= 2; memcpy(buffer, buffer + 2, buflen); used = TRUE; } break; default: length = buffer[1]; if (length == 0) length = 256; if (buffer[0] == STX) length += yappc; if (buflen >= (length + 2)) { if (!yapp_download_data(&filefd, buffer)) return; buflen -= length + 2; memcpy(buffer, buffer + length + 2, buflen); used = TRUE; } break; } } while (used); } } } } static int yapp_upload_data(int filefd, char *filename, int filelength, unsigned char *buffer) { char Message[80]; if (buffer[0] == CAN || buffer[0] == NAK) { Write_Status("RcvABORT"); return FALSE; } switch (state) { case STATE_S: if (buffer[0] == ACK && buffer[1] == 0x01) { Write_Status("SendHeader"); Send_HD(filename, filelength); state = STATE_SH; break; } if (buffer[0] == ACK && buffer[1] == 0x02) { sprintf(Message, "SendData %5d bytes transmitted", total); Write_Status(Message); outlen = read(filefd, outbuffer, readlen); outbufptr = 0; if (outlen) Send_DT(outlen); if (yappc) { outbuffer[outlen] = checksum(outbuffer, outlen); outlen++; } state = STATE_SD; break; } Send_CN("Unknown code"); Write_Status("SndABORT"); return FALSE; case STATE_SH: /* Could get three replies here: * ACK 02 : normal acknowledge. * ACK ACK: yappc acknowledge. * NAK ...: resume request. */ if (buffer[0] == NAK && buffer[2] == 'R') { int len; off_t rpos; len = buffer[1]; if (buffer[len] == 'C') yappc = 1; rpos = atol((char *)buffer + 4); lseek(filefd, rpos, SEEK_SET); buffer[0] = ACK; buffer[1] = yappc ? ACK : 0x02; } if (buffer[0] == ACK && (buffer[1] == 0x02 || buffer[1] == ACK)) { if (buffer[1] == ACK) yappc = 1; sprintf(Message, "SendData %5d bytes transmitted", total); Write_Status(Message); outlen = read(filefd, outbuffer, readlen); outbufptr = 0; if (outlen) Send_DT(outlen); state = STATE_SD; if (yappc) { outbuffer[outlen] = checksum(outbuffer, outlen); outlen++; } break; } Send_CN("Unknown code"); Write_Status("SndABORT"); return FALSE; case STATE_SD: Send_CN("Unknown code"); Write_Status("SndABORT"); return FALSE; case STATE_SE: if (buffer[0] == ACK && buffer[1] == 0x03) { Write_Status("SendEOT"); Send_ET(); state = STATE_ST; break; } Send_CN("Unknown code"); Write_Status("SndABORT"); return FALSE; case STATE_ST: if (buffer[0] == ACK && buffer[1] == 0x04) { return FALSE; } Send_CN("Unknown code"); Write_Status("SndABORT"); return FALSE; } return TRUE; } static void yapp_upload(int filefd, char *filename, long filelength) { struct timeval timeval; fd_set sock_read; fd_set sock_write; int n; unsigned char buffer[1024]; int buflen = 0; int length; int used; char Message[80]; Write_Status("SendInit"); readlen = (paclen - 2 > 253) ? 253 : paclen - 2; state = STATE_S; total = 0; yappc = 0; Send_SI(); while (TRUE) { FD_ZERO(&sock_read); FD_ZERO(&sock_write); FD_SET(STDIN_FILENO, &sock_read); FD_SET(fd, &sock_read); if (state == STATE_SD) { FD_SET(fd, &sock_write); n = select(fd + 1, &sock_read, &sock_write, NULL, NULL); } else { timeval.tv_usec = 0; timeval.tv_sec = TIMEOUT; n = select(fd + 1, &sock_read, NULL, NULL, &timeval); } if (n == -1) { if (!interrupted && errno == EAGAIN) continue; if (!interrupted) perror("select"); Write_Status("SndABORT"); Send_CN("Internal error"); return; } if (n == 0) { /* Timeout, not STATE_SD */ Write_Status("SndABORT"); Send_CN("Timeout"); return; } if (FD_ISSET(STDIN_FILENO, &sock_read)) { Write_Status("SndABORT"); Send_CN("Cancelled by user"); return; } if (FD_ISSET(fd, &sock_write)) { /* Writable, only STATE_SD */ if (outlen > 0) { n = write(fd, outbuffer + outbufptr, outlen); if (n > 0) { outbufptr += n; outlen -= n; total += n; } } if (outlen == 0) { total -= yappc; if ((outlen = read(filefd, outbuffer, readlen)) > 0) { sprintf(Message, "SendData %5d bytes transmitted", total); Write_Status(Message); outbufptr = 0; Send_DT(outlen); if (yappc) { outbuffer[outlen] = checksum(outbuffer, outlen); outlen++; } } else { Write_Status("SendEof"); state = STATE_SE; Send_EF(); } } } if (FD_ISSET(fd, &sock_read)) { length = read(fd, buffer + buflen, 511); if (length > 0) { buflen += length; do { used = FALSE; switch (buffer[0]) { case ACK: case ENQ: case ETX: case EOT: if (buflen >= 2) { if (!yapp_upload_data(filefd, filename, filelength, buffer)) return; buflen -= 2; memcpy(buffer, buffer + 2, buflen); used = TRUE; } break; default: length = buffer[1]; if (length == 0) length = 256; if (buflen >= (length + 2)) { if (!yapp_upload_data(filefd, filename, filelength, buffer)) return; buflen -= length + 2; memcpy(buffer, buffer + length + 2, buflen); used = TRUE; } break; } } while (used); } } } } void cmd_yapp(char *buf, int bytes) { int filefd; long size = 0L; char *t; if (bytes == 0) return; switch (buf[0]) { case 'U': case 'u': t = strchr(buf, '\n'); if (t != NULL) *t = '\0'; t = buf + 2; while (*t != '\0' && isspace(*t)) t++; if (*t == '\0') { printf ("\n[YAPP Upload requires a filename - eg ~yu hello.txt]\n"); Send_NR("No filename"); return; } filefd = open(t, O_RDONLY); if (filefd == -1) { printf("\n[Unable to open upload file]\n"); Send_NR("Invalid filename"); return; } if (lseek(filefd, 0L, SEEK_END) != -1) size = lseek(filefd, 0L, SEEK_CUR); lseek(filefd, 0L, SEEK_SET); if (size != -1) printf ("\n[Uploading %ld bytes from %s using YAPP]\n", size, t); else printf("\n[Uploading from %s using YAPP]\n", t); yapp_upload(filefd, t, size); close(filefd); printf("[Finished YAPP Upload, %d bytes Transmitted]\n", total); break; case 'D': case 'd': t = strchr(buf, '\n'); if (t != NULL) *t = '\0'; t = buf + 2; while (*t != '\0' && isspace(*t)) t++; if (*t == '\0') filefd = -1; else {filefd = open(t, O_RDWR | O_APPEND | O_CREAT, 0666); if (filefd == -1) { printf("\n[Unable to open %s]\n", buf + 2); Send_NR("Invalid filename"); return; } } printf("\n[Downloading using YAPP]\n"); yapp_download(filefd); close(filefd); printf("[Finished YAPP Download, %d bytes received]\n", total); break; default: printf("\nUnknown '~y' escape. Type ~h for a list.\n"); break; } } ax25-apps/call/menu.c0000644000175000017500000001631013521356721012765 0ustar irlirl/* * menu (c)1995 Alexander Tietzel (DG6XA) * little Menu-System for use with ncurses * date activity autor * 22.07.1995 wininfo->wint (vector->single chain) Alexander Tietzel (DG6XA) * 25.07.1995 some minor changes Alexander Tietzel (DG6XA) */ #define _DEFAULT_SOURCE #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED #include #include #include #include "menu.h" typedef struct { char *st_ptr; int xpos; char key; } topmenuitem; WINDOW *winopen(wint * wtab, int nlines, int ncols, int begin_y, int begin_x, int border) { while (wtab->next != NULL) wtab = wtab->next; wtab->next = (wint *) malloc(sizeof(wint)); wtab = wtab->next; wtab->next = NULL; wtab->ptr = newwin(nlines, ncols, begin_y, begin_x); if (wtab->ptr == NULL) return NULL; wtab->fline = begin_y; wtab->lline = begin_y + nlines; if (border) wborder(wtab->ptr, ACS_VLINE, ACS_VLINE, ACS_HLINE, ACS_HLINE, ACS_ULCORNER, ACS_URCORNER, ACS_LLCORNER, ACS_LRCORNER); return wtab->ptr; } void winclose(wint * wtab) { wint *awin; wint *lwin; int awin_lines; if (wtab->next == NULL) return; lwin = wtab; while (lwin->next->next != NULL) lwin = lwin->next; awin = lwin->next; awin_lines = awin->lline - awin->fline; while (wtab->next != NULL) { if (wtab->fline >= 0 && wtab->lline >= 0 && awin->lline >= wtab->fline && awin->fline <= wtab->lline) { if (wtab->fline <= awin->fline) { if (wtab->lline < awin->lline) { wtouchln(wtab->ptr, awin->fline - wtab->fline, awin_lines - (awin->lline - wtab->lline), 1); } else { wtouchln(wtab->ptr, awin->fline - wtab->fline, awin_lines, 1); } } else { wtouchln(wtab->ptr, 0, awin_lines - wtab->fline + awin->fline, 1); } wnoutrefresh(wtab->ptr); } wtab = wtab->next; } doupdate(); lwin->next = NULL; free(awin); } void menu_write_line(WINDOW * win, int ypos, int menu_breite, int reverse, char st[]) { int cnt; int high = FALSE; if (reverse) wattron(win, A_REVERSE); wmove(win, ypos + 1, 1); for (cnt = 0; st[cnt] != 0; cnt++) { if (st[cnt] == '~') { if (!reverse) { wattron(win, A_BOLD); high = TRUE; } } else { waddch(win, st[cnt]); if (high == TRUE) { high = FALSE; wattroff(win, A_BOLD); } } } for (cnt = strlen(st); cnt < menu_breite; cnt++) waddch(win, ' '); if (reverse) wattroff(win, A_REVERSE); } int p_dwn_menu(wint * wtab, menuitem * menustr, int starty, int startx) { int str_max_length = 0; int cnt = 0; int ypos, oldypos; int lines = 0; int c; WINDOW *menuwin; for (lines = 0; (*(menustr[lines].st_ptr) != 0); lines++) { if (strlen(menustr[lines].st_ptr) > str_max_length) str_max_length = strlen(menustr[lines].st_ptr); } menuwin = winopen(wtab, lines + 2, str_max_length + 1, starty, startx, TRUE); wrefresh(menuwin); menu_write_line(menuwin, 0, str_max_length, TRUE, menustr[0].st_ptr); for (ypos = 1; ypos < lines; ypos++) menu_write_line(menuwin, ypos, str_max_length, FALSE, menustr[ypos].st_ptr); wrefresh(menuwin); ypos = 0; do { while ((c = getch()) == ERR); oldypos = ypos; switch (c) { case KEY_DOWN: if (++ypos >= lines) ypos = 0; break; case KEY_UP: if (ypos == 0) ypos = lines - 1; else ypos--; break; default: if ((char) c >= 'a' && (char) c <= 'z') c -= 'a' - 'A'; for (cnt = 0; menustr[cnt].key != (char) c && cnt < lines; cnt++); if (menustr[cnt].key == (char) c) ypos = cnt; break; } if (ypos != oldypos) { menu_write_line(menuwin, ypos, str_max_length, TRUE, menustr[ypos].st_ptr); menu_write_line(menuwin, oldypos, str_max_length, FALSE, menustr[oldypos].st_ptr); wrefresh(menuwin); } } while (c != KEY_ENTER && c != '\r' && c != '\n' && c != KEY_RIGHT && c != KEY_LEFT && c != 0x1b); delwin(menuwin); winclose(wtab); if (c == 0x1b) return 0; if (c == KEY_RIGHT || c == KEY_LEFT) return c; else return ypos + 1; } void menu_write_item(WINDOW * win, int xpos, int reverse, const char st[]) { int cnt; int high = FALSE; if (reverse) wattron(win, A_REVERSE); wmove(win, 1, xpos + 1); for (cnt = 0; st[cnt] != 0; cnt++) { if (st[cnt] == '~') { if (!reverse) { wattron(win, A_BOLD); high = TRUE; } } else { waddch(win, st[cnt]); if (high) { high = FALSE; wattroff(win, A_BOLD); } } } if (reverse) wattroff(win, A_REVERSE); } int top_menu(wint * wtab, menuitem menustr[], int ystart) { int str_max_length = 0; int str_length = 0; int cnt; int xpos, oldxpos; int ypos = 0; int items = 0; int c; WINDOW *menuwin; int items_xpos[12]; curs_set(0); /*cursor visibility off */ for (items = 0; *(menustr[items].st_ptr) != 0; items++) { if (items == 0) items_xpos[0] = 1; else items_xpos[items] = items_xpos[items - 1] + str_length; if (strlen(menustr[items].st_ptr) > str_max_length) str_max_length = strlen(menustr[items].st_ptr); str_length = strlen(menustr[items].st_ptr) + 1; } menuwin = winopen(wtab, 3, 80, ystart, 0, TRUE); wrefresh(menuwin); menu_write_item(menuwin, 1, TRUE, menustr[0].st_ptr); for (xpos = 1; xpos < items; xpos++) menu_write_item(menuwin, items_xpos[xpos], FALSE, menustr[xpos].st_ptr); wrefresh(menuwin); xpos = 0; do { while ((c = getch()) == ERR); oldxpos = xpos; switch (c) { case KEY_RIGHT: if (++xpos >= items) xpos = 0; break; case KEY_LEFT: if (xpos == 0) xpos = items - 1; else xpos--; break; case KEY_DOWN: case KEY_ENTER: case '\r': case '\n': ypos = 0; do { switch (ypos) { case KEY_RIGHT: if (++xpos >= items) xpos = 0; menu_write_item(menuwin, items_xpos[xpos], TRUE, menustr[xpos]. st_ptr); menu_write_item(menuwin, items_xpos [oldxpos], FALSE, menustr[oldxpos]. st_ptr); wrefresh(menuwin); oldxpos = xpos; break; case KEY_LEFT: if (xpos == 0) xpos = items - 1; else xpos--; menu_write_item(menuwin, items_xpos[xpos], TRUE, menustr[xpos]. st_ptr); menu_write_item(menuwin, items_xpos [oldxpos], FALSE, menustr[oldxpos]. st_ptr); wrefresh(menuwin); oldxpos = xpos; break; } ypos = p_dwn_menu(wtab, (menuitem *) menustr[xpos]. arg, ystart + 2, items_xpos[xpos] + 1); touchwin(menuwin); wrefresh(menuwin); } while (ypos == KEY_RIGHT || ypos == KEY_LEFT); break; default: if ((char) c >= 'a' && (char) c <= 'z') c -= 'a' - 'A'; for (cnt = 0; menustr[cnt].key != (char) c && cnt <= items; cnt++); if (menustr[cnt].key == (char) c) xpos = cnt; } if (xpos != oldxpos) { menu_write_item(menuwin, items_xpos[xpos], TRUE, menustr[xpos].st_ptr); menu_write_item(menuwin, items_xpos[oldxpos], FALSE, menustr[oldxpos].st_ptr); wrefresh(menuwin); } } while (ypos == 0 && c != 0x1b); delwin(menuwin); curs_set(1); winclose(wtab); if (c == 0x1b) return 0; return (ypos & 0x0F) | ((xpos & 0x07) << 4); } ax25-apps/call/call.c0000644000175000017500000020642013521356721012737 0ustar irlirl/* 03.04.1995 add binary download "#BIN#-Protocol" Alexander Tietzel */ /* 01.05.1995 talkmode Alexander Tietzel (DG6XA) */ /* 15.07.1995 Pull-Down-Menus Alexander Tietzel (DG6XA) */ /* 17.07.1995 auto7+ newer #BIN#-Protocol Alexander Tietzel(DG6XA) */ /* 18.07.1995 Remote commands Alexander Tietzel (DG6XA) */ /* 19.07.1995 statusline Alexander Tietzel (DG6XA) */ /* 25.07.1995 some bug-fixes Alexander Tietzel (DG6XA) */ /* 14.08.1995 merged with mainstream call.c code Jonathan Naylor (G4KLX) */ /* 01.03.1996 support for different screen sizes, fixed 7plus download (DL1BKE) */ /* 19.08.1996 fixed enter key handling (G4KLX) */ /* 27.08.1996 added Rose support (G4KLX) */ /* 30.11.1996 added the called user in the call windows and set talk mode as default (IW0FBB) */ /* 07.12.1996 updated status line to cope with callsign, bits and status message (VK2KTJ) */ /* 02.02.1997 removed NETROM_PACLEN setting to match Jonathon removing it from kernel (VK2KTJ) */ #define _DEFAULT_SOURCE #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED #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 #include #include #include #include #include #include #include "../pathnames.h" #include "call.h" #include "crc.h" #include "menu.h" #define CTRL_C 0x03 #define MAX_PACKETLEN 512 #define MAX_BUFLEN 2*MAX_PACKETLEN #define MAX_CMPSTRLEN MAX_PACKETLEN #define STD_DWN_DIR "/var/ax25/" #define FLAG_RECONNECT 0x01 #define STATW_BITS 12 #define STATW_STAT 20 static int backoff = -1; static int ax25mode = -1; static int debug = FALSE; static int af_mode = AF_AX25; static int window; static int be_silent; static char *port; static char *mycall; static int stdin_is_tty = 1; static iconv_t ibm850toutf8, wcharttoibm850, wcharttoutf8, utf8towchart; volatile int interrupted = FALSE; static int sigwinchsignal; int paclen; int fd; static int curson = 1; static int wait_for_remote_disconnect = FALSE; static struct timespec inactivity_timeout; static int inactivity_timeout_is_set = FALSE; static int remote_commands_enabled = TRUE; #define GP_FILENAME_SIZE 255 typedef struct { char file_name[GP_FILENAME_SIZE]; unsigned long dwn_cnt; int dwn_file; int file_crc; int calc_crc; struct utimbuf ut; int new_header; } t_gp; typedef struct { WINDOW *ptr; int max_y; int max_x; wchar_t string[MAX_BUFLEN]; unsigned long bytes; int curs_pos; } t_win; #define TALKMODE 001 /* two windows (outgoing and incoming) with menu */ #define SLAVEMODE 002 /* Menu mode */ #define RAWMODE 004 /* mode used by earlier versions */ #define ORIGINALENCODING 0 #define UTF8ENCODING 1 #define SCROLLBACKSIZE 5000 typedef struct { char *str; int len; }scrollbackstruct; static scrollbackstruct scrollback[SCROLLBACKSIZE]; static int topscroll, lastscroll, scrolledup, eatchar; static char inbuf[MAX_BUFLEN]; static int inbuflen, inbufwid; static char incharbuf[6]; static int incharbuflen; static void addscrollline(char *s, int len) { scrollback[lastscroll].str = malloc(len); memcpy(scrollback[lastscroll].str, s, len); scrollback[lastscroll].len = len; if (++lastscroll >= SCROLLBACKSIZE) lastscroll = 0; if (lastscroll == topscroll) { free(scrollback[topscroll].str); if (++topscroll >= SCROLLBACKSIZE) topscroll = 0; } } /* * Notes: Japanese, Chinese and Korean characters mostly take up two characters * in width. These characters return 2 via wcwidth to let the code know that * they require two spaces. Mono-space fonts/ncurses seems to work correctly * with East Asian characters. * * There are also some characters that return a wcwidth of 0. I'm not sure * about all of them, but some of the 0 width characters add a mark above or * below a character. In order for these marks to appear correctly, the * previous character and the overstrike must be drawn together. using wmove * and drawing the accent doesn't work. * * There are some other characters that have functions, and these are not * supported using a print to the screen. South Asian fonts are complicated. I * don't believe south asian fonts work correctly from the Linux command line * (as of late 2009). They print but don't combine. The result is distorted. * * Many characters, as of 12/09, are too wide for a single space in the font, * although they return a wcwidth of 1. I suspect this is due to these * characters not being part of the mono-spaced font and are instead pulled * from an alternate font. These characters also glitch in xterm, vim and * other ncurses software. I suspect this is actually a font bug, and any * character that returns wcwidth=1 in a monospaced font should be monospaced. */ static int widthchar(char *s, size_t bytes, int xpos) { wchar_t c; int width; char *outbuf=(char *) &c; size_t outsize = sizeof(wchar_t); /* * Note: Actually need to check if bad UTF8 characters show as ? */ if (iconv(utf8towchart, &s, &bytes, &outbuf, &outsize) == (size_t) -1) return 0; if (c == 9) { return 8 - (xpos & 7); } width = wcwidth(c); if (width < 0) return 0; return width; } static int completecharlen(char *s) { unsigned ut = (unsigned char)s[0]; int clen; if (ut <= 0x80) clen = 1; else if ((ut >= 192) && (ut < 192+32)) clen = 2; else if ((ut >= 224) && (ut < 224+16)) clen = 3; else if ((ut >= 240) && (ut < 240+8)) clen = 4; else if ((ut >= 248) && (ut < 248+4)) clen = 5; else if ((ut >= 252) && (ut < 252+2)) clen = 6; else clen = 1; /* bad */ return clen; } /* * Must check for COLS while redrawing from history. Or otherwise the text * wraps around and does strange things. */ static int waddnstrcolcheck(WINDOW *win, char *s, int len, int twidth) { int n; for (twidth = 0,n=0;n len) /* Error condition */ return twidth; width = widthchar(&s[n], cwidth, twidth); if (twidth+width > COLS) return twidth; waddnstr(win, &s[n], cwidth); n += cwidth; twidth += width; } return twidth; } /* * Update a line on the screen from the backscroll buffer. */ static void updateline(int screeny, int yfrom, t_win *win_in, int mode, t_win *win_out) { wmove(win_in->ptr, screeny, 0); if (yfrom == lastscroll) { int twidth = 0; if (inbuflen > 0) twidth = waddnstrcolcheck(win_in->ptr, inbuf, inbuflen, 0); if (mode == SLAVEMODE) { char obuf[MAX_BUFLEN]; char *inbuf = (char *)win_out->string, *outbuf = obuf; size_t insize = win_out->bytes * sizeof(wchar_t), outsize = MAX_BUFLEN; iconv(wcharttoutf8, &inbuf, &insize, &outbuf, &outsize); waddnstrcolcheck(win_in->ptr, obuf, MAX_BUFLEN - outsize, twidth); win_out->curs_pos = win_out->bytes; } } else { waddnstrcolcheck(win_in->ptr, scrollback[yfrom].str, scrollback[yfrom].len, 0); } } /* * Cursor in SLAVE mode while scrolling looks broken. * Cursor in TALK mode is always good, because it's on the bottom window */ static void checkcursor(int mode) { int newcursor; if ((mode == SLAVEMODE) && scrolledup) newcursor = 0; else newcursor = 1; if (curson != newcursor) { curs_set(newcursor); curson = newcursor; } } /* For CJK, it's important to keep the cursor always on the input * window. Otherwise the display is confused */ static void restorecursor(int mode, t_win *win_out) { checkcursor(mode); if (mode != RAWMODE) { int x,y; getyx(win_out->ptr, y, x); /* Must restore input cursor */ wmove(win_out->ptr,y, x); /* location. */ wrefresh(win_out->ptr); } } static void redrawscreen(t_win *win_in, int mode, t_win *win_out) { int y, storedlines; if (lastscroll >= topscroll) storedlines = lastscroll - topscroll; else storedlines = lastscroll + SCROLLBACKSIZE - topscroll; /* * Note it's stored lines + 1 extra line for text input. */ for (y=0;(y<=win_in->max_y) && (y <= storedlines);y++) { int linefrom; if (storedlines <= win_in->max_y) { /* * This is a little confusing. * The screen scrolls top down at start */ linefrom = topscroll + y; } else linefrom = lastscroll -scrolledup - (win_in->max_y) + y; while (linefrom < 0) linefrom += SCROLLBACKSIZE; while (linefrom >= SCROLLBACKSIZE) linefrom -= SCROLLBACKSIZE; updateline(y,linefrom , win_in, mode, win_out); } checkcursor(mode); } static void statline(int mode, char *s) { static int oldlen = 0; int l, cnt; if (*s == '\0') { if (mode == RAWMODE) return; if (oldlen > 0) { move(0, STATW_STAT); attron(A_REVERSE); for (cnt = STATW_STAT; cnt < COLS; cnt++) addch(' '); oldlen = 0; attroff(A_REVERSE); refresh(); } return; } if (mode == RAWMODE) { printf(">>%s\n", s); fflush(stdout); return; } if (COLS <= STATW_STAT) return; l = strlen(s); if (l > COLS - STATW_STAT) l = COLS-STATW_STAT; move(0, STATW_STAT); attron(A_REVERSE); addnstr(s, l); for (cnt = STATW_STAT+l;cnt < COLS;cnt++) addch(' '); attroff(A_REVERSE); oldlen = l; refresh(); } static void scrolltext(t_win *win_in, int lines, int mode, t_win *win_out) { int topline, storedlines; int y; int wasscrolledup; if (scrolledup + lines < 0) { lines = -scrolledup; } /* * storedlines = Lines stored in buffer. */ if (lastscroll >= topscroll) storedlines = lastscroll - topscroll; else storedlines = lastscroll + SCROLLBACKSIZE - topscroll; /* * The max scrolling we can do is the # of lines stored - the * screen size. */ topline = storedlines - win_in->max_y; if (topline < 0) topline = 0; if (scrolledup + lines > topline) { lines = topline - scrolledup; } if (!lines) return; wasscrolledup = scrolledup; scrolledup += lines; wscrl(win_in->ptr, -lines); scrollok(win_in->ptr, FALSE); if (lines > 0) { for (y=0;ymax_y) + y; while (linefrom < 0) linefrom += SCROLLBACKSIZE; while (linefrom >= SCROLLBACKSIZE) linefrom -= SCROLLBACKSIZE; updateline(y,linefrom , win_in, mode, win_out); } } else { for (y=-lines-1;y>=0;y--) { int linefrom = lastscroll -scrolledup - y; while (linefrom < 0) linefrom += SCROLLBACKSIZE; while (linefrom >= SCROLLBACKSIZE) linefrom -= SCROLLBACKSIZE; updateline(win_in->max_y - y,linefrom , win_in, mode, win_out); } } scrollok(win_in->ptr, TRUE); checkcursor(mode); wrefresh(win_in->ptr); if (wasscrolledup && !scrolledup) { statline(mode, ""); } else if (!wasscrolledup && scrolledup) { statline(mode, "Viewing Scrollback"); } } static void usage(void) { fprintf(stderr, "usage: call [-8] [-b l|e] [-d] [-h] [-i] [-m s|e] [-p paclen] [-r] [-R]\n"); fprintf(stderr, " [-s mycall] [-S] [-t] [-T timeout] [-v] [-w window] [-W]\n"); fprintf(stderr, " port callsign [[via] digipeaters...]\n"); exit(1); } static WINDOW *win; static const char *key_words[] = { "//", "#BIN#", " go_7+. ", " stop_7+. ", "\0" }; static const char *rkey_words[] = { /* * actually restricted keywords are very restrictive */ "\0" }; static void convert_cr_lf(char *buf, int len) { while (len--) { if (*buf == '\r') *buf = '\n'; buf++; } } static void convert_lf_cr(char *buf, int len) { while (len--) { if (*buf == '\n') *buf = '\r'; buf++; } } static void convert_upper_lower(char *buf, int len) { while (len--) { *buf = tolower(*buf); buf++; } } /* Return the with of this character in character blocks. (Normal = 1, CJK=2) * Also for control chracters, return the width of the replacement string. * */ static int wcwidthcontrol(wchar_t c) { int width; cchar_t cc = {0}; wchar_t *str; cc.chars[0] = c; str = wunctrl(&cc); if (!str) return 0; width = wcswidth(str, wcslen(str)); return width; } /* Return a string to print for a wchar_t. Expand control characters. * Strings returned by wunctrl don't like to be freed. It seems. */ static wchar_t *wunctrlwchar(wchar_t c) { cchar_t cc = {0}; wchar_t *str; cc.chars[0] = c; str = wunctrl(&cc); return str; } /* * For some reason wins_nwstr fails on fedora 12 on some characters but * waddnstr works. Draw the entire input buffer when adding text. * The fonts that do overstrike fail when written one char at a time. */ static void drawinbuf(WINDOW *w, wchar_t *string, int bytes, int cur_pos) { int n, x, cursorx, xpos, ypos, width; /* * Assume cursor to be at position of current char to draw. */ getyx(w, ypos, xpos); x = xpos; cursorx = xpos; // cur_pos-1 = the chracter that was just added. for (n=cur_pos-2;n>=0;n--) { /* * Move x position to start of string or 0 */ width = wcwidthcontrol(string[n]); if (x >= width) x -= width; else x = 0; } wmove(w, ypos, x); for (n=0;n>%s\n", s); fflush(stdout); return NULL; } win = winopen(wintab, lines, cols, ((LINES - 1) - lines) / 2, ((COLS) - cols) / 2, TRUE); mvwaddstr(win, 1, 1 + (cols - strlen(s)) / 2, s); wmove(win, 3, 2); return win; } static void wrdstatw(WINDOW * win, char s[]) { int y; if (win == NULL) { printf(" %s\n", s); fflush(stdout); return; } waddstr(win, s); y = getcury(win); wmove(win, y + 1, 2); wrefresh(win); } static void dupdstatw(WINDOW * win, char *s, int add) { static char infostr[80]; static int y, x; static int oldlen; int l, cnt; if (add) { oldlen = 0; memcpy(infostr, s, sizeof(infostr)-1); infostr[sizeof(infostr)-1] = 0; if (win == NULL) { printf(" %s", s); fflush(stdout); return; } waddstr(win, s); getyx(win, y, x); wrefresh(win); return; } if (win == NULL) { printf("\r %s%s", infostr, s); fflush(stdout); } else { mvwaddstr(win, y, x, s); } if (oldlen > strlen(s)) { l = oldlen - strlen(s); for (cnt = 0; cnt < l; cnt++) if (win == NULL) { printf(" "); fflush(stdout); } else waddch(win, ' '); } if (win == NULL) { fflush(stdout); } else { wrefresh(win); } oldlen = strlen(s); } static int start_ab_download(int mode, WINDOW ** swin, wint * wintab, char parms[], int parmsbytes, char buf[], int bytes, t_gp * gp, char *address[]) { int crcst; /* startposition crc-field */ int datest = 0; /* startposition date-field */ int namest = 0; /* startposition name-field */ int cnt; int date = 0; struct tm ft; char s[GP_FILENAME_SIZE + 18]; int time_set = 0; for (crcst = 2; crcst < parmsbytes - 1 && (!(parms[crcst - 2] == '#' && parms[crcst - 1] == '|')); crcst++); if (crcst < parmsbytes - 1) { gp->file_crc = atoi(parms + crcst); for (datest = crcst; datest < parmsbytes - 1 && (!(parms[datest - 2] == '#' && parms[datest - 1] == '$')); datest++); if (datest < parmsbytes -1) { date = (int) strtol(parms + datest, NULL, 16); ft.tm_sec = (date & 0x1F) * 2; date >>= 5; ft.tm_min = date & 0x3F; date >>= 6; ft.tm_hour = date & 0x1F; date >>= 5; ft.tm_mday = date & 0x1F; date >>= 5; ft.tm_mon = (date & 0x0F) -1; date >>= 4; ft.tm_year = (date & 0x7F) + 80; ft.tm_isdst = -1; ft.tm_yday = -1; ft.tm_wday = -1; gp->ut.actime = mktime(&ft); gp->ut.modtime = gp->ut.actime; time_set = 1; } else { datest = crcst + 1; } for (namest = datest; namest < parmsbytes - 1 && (parms[namest - 1] != '#'); namest++); } if (!time_set) { time_t t = time(NULL); memcpy(&ft, localtime(&t), sizeof(struct tm)); gp->ut.actime = mktime(localtime(&t)); gp->ut.modtime = gp->ut.actime; } gp->dwn_cnt = (unsigned long ) atol(parms); strncpy(gp->file_name, STD_DWN_DIR, GP_FILENAME_SIZE-1); gp->file_name[GP_FILENAME_SIZE-1] = 0; if (crcst == parmsbytes - 1 || datest - crcst > 7 || namest - datest > 10) { *swin = opnstatw(mode, wintab, "Remote starts AutoBin transfer", 6, 52); gp->new_header = FALSE; wrdstatw(*swin, "old styled Header (no filename)"); if (strlen(gp->file_name) + strlen(address[0]) < GP_FILENAME_SIZE -1) strcat(gp->file_name, address[0]); else return -1; if (strlen(gp->file_name) + strlen(".dwnfile") < GP_FILENAME_SIZE -1) strcat(gp->file_name, ".dwnfile"); else return -1; } else { int len; gp->new_header = TRUE; for (cnt = parmsbytes - namest; cnt > 0 && !(parms[cnt + namest - 1] == '\\' || parms[cnt + namest - 1] == '/'); cnt--); len = parmsbytes - namest - cnt; if (len < 1) return -1; if (len > sizeof(s) -1) len = sizeof(s) -1; strncpy(s, &parms[namest + cnt], len); s[len] = 0; if (*s == '\r') return -1; /* convert_upper_lower(s, len); */ if (strlen(gp->file_name) + len < GP_FILENAME_SIZE - 1) strcat(gp->file_name, s); else return -1; *swin = opnstatw(mode, wintab, "Remote starts AutoBin transfer", 10, 52); sprintf(s, "size of file : %lu", (unsigned long) gp->dwn_cnt); wrdstatw(*swin, s); snprintf(s, sizeof(s), "filename : %s", gp->file_name); wrdstatw(*swin, s); sprintf(s, "last mod. date : %02i.%02i.%04i", ft.tm_mday, ft.tm_mon+1 , ft.tm_year + 1900); wrdstatw(*swin, s); sprintf(s, "last mod. time : %02i:%02i:%02i", ft.tm_hour, ft.tm_min, ft.tm_sec); wrdstatw(*swin, s); } dupdstatw(*swin, "Bytes to receive: ", TRUE); gp->dwn_file = open(gp->file_name, O_RDWR | O_CREAT, 0666); if (gp->dwn_file == -1) { snprintf(s, sizeof(s), "Unable to open %s", gp->file_name); statline(mode, s); if (write(fd, "#ABORT#\r", 8) == -1) { perror("write"); } gp->dwn_cnt = 0; gp->file_name[0] = '\0'; return -1; } if (bytes == 1) { if (write(fd, "#OK#\r", 5) == -1) { perror("write"); close(gp->dwn_file); gp->dwn_file = -1; gp->dwn_cnt = 0; gp->file_name[0] = '\0'; return -1; } gp->calc_crc = 0; } else { unsigned long offset = 0L; while (offset != bytes) { int ret = write(gp->dwn_file, buf+offset, bytes-offset); if (ret == -1) { perror("write"); if (errno == EWOULDBLOCK || errno == EAGAIN) { usleep(100000); continue; } close(gp->dwn_file); gp->dwn_file = -1; gp->dwn_cnt = 0; gp->file_name[0] = '\0'; return -1; } if (ret == 0) { close(gp->dwn_file); gp->dwn_file = -1; gp->dwn_cnt = 0; gp->file_name[0] = '\0'; return -1; break; } gp->calc_crc = calc_crc((unsigned char *) buf, ret, 0); gp->dwn_cnt -= ret; offset += ret; } } return 0; } static int ab_down(int mode, WINDOW * swin, wint * wintab, char buf[], int *bytes, t_gp * gp) { unsigned long extrach = 0; char s[80]; if (*bytes == 8 && strncmp(buf, "#ABORT#\r", 8) == 0) { gp->dwn_cnt = 0; close(gp->dwn_file); gp->dwn_file = -1; statline(mode, "Remote aborts AutoBin transfer!"); gp->file_name[0] = '\0'; *bytes = 0; if (mode != RAWMODE) { delwin(swin); winclose(wintab); } else { printf("\n"); fflush(stdout); } return 0; } if (gp->dwn_cnt < *bytes) { extrach = *bytes - gp->dwn_cnt; *bytes = gp->dwn_cnt; } if (write(gp->dwn_file, buf, *bytes) != *bytes) { close(gp->dwn_file); gp->dwn_file = -1; gp->dwn_cnt = 0; gp->file_name[0] = '\0'; statline(mode, "Error while writing download file. Download aborted."); if (mode != RAWMODE) { delwin(swin); winclose(wintab); } else { printf("\n"); fflush(stdout); } } else { gp->calc_crc = calc_crc((unsigned char *) buf, *bytes, gp->calc_crc); gp->dwn_cnt -= *bytes; if (gp->dwn_cnt == 0) { if (mode != RAWMODE) { delwin(swin); winclose(wintab); } else { printf("\n"); fflush(stdout); } strcpy(s, "AutoBin download finished "); if (gp->new_header) if (gp->calc_crc == gp->file_crc) strcat(s, "CRC check ok"); else { strcat(s, "CRC check failed!"); } else { sprintf(s + strlen(s), "CRC=%u", gp->calc_crc); } statline(mode, s); close(gp->dwn_file); gp->dwn_file = -1; utime(gp->file_name, &gp->ut); gp->file_name[0] = '\0'; if (extrach != 0) { memmove(buf, buf + *bytes, extrach); *bytes = extrach; } else *bytes = 0; } else { sprintf(s, "%u", (unsigned int) gp->dwn_cnt); dupdstatw(swin, s, FALSE); *bytes = 0; } } return 0; } static int start_screen(char *call[]) { int cnt; char idString[12]; char *p; struct winsize winsz = {0}; if ((ioctl(0, TIOCGWINSZ, &winsz) >= 0) && winsz.ws_row && winsz.ws_col) resizeterm(winsz.ws_row, winsz.ws_col); sprintf(idString, " %9.9s ", call[0]); for (p = idString; *p; p++) if (islower(*p)) *p = toupper(*p); win = initscr(); if (win == NULL) return -1; attron(A_REVERSE); move(0, 0); addstr(idString); addch(ACS_VLINE); addstr("--------"); addch(ACS_VLINE); move(0, STATW_STAT); for (cnt = STATW_STAT; cnt < COLS; cnt++) addch(' '); attroff(A_REVERSE); noecho(); raw(); nodelay(win, TRUE); keypad(win, TRUE); curson = 1; refresh(); return 0; } static int start_slave_mode(wint * wintab, t_win * win_in, t_win * win_out) { win_in->max_y = LINES - 2; win_in->max_x = COLS; win_in->ptr = winopen(wintab, win_in->max_y + 1, win_in->max_x, 1, 0, FALSE); win_out->ptr = win_in->ptr; scrollok(win_in->ptr, TRUE); wclear(win_out->ptr); wrefresh(win_out->ptr); win_out->bytes = 0; win_out->curs_pos = 0; win_in->bytes = 0; win_in->curs_pos = 0; redrawscreen(win_in, SLAVEMODE, win_out); wrefresh(win_in->ptr); return 0; } static int start_talk_mode(wint * wintab, t_win * win_in, t_win * win_out) { int cnt; WINDOW *win; win_out->max_y = 4; /* TXLINES */ win_out->max_x = COLS; win_in->max_y = (LINES - 4) - win_out->max_y; win_in->max_x = COLS; win_out->ptr = winopen(wintab, win_out->max_y + 1, win_out->max_x, (win_in->max_y + 3), 0, FALSE); win_in->ptr = winopen(wintab, win_in->max_y + 1, win_in->max_x, 1, 0, FALSE); win = winopen(wintab, 1, win_out->max_x, win_in->max_y + 2, 0, FALSE); for (cnt = 0; cnt < COLS; cnt++) waddch(win, '-'); wrefresh(win); scrollok(win_in->ptr, TRUE); scrollok(win_out->ptr, TRUE); wclear(win_out->ptr); wclear(win_in->ptr); win_out->bytes = 0; win_out->curs_pos = 0; win_in->bytes = 0; win_out->curs_pos = 0; redrawscreen(win_in, TALKMODE, win_out); restorecursor(TALKMODE, win_out); wrefresh(win_in->ptr); wrefresh(win_out->ptr); return 0; } static int change_mode(int oldmode, int newmode, wint * wintab, t_win * win_in, t_win * win_out, char *call[]) { switch (oldmode) { case RAWMODE: if (newmode == TALKMODE) { start_screen(call); start_talk_mode(wintab, win_in, win_out); } if (newmode == SLAVEMODE) { start_screen(call); start_slave_mode(wintab, win_in, win_out); } break; case TALKMODE: if (newmode == RAWMODE) { wclear(win_out->ptr); wrefresh(win_out->ptr); wclear(win_in->ptr); wrefresh(win_in->ptr); wintab->next = NULL; endwin(); } if (newmode == SLAVEMODE) { delwin(win_out->ptr); delwin(win_in->ptr); wintab->next = NULL; start_slave_mode(wintab, win_in, win_out); } break; case SLAVEMODE: if (newmode == RAWMODE) { wclear(win_out->ptr); wrefresh(win_out->ptr); wintab->next = NULL; endwin(); } if (newmode == TALKMODE) { delwin(win_out->ptr); wintab->next = NULL; start_talk_mode(wintab, win_in, win_out); } break; } scrolledup = 0; return newmode; } static void reinit_mode(int mode, wint * wintab, t_win * win_in, t_win * win_out, char *call[]) { switch (mode) { case RAWMODE: break; case TALKMODE: /* Clear the screen and re-init. Which looks awful. */ wclear(win_out->ptr); wrefresh(win_out->ptr); wclear(win_in->ptr); /* wrefresh(win_in->ptr); */ wintab->next = NULL; endwin(); start_screen(call); start_talk_mode(wintab, win_in, win_out); restorecursor(mode, win_out); break; case SLAVEMODE: /* Also fix me. */ wclear(win_out->ptr); /* wrefresh(win_out->ptr); */ wintab->next = NULL; endwin(); start_screen(call); start_slave_mode(wintab, win_in, win_out); restorecursor(mode, win_out); break; } } static void waddnstrcrcheck(t_win *win_in, char *buf, int bytes, int draw, int mode, t_win *win_out) { int n; for (n=0;n incharbuflen) continue; if (eatchar && (incharbuf[0] == '\n')) { eatchar = 0; incharbuflen = 0; continue; } width = widthchar(incharbuf, incharbuflen, inbufwid); eatchar = 0; if (draw) { if (win_in) waddnstr(win_in->ptr, incharbuf, incharbuflen); else write(STDOUT_FILENO, incharbuf, incharbuflen); } if (incharbuf[0] == '\n') incharbuflen = 0; else if (width + inbufwid <= COLS) { if (inbuflen + incharbuflen <= MAX_BUFLEN) { memcpy(&inbuf[inbuflen], incharbuf, incharbuflen); inbuflen += incharbuflen; } incharbuflen = 0; inbufwid += width; if (inbufwid >= COLS) eatchar = 1; continue; /* Skip to next line when width goes over. */ } addscrollline(inbuf, inbuflen); if (incharbuflen) { memcpy(&inbuf[0], incharbuf, incharbuflen); inbuflen = incharbuflen; incharbuflen = 0; inbufwid = width; } else { inbuflen = 0; inbufwid = 0; } if (scrolledup && win_in && win_out) { /* * scrolledup is relative to bottom line */ scrolledup++; scrolltext(win_in, 0, mode, win_out); } } if (draw && win_in) wrefresh(win_in->ptr); } static void writeincom(int mode, int encoding, t_win * win_in, unsigned char buf[], int bytes, t_win *win_out) { if (mode & RAWMODE) { while (write(STDOUT_FILENO, buf, bytes) == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) { usleep(100000); continue; } exit(1); } return; } else if (encoding == UTF8ENCODING) waddnstrcrcheck(win_in, (char *)buf, bytes,scrolledup == 0, mode, win_out); else { char *inbuf = (char *) buf, out[MAX_BUFLEN], *outbuf=out; size_t insize = bytes, outsize = MAX_BUFLEN; iconv(ibm850toutf8, &inbuf, &insize, &outbuf, &outsize); waddnstrcrcheck(win_in, out, MAX_BUFLEN-outsize,scrolledup == 0, mode, win_out); } return; } static void writeincomstr(int mode, int encoding, t_win * win_in, char buf[], t_win *win_out) { int len; len = strlen(buf); writeincom(mode, encoding, win_in, (unsigned char *)buf, len, win_out); } static int outstring(char *buf, wchar_t *string, int bytes, int encoding) { char *inbuf = (char *) string, *outbuf = buf; size_t insize = bytes * sizeof(wchar_t), outsize = MAX_BUFLEN-1; if (encoding == UTF8ENCODING) { iconv(wcharttoutf8, &inbuf, &insize, &outbuf, &outsize); } else { iconv(wcharttoibm850, &inbuf, &insize, &outbuf, &outsize); } buf[(MAX_BUFLEN-1)-outsize] = '\0'; return (MAX_BUFLEN-1)-outsize; } static int getstring(wint * wintab, char text[], char buf[]) { wchar_t c; int ypos = 0, xpos = 0; int bytes = 0; wchar_t wbuf[MAX_BUFLEN]; WINDOW *win = winopen(wintab, 3, COLS, 10, 0, TRUE); int done = 0; wmove(win, 1, 2); waddstr(win, text); wrefresh(win); do { int r; wint_t ci; r = get_wch(&ci); if (r != ERR) { c = (wchar_t) ci; if (((r == KEY_CODE_YES) && (c == KEY_BACKSPACE))|| ((r == OK) && ((c==127)|| (c==8)))) { getyx(win, ypos, xpos); if (bytes > 0) { int width, j; width = wcwidthcontrol(wbuf[bytes-1]); for (j=0;jptr); return 2; } if (((r == KEY_CODE_YES) && (c == KEY_BACKSPACE))|| ((r == OK) && ((c==127)|| (c==8)))) { if ((mode == SLAVEMODE) && scrolledup) return 0; while(win_out->curs_pos > 0) { int width; int j; getyx(win_out->ptr, ypos, xpos); width = wcwidthcontrol(win_out->string[win_out->curs_pos-1]); for (j=0;jptr, ypos, xpos-width); xpos -= width; wmove(win_out->ptr, ypos, xpos); if (win_out->curs_pos < win_out->bytes) { memmove(&win_out-> string[win_out->curs_pos - 1], &win_out->string[win_out-> curs_pos], (win_out->bytes - win_out->curs_pos) * sizeof(wchar_t)); } win_out->bytes--; win_out->curs_pos--; if (width) break; } } else if (( (r==KEY_CODE_YES) && (c == KEY_ENTER))|| ( (r == OK) && ((c=='\n') || (c=='\r')))) { if ((mode == SLAVEMODE) && scrolledup) return 0; while (win_out->curs_pos < win_out->bytes) { /* Move to end of the line */ int width; width = wcwidthcontrol(win_out->string[win_out->curs_pos]); win_out->curs_pos++; getyx(win_out->ptr, ypos, xpos); wmove(win_out->ptr, ypos, xpos + width); } waddch(win_out->ptr, '\n'); win_out->string[win_out->bytes++] = (wchar_t) '\n'; wrefresh(win_out->ptr); out_cnt = outstring(buf, win_out->string, win_out->bytes, encoding); if (mode == SLAVEMODE) { char obuf[MAX_BUFLEN]; char *inbuf = (char *)win_out->string, *outbuf = obuf; size_t insize = win_out->bytes * sizeof(wchar_t), outsize = MAX_BUFLEN; iconv(wcharttoutf8, &inbuf, &insize, &outbuf, &outsize); waddnstrcrcheck(win_in, obuf, MAX_BUFLEN-outsize, 0, mode, win_out); } win_out->bytes = 0; win_out->curs_pos = 0; return out_cnt; } else if (r == KEY_CODE_YES) { switch(c) { case KEY_LEFT: /* Character of 0 width */ while (win_out->curs_pos > 0) { int width; win_out->curs_pos--; width = wcwidthcontrol(win_out->string[win_out->curs_pos]); getyx(win_out->ptr, ypos, xpos); wmove(win_out->ptr, ypos, xpos - width); if (width) break; /* Skip to non-width */ } break; case KEY_RIGHT: { /* Skip over 0 length characters */ int skipped = 0; while (win_out->curs_pos < win_out->bytes) { int width; width = wcwidthcontrol(win_out->string[win_out->curs_pos]); if (width) { if (skipped) break; skipped = 1; } win_out->curs_pos++; getyx(win_out->ptr, ypos, xpos); wmove(win_out->ptr, ypos, xpos + width); } break; } case KEY_UP: scrolltext(win_in, 1, mode, win_out); break; case KEY_DOWN: scrolltext(win_in, -1, mode, win_out); break; case KEY_NPAGE: scrolltext(win_in, -win_in->max_y, mode, win_out); break; case KEY_PPAGE: scrolltext(win_in, win_in->max_y, mode, win_out); break; case KEY_HOME: while (win_out->curs_pos > 0) { int width; win_out->curs_pos--; width = wcwidthcontrol(win_out->string[win_out->curs_pos]); getyx(win_out->ptr, ypos, xpos); wmove(win_out->ptr, ypos, xpos - width); } break; case KEY_END: while (win_out->curs_pos < win_out->bytes) { /* * Move to end of the line */ int width; width = wcwidthcontrol(win_out->string[win_out->curs_pos]); win_out->curs_pos++; getyx(win_out->ptr, ypos, xpos); wmove(win_out->ptr, ypos, xpos + width); } break; case KEY_DC:{ int skipped = 0; if ((mode == SLAVEMODE) && scrolledup) return 0; while (win_out->curs_pos < win_out->bytes) { int width; int j; getyx(win_out->ptr, ypos, xpos); width = wcwidthcontrol(win_out->string[win_out->curs_pos]); if (width) { if (skipped) break; skipped = 1; } for (j=0;jptr, ypos, xpos); if (win_out->curs_pos + 1 < win_out->bytes) { memmove(&win_out-> string[win_out->curs_pos], &win_out->string[win_out-> curs_pos+1], (win_out->bytes - (win_out->curs_pos+1)) * sizeof(wchar_t)); } win_out->bytes--; } break; } case KEY_RESIZE: break; case KEY_BACKSPACE: break; case KEY_ENTER: break; default: break; } } else switch (c) { case 8: case 127: case '\r': case '\n': break; default: /* * Don't try to edit while scrolled up in SLAVEmode. */ if ((mode == SLAVEMODE) && scrolledup) return 0; /* * It's just not possible because the cursor is off screen */ if (win_out->bytes < MAX_BUFLEN ) { if (win_out->curs_pos < win_out->bytes) { memmove(&win_out-> string[win_out->curs_pos + 1], &win_out->string[win_out-> curs_pos], (win_out->bytes - win_out->curs_pos) * sizeof(wchar_t)); } win_out->string[win_out->curs_pos] = c; win_out->bytes++; win_out->curs_pos++; drawinbuf(win_out->ptr, win_out->string, win_out->bytes, win_out->curs_pos); break; } } wrefresh(win_out->ptr); return 0; } #if 0 static void remotecommand(char buf[], int bytes) { int firstchar; if (bytes == 0) return; switch (buf[0]) { case 'e': case 'E': { for (firstchar = 0; buf[firstchar] != ' '; firstchar++); firstchar++; buf[bytes] = '\n'; convert_lf_cr(buf + firstchar, bytes - firstchar + 1); write(fd, buf + firstchar, bytes - firstchar + 1); } break; default: write(fd, "Unknown command\r", 16); } } #endif static int compstr(const char st1[], char st2[], int maxbytes) { int cnt; for (cnt = 0; st1[cnt] == st2[cnt] && cnt + 1 < maxbytes && st1[cnt + 1] != 0; cnt++); if (st1[cnt] != st2[cnt]) return -1; if (st1[cnt + 1] == 0) return 0; if (cnt == maxbytes - 1) return -2; return -1; } static int eol(char c) { if (c == '\r' || c == '\n' || c == 0x1A) return TRUE; else return FALSE; } static int searche_key_words(char buf[], int *bytes, char *parms, int *parmsbytes, char restbuf[], int *restbytes) { static char cmpstr[MAX_CMPSTRLEN]; static int cmpstrbyte = 0; static int command = -1; const char **pkey_words = remote_commands_enabled ? key_words : rkey_words; int cmdstpos = 0; int cnt = 0; int t = 0; if (cmpstrbyte != 0) { memmove(buf + cmpstrbyte, buf, *bytes); *bytes += cmpstrbyte; strncpy(buf, cmpstr, cmpstrbyte); cmpstrbyte = 0; for (cnt = 0; !eol(buf[cnt]) && cnt < *bytes - 1; cnt++); if (cnt == *bytes - 1 && !eol(buf[cnt])) { printf("Problem!!!\n"); fflush(stdout); command = -1; *restbytes = 0; *parmsbytes = 0; return -1; } } if (command == -1) { cnt = 0; do { command = 0; cmdstpos = cnt; t = -1; while (*pkey_words[command] != '\0') { t = compstr(pkey_words[command], &buf[cnt], *bytes - cnt); if (t != -1) break; command++; } for (; !eol(buf[cnt]) && cnt < *bytes - 1; cnt++); if (cnt < *bytes - 1) cnt++; else break; } while (t == -1); if (t < 0) command = -1; } if (t == -2 || (command != -1 && cnt == *bytes - 1 && !eol(buf[*bytes - 1]))) { cmpstrbyte = *bytes - cmdstpos; strncpy(cmpstr, &buf[cmdstpos], cmpstrbyte); cmpstr[cmpstrbyte] = 0; *bytes -= cmpstrbyte; *restbytes = 0; return -1; } if (t == -1) { command = -1; cmpstrbyte = 0; *restbytes = 0; return -1; } t = cmdstpos + strlen(pkey_words[command]); *restbytes = *bytes - cnt; strncpy(parms, &buf[t], cnt - t); *parmsbytes = cnt - t; strncpy(restbuf, buf + cnt, *restbytes); *bytes = cmdstpos; t = command; command = -1; return t; } static int sevenplname(int mode, WINDOW ** swin, wint * wintab, int *f, int *logfile, char parms[], int parmsbytes) { int cnt; int part; int nrparts; int lines; char orgn[13]; char prtn[13+3]; char strn[PATH_MAX]; char v[20]; char s[80]; if (parmsbytes >= 40) if (strcmp(" of ", &parms[3]) == 0 || parmsbytes < 41 || parms[10] != ' ' || parms[23] != ' ' || parms[31] != ' ' || parms[36] != ' ' || parms[40] != ' ') { return -1; } part = atof(parms); lines = (int) strtol(parms + 37, NULL, 16); nrparts = (int) strtol(parms + 7, NULL, 10); strncpy(orgn, &parms[11], 12); convert_upper_lower(orgn, 12); for (cnt = 11; orgn[cnt] == ' '; cnt--) { if (cnt == 0) break; } orgn[cnt + 1] = 0; if (orgn[cnt - 3] == '.') { strncpy(prtn, orgn, cnt - 2); if (nrparts == 1) sprintf(prtn + cnt - 2, "7pl"); else sprintf(prtn + cnt - 2, "p%02x", part); } else { strcpy(prtn, orgn); if (nrparts == 1) sprintf(prtn + cnt, ".7pl"); else sprintf(prtn + cnt, ".p%02x", part); } strcpy(strn, STD_DWN_DIR); strcat(strn, prtn); for (cnt = 0; cnt + 41 < parmsbytes && parms[cnt + 41] != ')'; cnt++) ; if (parms[cnt + 41] != ')') { return -1; } if (cnt > sizeof(v)-2) cnt = sizeof(v)-2; strncpy(v, &parms[41], cnt + 1); v[cnt + 1] = 0; *swin = opnstatw(mode, wintab, "Remote starts 7+ Download", 11, 55); sprintf(s, "7plus version : %-.55s", v); wrdstatw(*swin, s); sprintf(s, "Name of decoded file : %-.55s", orgn); wrdstatw(*swin, s); sprintf(s, "Storagename : %-.55s", strn); wrdstatw(*swin, s); sprintf(s, "Parts : %i", nrparts); wrdstatw(*swin, s); sprintf(s, "Number of this Part : %i", part); wrdstatw(*swin, s); sprintf(s, "Lines : %i", lines); wrdstatw(*swin, s); dupdstatw(*swin, "Outstanding lines : ", TRUE); if (*f != -1) { close(*f); *f = -1; } if ((*f = open(strn, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { sprintf(s, "Unable to open %s", strn); statline(mode, s); return -1; } else if (*logfile != -1) { sprintf(s, "*** 7plus download into file: %s ***\n", strn); write(*logfile, s, strlen(s)); } write(*f, key_words[2], strlen(key_words[2])); convert_cr_lf(parms, parmsbytes); write(*f, parms, parmsbytes); return lines; } static void statbits(int mode, char stat, int m) { if (mode == RAWMODE) return; move(0, STATW_BITS + m); attron(A_REVERSE); addch(stat); attroff(A_REVERSE); refresh(); } static int cmd_call(char *call[], int mode, int encoding) { menuitem con[] = { {"~Reconnect", 'R', M_ITEM, (void *) 0x01}, {"~Exit", 'E', M_ITEM, (void *) 0x02}, {"\0", 0, M_END, NULL} }; menuitem fil[] = { {"~Open Logfile", 'O', M_ITEM, NULL}, {"~Close Logfile", 'C', M_ITEM, NULL}, {"Send ~Textfile", 'T', M_ITEM, NULL}, {"Send ~Binary", 'B', M_ITEM, NULL}, {"Send ~AutoBin", 'A', M_ITEM, NULL}, {"\0", 0, M_END, NULL} }; menuitem mod[] = { {"~Slavemode", 'S', M_ITEM, NULL}, {"~Talkmode", 'T', M_ITEM, NULL}, {"~Rawmode", 'R', M_ITEM, NULL}, {"\0", 0, M_END, NULL} }; menuitem win[] = { {"~Clear", 'C', M_ITEM, NULL}, {"~Resize", 'R', M_ITEM, NULL}, {"\0", 0, M_END, NULL} }; menuitem top[] = { {"~Connect", 'C', M_P_DWN, con}, {"~File", 'F', M_P_DWN, fil}, {"~Mode", 'M', M_P_DWN, mod}, {"~Window", 'W', M_P_DWN, win}, {"\0", 0, M_END, NULL} }; wint wintab; fd_set sock_read; fd_set sock_write; char buf[MAX_BUFLEN]; /* char restbuf[MAX_PACKETLEN]; */ char restbuf[MAX_BUFLEN]; /* char parms[256]; */ char parms[MAX_BUFLEN]; int sevenplus = FALSE; int sevenplcnt = 0; int bytes; int restbytes = 0; int parmsbytes; int com_num; int logfile = -1; int uploadfile = -1; int downloadfile = -1; int binup = FALSE; unsigned long uplsize = 0; unsigned long uplpos = 0; char uplbuf[128]; /* Upload buffer */ int upldp = 0; int upllen = 0; char *c, *t; t_gp gp; t_win win_in = { .ptr = NULL, }; t_win win_out = { .ptr = NULL, }; WINDOW *swin = NULL; int cnt; int crc = 0; char s[80]; int flags = 0; int EOF_on_STDIN = FALSE; sigset_t oursigs, oldsigs; init_crc(); if (paclen > sizeof(buf)) paclen = sizeof(buf); gp.dwn_cnt = 0; wintab.next = NULL; fd = connect_to(call); if (fd == -1) return FALSE; interrupted = FALSE; sigwinchsignal = FALSE; signal(SIGQUIT, cmd_intr); signal(SIGINT, SIG_IGN); signal(SIGTSTP, SIG_IGN); signal(SIGWINCH, cmd_sigwinch); sigemptyset(&oursigs); sigaddset(&oursigs, SIGQUIT); fcntl(fd, F_SETFL, O_NONBLOCK); fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); if (mode != RAWMODE) start_screen(call); switch (mode) { case TALKMODE: start_talk_mode(&wintab, &win_in, &win_out); break; case SLAVEMODE: start_slave_mode(&wintab, &win_in, &win_out); break; case RAWMODE: if (!be_silent) { printf("Rawmode\n"); fflush(stdout); } } sigprocmask(SIG_BLOCK, &oursigs, &oldsigs); while (TRUE) { struct timespec tv, *timeout; int nfds; if (inactivity_timeout_is_set == TRUE && uploadfile == -1 && downloadfile == -1) { tv.tv_sec = inactivity_timeout.tv_sec; tv.tv_nsec = inactivity_timeout.tv_nsec; } else { tv.tv_sec = 0; tv.tv_nsec = 10000; /* 10ms */ } FD_ZERO(&sock_read); if (EOF_on_STDIN == FALSE) FD_SET(STDIN_FILENO, &sock_read); FD_SET(fd, &sock_read); FD_ZERO(&sock_write); if (uploadfile != -1) FD_SET(fd, &sock_write); if (uploadfile == -1 && downloadfile == -1 && inactivity_timeout_is_set == FALSE) timeout = NULL; else timeout = &tv; nfds = pselect(fd + 1, &sock_read, &sock_write, NULL, timeout, &oldsigs); if (nfds == -1) { if (!interrupted) { if (errno == EINTR) continue; if (errno == EAGAIN) { usleep(100000); continue; } if ((errno == EINTR) && sigwinchsignal) { /* * Just process screen resize here. */ reinit_mode(mode, &wintab, &win_in, &win_out, call); sigwinchsignal = 0; continue; } perror("select"); } break; } if (inactivity_timeout_is_set == TRUE && !FD_ISSET(fd, &sock_read) && !FD_ISSET(STDIN_FILENO, &sock_read)) { if (!be_silent) { printf("*** Inactivity timeout reached. Terminating..\n"); fflush(stdout); } break; } if (FD_ISSET(fd, &sock_read)) { /* bytes = read(fd, buf, 511); */ bytes = read(fd, buf, sizeof(buf)); if (bytes == 0) { /* read EOF on connection */ /* cause program to terminate */ flags &= ~FLAG_RECONNECT; break; } if (bytes == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) { usleep(100000); continue; } if (errno != ENOTCONN) perror("read"); break; } if (gp.dwn_cnt != 0) { ab_down(mode, swin, &wintab, buf, &bytes, &gp); if (bytes == 0) { continue; } } do { /* imagine one line ("bar go_7+") was split into * two packets: 1. "...foo\nbar" 2. " go_7+. ..." * then searche_key_words misinterprets " go_7+. " * as start of a line. */ com_num = searche_key_words(buf, &bytes, parms, &parmsbytes, restbuf, &restbytes); if (bytes != 0) { convert_cr_lf(buf, bytes); if (!sevenplus) { writeincom(mode, encoding, &win_in, (unsigned char * ) buf, bytes, &win_out); restorecursor(mode, &win_out); } else { for (cnt = 0; cnt < bytes; cnt++) if (eol(buf[cnt])) sevenplcnt--; dupdstatw(swin, s, FALSE); } if (downloadfile != -1) { if (write (downloadfile, buf, bytes) != bytes) { close (downloadfile); downloadfile = -1; statline(mode, "Error while writing file. Downloadfile closed."); } } else if (logfile != -1) { if (write (logfile, buf, bytes) != bytes) { close(logfile); logfile = -1; statline(mode, "Error while writing log. Log closed."); } } } switch (com_num) { case 0: #if 0 /* * FIXME! We should, no: WE MUST be * able to turn off all remote commands * to avoid mail bombs generating * offensive mails with //e while *sucking the BBS */ { remotecommand(parms, parmsbytes); } #endif break; case 1: { start_ab_download(mode, &swin, &wintab, parms, parmsbytes, restbuf, restbytes, &gp, call); restbytes = 0; } break; case 2: { sevenplcnt = sevenplname(mode, &swin, &wintab, &downloadfile, &logfile, parms, parmsbytes); if (sevenplcnt != -1) sevenplus = TRUE; } break; case 3: { if (!sevenplus) break; write(downloadfile, key_words[3], strlen(key_words [3])); convert_cr_lf(parms, parmsbytes); write(downloadfile, parms, parmsbytes); if (mode != RAWMODE) { delwin(swin); winclose(&wintab); } else { printf("\n"); fflush(stdout); } statline(mode, "7+ Download finished."); sevenplus = FALSE; close(downloadfile); downloadfile = -1; } break; } memcpy(buf, restbuf, restbytes); bytes = restbytes; } while (restbytes != 0); } if (FD_ISSET(STDIN_FILENO, &sock_read)) { if ((mode & RAWMODE) == RAWMODE) { /* bytes = read(STDIN_FILENO, buf, 511); */ bytes = read(STDIN_FILENO, buf, paclen); /* bytes == 0? select() indicated that there * is data to read, but read() returned 0 * bytes. -> terminate normaly */ if (bytes == 0) EOF_on_STDIN = TRUE; else if (bytes == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) { usleep(100); } else { perror("input"); EOF_on_STDIN = TRUE; } } } else { bytes = readoutg(&win_out, &wintab, top, buf, 0x1d, mode, encoding, &win_in); if (bytes == -1) { wclear(win_in.ptr); wrefresh(win_in.ptr); wclear(win_out.ptr); wrefresh(win_out.ptr); bytes = 0; } } if (bytes > 0) statline(mode, ""); if (bytes > 1 && *buf == '~' && stdin_is_tty) { buf[bytes] = 0; switch (buf[1]) { case '.': { bytes = 0; interrupted = TRUE; } break; case '!': change_mode(mode, RAWMODE, &wintab, &win_in, &win_out, call); if (buf[2] != '\0' && buf[2] != '\n') { c = buf + 2; t = strchr(c, '\n'); if (t != NULL) *t = '\0'; } else { c = getenv("SHELL"); if (c == NULL) c = "/bin/sh"; } fcntl(STDIN_FILENO, F_SETFL, 0); printf("\n[Spawning subshell]\n"); fflush(stdout); system(c); printf ("\n[Returned to connect]\n"); fflush(stdout); fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); change_mode(RAWMODE, mode, &wintab, &win_in, &win_out, call); continue; case 'z': case 'Z': case 'Z' - 64: fcntl(STDIN_FILENO, F_SETFL, 0); kill(getpid(), SIGSTOP); statline(mode, "Resumed"); fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); continue; case '?': case 'h': case 'H': writeincomstr(mode, encoding, &win_in,"\nTilde escapes:\n", &win_out); writeincomstr(mode, encoding, &win_in,". close\n", &win_out); writeincomstr(mode, encoding, &win_in,"~ send ~\n", &win_out); writeincomstr(mode, encoding, &win_in,"r reconnect\n", &win_out); writeincomstr(mode, encoding, &win_in,"! shell\n", &win_out); writeincomstr(mode, encoding, &win_in,"Z suspend program. Resume with \"fg\"\n", &win_out); writeincomstr(mode, encoding, &win_in,"s Stop upload\n", &win_out); writeincomstr(mode, encoding, &win_in,"o Open log\n", &win_out); writeincomstr(mode, encoding, &win_in,"c Close log\n", &win_out); writeincomstr(mode, encoding, &win_in,"0 Switch GUI to \"RAW\" (line) mode - already here ;)\n", &win_out); writeincomstr(mode, encoding, &win_in,"1 Switch GUI to \"Slave\" mode\n", &win_out); writeincomstr(mode, encoding, &win_in,"2 Switch GUI to \"Talk\" (split) mode\n", &win_out); writeincomstr(mode, encoding, &win_in,"u Upload\n", &win_out); writeincomstr(mode, encoding, &win_in,"a Upload (autobin protocol)\n", &win_out); writeincomstr(mode, encoding, &win_in,"b Upload binary data (crlf conversion)\n", &win_out); writeincomstr(mode, encoding, &win_in,"yd YAPP Download\n", &win_out); writeincomstr(mode, encoding, &win_in,"yu YAPP Upload\n", &win_out); writeincomstr(mode, encoding, &win_in,"i IBM850 encoding\n", &win_out); writeincomstr(mode, encoding, &win_in,"8 UTF-8 encoding\n", &win_out); restorecursor(mode, &win_out); continue; case 'S': case 's': if (uploadfile != -1) { statline(mode, "Upload file closed"); close(uploadfile); uploadfile = -1; } else { statline(mode, "No upload in progress"); } restorecursor(mode, &win_out); continue; case 'A': case 'a': case 'b': case 'B': case 'u': case 'U': if (uploadfile != -1) { statline(mode, "Already uploading"); restorecursor(mode, &win_out); continue; } t = strchr(buf, '\n'); if (t != NULL) *t = '\0'; t = buf + 2; while (*t != '\0' && isspace(*t)) t++; if (*t == '\0') { statline(mode, "Upload requires a filename"); restorecursor(mode, &win_out); continue; } uploadfile = open(t, O_RDONLY); if (uploadfile == -1) { statline(mode, "Unable to open upload file"); restorecursor(mode, &win_out); continue; } if (lseek(uploadfile, 0L, SEEK_END) != -1) uplsize = lseek(uploadfile, 0L, SEEK_CUR); else uplsize = 0; lseek(uploadfile, 0L, SEEK_SET); uplpos = 0; upldp = -1; upllen = 0; if (uplsize != -1) { sprintf(s, "Uploading %ld bytes from %s", uplsize, t); swin = opnstatw(mode, &wintab, s, 6, 50); dupdstatw(swin, "bytes sent : ", TRUE); } else { sprintf(s, "Uploading from %s", t); swin = opnstatw(mode, &wintab, s, 6, 50); dupdstatw(swin, "bytes sent : ", TRUE); } switch (buf[1]) { case 'a': case 'A': { struct stat statbuf; time_t file_time = 0L; binup = TRUE; crc = 0; if (!fstat(uploadfile, &statbuf)) file_time = statbuf.st_mtime; else file_time = time(NULL); do { upllen = read (uploadfile, uplbuf, 128); if (upllen == -1) { close (uploadfile); uploadfile = -1; delwin (swin); winclose (&wintab); sprintf (s, "Error reading upload file: upload aborted"); statline (mode, s); break; } crc = calc_crc ((unsigned char *) uplbuf, upllen, crc); } while (upllen > 0); lseek(uploadfile, 0L, SEEK_SET); sprintf(s, "#BIN#%ld#|%u#$%s#%s\r", uplsize, crc, unix_to_sfbin_date_string(file_time), t); if ( write(fd, s, strlen(s)) != strlen(s)) { perror("write"); exit(1); } uplpos = 0; upldp = -1; upllen = 0; } restorecursor(mode, &win_out); break; case 'b': case 'B': binup = TRUE; break; case 'u': case 'U': binup = FALSE; } continue; case 'O': case 'o': t = strchr(buf, '\n'); if (t != NULL) *t = '\0'; if (logfile != -1) { close(logfile); logfile = -1; } if (downloadfile != -1) { close(downloadfile); downloadfile = -1; } t = buf + 2; while (*t != '\0' && isspace(*t)) t++; if (*t == '\0') t = "logfile.txt"; if ((logfile = open(t, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { sprintf(s, "Unable to open %s", buf + 2); statline(mode, s); } else statbits(mode, 'L', 1); restorecursor(mode, &win_out); continue; case 'C': case 'c': if (logfile != -1) { close(logfile); logfile = -1; statbits(mode, '-', 1); } else { statline(mode, "Log file not open"); } restorecursor(mode, &win_out); continue; case 'Y': case 'y': cmd_yapp(buf + 2, bytes - 2); restorecursor(mode, &win_out); continue; case '~': bytes--; memmove(buf, buf + 1, strlen(buf)); break; case 'R': case 'r': flags |= FLAG_RECONNECT; bytes = 0; interrupted = TRUE; continue; case '0': mode = change_mode(mode, RAWMODE, &wintab, &win_in, &win_out, call); continue; case '1': mode = change_mode(mode, SLAVEMODE, &wintab, &win_in, &win_out, call); continue; case '2': mode = change_mode(mode, TALKMODE, &wintab, &win_in, &win_out, call); continue; case '8': encoding = UTF8ENCODING; statline(mode, "UTF-8 encoding"); restorecursor(mode, &win_out); continue; case 'i': case 'I': encoding = ORIGINALENCODING; statline(mode, "IBM850 encoding"); restorecursor(mode, &win_out); continue; default: statline(mode, "Unknown '~' escape. Type ~h for a list"); restorecursor(mode, &win_out); continue; } } if (interrupted) break; if (bytes > 0) { unsigned long offset = 0L; int err = 0; sevenplus = FALSE; if (uploadfile != -1) { statline(mode, "Ignored. Type ~s to stop upload"); restorecursor(mode, &win_out); continue; } convert_lf_cr(buf, bytes); while (offset != bytes) { int len = (bytes-offset > paclen) ? paclen : bytes-offset; int ret = write(fd, buf+offset, len); if (ret == 0) break; if (ret == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) { usleep(100000); continue; } perror("write"); err = 1; break; } offset += ret; } if (err) break; } if (EOF_on_STDIN == TRUE && wait_for_remote_disconnect == FALSE) break; } if (uploadfile != -1) { if (uplsize == 0) { close(uploadfile); uploadfile = -1; delwin(swin); winclose(&wintab); statline(mode, "Upload complete: 0 bytes"); restorecursor(mode, &win_out); continue; } if (upldp == -1) { upllen = read(uploadfile, uplbuf, 128); if (upllen == 0) { close(uploadfile); uploadfile = -1; delwin(swin); winclose(&wintab); sprintf(s, "Upload complete: %ld bytes", uplpos); statline(mode, s); restorecursor(mode, &win_out); continue; } if (upllen == -1) { close(uploadfile); uploadfile = -1; delwin(swin); winclose(&wintab); sprintf(s, "Error reading upload file: upload aborted at %ld bytes", uplpos); statline(mode, s); restorecursor(mode, &win_out); continue; } if (!binup) convert_lf_cr(uplbuf, upllen); upldp = 0; } bytes = write(fd, uplbuf + upldp, upllen - upldp); if (bytes == 0) { break; } if (bytes == -1) { if (errno != EWOULDBLOCK && errno != EAGAIN) { sprintf(s, "Write error during upload. Connection lost"); statline(mode, s); restorecursor(mode, &win_out); perror("write"); break; } usleep(100000); continue; } /* if (uplpos / 1024 != (uplpos + bytes) / 1024) { */ /* printf("\r%ld bytes sent ", uplpos + bytes); */ sprintf(s, "%ld", uplpos + bytes); dupdstatw(swin, s, FALSE); /* } */ uplpos += bytes; upldp += bytes; if (upldp >= upllen) upldp = -1; } } close(fd); fd = -1; if (logfile != -1) { close(logfile); logfile = -1; } if (downloadfile != -1) { close(downloadfile); downloadfile = -1; } if (gp.dwn_cnt != 0) { close(gp.dwn_file); gp.dwn_file = -1; gp.dwn_cnt = 0; gp.file_name[0] = '\0'; } fcntl(STDIN_FILENO, F_SETFL, 0); sigprocmask(SIG_SETMASK, &oldsigs, NULL); signal(SIGQUIT, SIG_IGN); signal(SIGINT, SIG_DFL); if (mode != RAWMODE) endwin(); if (!be_silent) { printf("*** Cleared\n"); fflush(stdout); } if (flags & FLAG_RECONNECT) { return TRUE; } else { return FALSE; } } static void iconvclose(void) { iconv_close(ibm850toutf8); iconv_close(wcharttoibm850); iconv_close(wcharttoutf8); iconv_close(utf8towchart); } int main(int argc, char **argv) { int p; int mode = TALKMODE; int encoding = UTF8ENCODING; /* Maybe controversial? */ setlocale(LC_ALL, ""); if (!isatty(STDIN_FILENO)) stdin_is_tty = 0; setlinebuf(stdin); while ((p = getopt(argc, argv, "b:dhm:p:rs:RStT:vw:Wi8")) != -1) { switch (p) { case 'b': if (*optarg != 'e' && *optarg != 'l') { fprintf(stderr, "call: invalid argument for option '-b'\n"); return 1; } backoff = *optarg == 'e'; break; case 'd': debug = TRUE; break; case 'h': mode = SLAVEMODE; break; case 'm': if (*optarg != 's' && *optarg != 'e') { fprintf(stderr, "call: invalid argument for option '-m'\n"); return 1; } ax25mode = *optarg == 'e'; break; case 'p': paclen = atoi(optarg); if (paclen == 0) { fprintf(stderr, "call: option '-p' requires a numeric argument\n"); return 1; } if (paclen < 1 || paclen > 500) { fprintf(stderr, "call: paclen must be between 1 and 500\n"); return 1; } break; case 'r': /* * This is used to format the scrollback buffer, * which is stored in raw */ COLS = 80; mode = RAWMODE; break; case 's': mycall = strdup(optarg); break; case 'R': remote_commands_enabled = FALSE; case 'S': be_silent = 1; break; case 'T': { double f = atof(optarg); inactivity_timeout.tv_sec = f; inactivity_timeout.tv_nsec = (f - (long) f) * 1000000000; if (f < 0.001 || f > (double) LONG_MAX) { fprintf(stderr, "call: option '-T' must be > 0.001 (1ms) and < %ld seconds\n", LONG_MAX); return 1; } inactivity_timeout_is_set = TRUE; } break; case 't': mode = TALKMODE; break; case 'v': printf("call: %s\n", VERSION); return 0; case 'w': window = atoi(optarg); if (window == 0) { fprintf(stderr, "call: option '-w' requires a numeric argument\n"); return 1; } if (ax25mode) { if (window < 1 || window > 63) { fprintf(stderr, "call: window must be between 1 and 63 frames\n"); return 1; } } else { if (window < 1 || window > 7) { fprintf(stderr, "call: window must be between 1 and 7 frames\n"); return 1; } } break; case 'W': wait_for_remote_disconnect = TRUE; break; case 'i': encoding = ORIGINALENCODING; break; case '8': encoding = UTF8ENCODING; break; case '?': case ':': usage(); } } if (optind == argc || optind == argc - 1) { usage(); } ibm850toutf8 = iconv_open("UTF8", "IBM850"); wcharttoibm850 = iconv_open("IBM850", "WCHAR_T"); wcharttoutf8 = iconv_open("UTF8", "WCHAR_T"); utf8towchart = iconv_open("WCHAR_T", "UTF8"); atexit(iconvclose); port = argv[optind]; if (ax25_config_load_ports() == 0) { fprintf(stderr, "call: no AX.25 port data configured\n"); return 1; } if (ax25_config_get_addr(port) == NULL) { nr_config_load_ports(); if (nr_config_get_addr(port) == NULL) { rs_config_load_ports(); if (rs_config_get_addr(port) == NULL) { fprintf(stderr, "call: invalid port setting\n"); return 1; } else { af_mode = AF_ROSE; } } else { af_mode = AF_NETROM; } } else { af_mode = AF_AX25; } switch (af_mode) { case AF_ROSE: paclen = rs_config_get_paclen(port); break; case AF_NETROM: if (paclen == 0) paclen = nr_config_get_paclen(port); break; case AF_AX25: if (window == 0) window = ax25_config_get_window(port); if (paclen == 0) paclen = ax25_config_get_paclen(port); break; } if (!be_silent) { printf("GW4PTS AX.25 Connect v1.11\n"); fflush(stdout); } while (cmd_call(argv + optind + 1, mode, encoding)) { if (!be_silent) { printf("Wait 60 sec before reconnect\n"); fflush(stdout); } sleep(60); } return 0; } ax25-apps/call/menu.h0000644000175000017500000000077613521356721013003 0ustar irlirl#define M_ITEM 0x01 #define M_P_DWN 0x02 #define M_END 0x03 typedef struct { char* st_ptr; char key; int entr_type; void* arg; } menuitem; struct wint_s { WINDOW* ptr; int fline; int lline; struct wint_s* next; }; typedef struct wint_s wint; WINDOW* winopen(wint*, int, int, int, int, int); void winclose(wint*); void menu_write_line(WINDOW*, int, int,int, char*); int p_dwn_menu(wint*, menuitem*, int, int); void menu_write_item(WINDOW*, int,int, const char*); int top_menu(wint*, menuitem*, int); ax25-apps/ax25rtd/0000755000175000017500000000000013521356721012232 5ustar irlirlax25-apps/ax25rtd/.gitignore0000644000175000017500000000006713521356721014225 0ustar irlirlax25rtctl ax25rtctl.8 ax25rtd ax25rtd.8 ax25rtd.conf.5 ax25-apps/ax25rtd/cache_dump.c0000644000175000017500000000763513521356721014501 0ustar irlirl/* * Copyright (c) 1996 Joerg Reuter (jreuter@poboxes.com) * * 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 received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include "ax25rtd.h" void dump_ip_routes(int fd, int cmd) { ip_rt_entry *bp; config *config; char buf[256], *dev; unsigned long ip; int len; for (bp = ip_routes; bp; bp = bp->next) { ip = htonl(bp->ip); if (cmd) { len = sprintf(buf, "add ip "); dev = bp->iface; } else { len = 0; config = dev_get_config(bp->iface); if (config != NULL) dev = config->port; else dev = bp->iface; } len += sprintf(buf + len, "%d.%d.%d.%d", (int) ((ip & 0xFF000000) >> 24), (int) ((ip & 0x00FF0000) >> 16), (int) ((ip & 0x0000FF00) >> 8), (int) (ip & 0x000000FF)); len += sprintf(buf + len, " %-4s %8.8lx %-9s ", dev, bp->timestamp, ax25_ntoa(&bp->call)); if (bp->invalid) len += sprintf(buf + len, "X\n"); else len += sprintf(buf + len, "%c\n", bp->ipmode ? 'v' : 'd'); write(fd, buf, len); } if (!cmd) write(fd, ".\n", 2); } void dump_ax25_routes(int fd, int cmd) { ax25_rt_entry *bp; config *config; char buf[256], *dev; int k, len; for (bp = ax25_routes; bp; bp = bp->next) { if (cmd) { len = sprintf(buf, "add ax25 "); dev = bp->iface; } else { len = 0; config = dev_get_config(bp->iface); if (config != NULL) dev = config->port; else dev = bp->iface; } len += sprintf(buf + len, "%-9s %-4s %8.8lx", ax25_ntoa(&bp->call), dev, bp->timestamp); for (k = 0; k < bp->ndigi; k++) len += sprintf(buf + len, " %s", ax25_ntoa(&bp->digipeater[k])); len += sprintf(buf + len, "\n"); write(fd, buf, len); } if (!cmd) write(fd, ".\n", 2); } void dump_config(int fd) { config *config; int k; fprintf(stderr, "config:\n"); for (config = Config; config; config = config->next) { fprintf(stderr, "Device = %s\n", config->dev); fprintf(stderr, "Port = %s\n", config->port); fprintf(stderr, "ax25_add_route = %d\n", config->ax25_add_route); fprintf(stderr, "ax25_for_me = %d\n", config->ax25_for_me); fprintf(stderr, "ax25_add_default = %d\n", config->ax25_add_default); fprintf(stderr, "ip_add_route = %d\n", config->ip_add_route); fprintf(stderr, "ip_add_arp = %d\n", config->ip_add_arp); fprintf(stderr, "ip_adjust_mode = %d\n", config->ip_adjust_mode); fprintf(stderr, "netmask = %8.8lx\n", config->netmask); fprintf(stderr, "ip = %8.8lx\n", config->ip); fprintf(stderr, "nmycalls = %d\n", config->nmycalls); fprintf(stderr, "calls ="); for (k = 0; k < config->nmycalls; k++) fprintf(stderr, " %s", ax25_ntoa(&config->mycalls[k])); fprintf(stderr, "\n"); fprintf(stderr, "ax25_default_path="); for (k = 0; k < config->ax25_default_path.fsa_ax25.sax25_ndigis; k++) fprintf(stderr, " %s", ax25_ntoa(&config->ax25_default_path. fsa_digipeater[k])); fprintf(stderr, "\n.\n"); } } ax25-apps/ax25rtd/ax25rtd.c0000644000175000017500000001200313521356721013663 0ustar irlirl/* * Copyright (c) 1996 Joerg Reuter (jreuter@poboxes.com) * * 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 received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA * 02110-1301, USA. * */ /* * This daemon tries to learn AX.25, ARP, IP route entries by listening * to the AX.25 traffic. It caches up to 256 entries (in "FIFO" mode) * and saves the cache on demand or at shutdown in /var/ax25/ax25rtd/ip_route * and /var/ax25/ax25rtd/ax25_route. The configuration file is * /etc/ax25/ax25rtd.conf, you can almost everything configure * there. See ax25rtcl.c for runtime maintainance. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GLIBC__ #include #else #include #endif #include #include #include #include #include "../pathnames.h" #include "ax25rtd.h" config *Config = NULL; int reload = 0; ip_rt_entry *ip_routes; int ip_routes_cnt; int ip_maxroutes = IP_MAXROUTES; ax25_rt_entry *ax25_routes; int ax25_routes_cnt; int ax25_maxroutes = AX25_MAXROUTES; char ip_encaps_dev[32] = ""; char iproute2_table[32] = ""; config *dev_get_config(char *dev) { config *config; for (config = Config; config; config = config->next) if (!strcmp(config->dev, dev)) return config; return port_get_config(dev); } config *port_get_config(char *port) { config *config; for (config = Config; config; config = config->next) if (!strcmp(config->port, port)) return config; return NULL; } static void sig_reload(int d) { reload = 1; signal(SIGHUP, sig_reload); } static void sig_debug(int d) { fprintf(stderr, "config:\n"); dump_config(2); fprintf(stderr, "ip-routes:\n"); dump_ip_routes(2, 0); fprintf(stderr, "ax25-routes:\n"); dump_ax25_routes(2, 0); signal(SIGUSR1, sig_debug); } static void sig_term(int d) { save_cache(); daemon_shutdown(0); } void daemon_shutdown(int reason) { unlink(DATA_AX25ROUTED_CTL_SOCK); exit(reason); } #define FD_MAX(fd) {fd_max = (fd > fd_max? fd : fd_max); FD_SET(fd, &read_fds);} int main(int argc, char **argv) { char buf[256]; int size, s, cntrl_s, cntrl_fd; socklen_t cntrl_len; struct sockaddr_un cntrl_addr; fd_set read_fds, write_fds; int fd_max; if (ax25_config_load_ports() == 0) { fprintf(stderr, "ax25rtd: no AX.25 port configured\n"); return 1; } load_config(); load_cache(); if (fork()) return 0; s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_AX25)); if (s == -1) { perror("AX.25 socket"); return 1; } cntrl_s = socket(AF_UNIX, SOCK_STREAM, 0); if (cntrl_s < 0) { perror("Control socket"); return 1; } unlink(DATA_AX25ROUTED_CTL_SOCK); cntrl_addr.sun_family = AF_UNIX; strcpy(cntrl_addr.sun_path, DATA_AX25ROUTED_CTL_SOCK); cntrl_len = sizeof(cntrl_addr.sun_family) + strlen(DATA_AX25ROUTED_CTL_SOCK); if (bind(cntrl_s, (struct sockaddr *) &cntrl_addr, cntrl_len) < 0) { perror("bind Control socket"); daemon_shutdown(1); } chmod(DATA_AX25ROUTED_CTL_SOCK, 0600); listen(cntrl_s, 1); signal(SIGUSR1, sig_debug); signal(SIGHUP, sig_reload); signal(SIGTERM, sig_term); cntrl_fd = -1; for (;;) { fd_max = 0; FD_ZERO(&read_fds); FD_ZERO(&write_fds); FD_MAX(s); if (cntrl_fd > 0) { FD_MAX(cntrl_fd); FD_SET(cntrl_fd, &write_fds); } else { FD_MAX(cntrl_s); } if (select(fd_max + 1, &read_fds, NULL, &write_fds, NULL) < 0) { if (errno == EINTR) /* woops! */ continue; if (!FD_ISSET(cntrl_fd, &write_fds)) { perror("select"); save_cache(); daemon_shutdown(1); } else { close(cntrl_fd); cntrl_fd = -1; continue; } } if (cntrl_fd > 0) { if (FD_ISSET(cntrl_fd, &read_fds)) { size = read(cntrl_fd, buf, sizeof(buf)); if (size > 0) { buf[size] = '\0'; interpret_command(cntrl_fd, buf); } else { close(cntrl_fd); cntrl_fd = -1; } } } else if (FD_ISSET(cntrl_s, &read_fds)) { cntrl_fd = accept(cntrl_s, (struct sockaddr *)&cntrl_addr, &cntrl_len); if (cntrl_fd < 0) { perror("accept Control"); save_cache(); daemon_shutdown(1); } } if (reload) reload_config(); if (FD_ISSET(s, &read_fds)) ax25_receive(s); } return 0; /* what ?! */ } ax25-apps/ax25rtd/Makefile.am0000644000175000017500000000707613521356721014300 0ustar irlirl etcfiles = ax25rtd.conf varfiles = ax25_route ip_route man_MANS = ax25rtd.8 ax25rtctl.8 ax25rtd.conf.5 CLEANFILES = ax25rtd.8 ax25rtd.8.tmp \ ax25rtctl.8 ax25rtctl.8.tmp \ ax25rtd.conf.5 ax25rtd.conf.5.tmp ax25rtd.8: ax25rtd.man name_ax25rtd=$$(echo ax25rtd | sed -e '$(transform)') \ name_Ax25rtd=$$(echo $$name_ax25rtd | sed -r 's@^(.)@\U\1\E@') && \ name_AX25RTD=$$(echo $$name_ax25rtd | sed -r 's@^(.*)@\U\1\E@') && \ name_ax25rtctl=$$(echo ax25rtctl | sed -e '$(transform)') && \ name_Ax25rtctl=$$(echo $$name_ax25rtctl | sed -r 's@^(.)@\U\1\E@') && \ name_AX25RTCTL=$$(echo $$name_ax25rtctl | sed -r 's@^(.*)@\U\1\E@') && \ sed -e "s/@@@ax25rtd@@@/$$name_ax25rtd/g" \ -e "s/@@@Ax25rtd@@@/$$name_Ax25rtd/g" \ -e "s/@@@AX25RTD@@@/$$name_AX25RTD/g" \ -e "s/@@@ax25rtctl@@@/$$name_ax25rtctl/g" \ -e "s/@@@Ax25rtctl@@@/$$name_Ax25rtctl/g" \ -e "s/@@@AX25RTCTL@@@/$$name_AX25RTCTL/g" \ $(srcdir)/ax25rtd.man > ax25rtd.8.tmp && \ mv ax25rtd.8.tmp ax25rtd.8; ax25rtctl.8: ax25rtctl.man name_ax25rtd=$$(echo ax25rtd | sed -e '$(transform)') \ name_Ax25rtd=$$(echo $$name_ax25rtd | sed -r 's@^(.)@\U\1\E@') && \ name_AX25RTD=$$(echo $$name_ax25rtd | sed -r 's@^(.*)@\U\1\E@') && \ name_ax25rtctl=$$(echo ax25rtctl | sed -e '$(transform)') && \ name_Ax25rtctl=$$(echo $$name_ax25rtctl | sed -r 's@^(.)@\U\1\E@') && \ name_AX25RTCTL=$$(echo $$name_ax25rtctl | sed -r 's@^(.*)@\U\1\E@') && \ sed -e "s/@@@ax25rtd@@@/$$name_ax25rtd/g" \ -e "s/@@@Ax25rtd@@@/$$name_Ax25rtd/g" \ -e "s/@@@AX25RTD@@@/$$name_AX25RTD/g" \ -e "s/@@@ax25rtctl@@@/$$name_ax25rtctl/g" \ -e "s/@@@Ax25rtctl@@@/$$name_Ax25rtctl/g" \ -e "s/@@@AX25RTCTL@@@/$$name_AX25RTCTL/g" \ $(srcdir)/ax25rtctl.man > ax25rtctl.8.tmp && \ mv ax25rtctl.8.tmp ax25rtctl.8; ax25rtd.conf.5: ax25rtd.conf.man name_ax25rtd=$$(echo ax25rtd | sed -e '$(transform)') \ name_Ax25rtd=$$(echo $$name_ax25rtd | sed -r 's@^(.)@\U\1\E@') && \ name_AX25RTD=$$(echo $$name_ax25rtd | sed -r 's@^(.*)@\U\1\E@') && \ name_ax25rtctl=$$(echo ax25rtctl | sed -e '$(transform)') && \ name_Ax25rtctl=$$(echo $$name_ax25rtctl | sed -r 's@^(.)@\U\1\E@') && \ name_AX25RTCTL=$$(echo $$name_ax25rtctl | sed -r 's@^(.*)@\U\1\E@') && \ sed -e "s/@@@ax25rtd@@@/$$name_ax25rtd/g" \ -e "s/@@@Ax25rtd@@@/$$name_Ax25rtd/g" \ -e "s/@@@AX25RTD@@@/$$name_AX25RTD/g" \ -e "s/@@@ax25rtctl@@@/$$name_ax25rtctl/g" \ -e "s/@@@Ax25rtctl@@@/$$name_Ax25rtctl/g" \ -e "s/@@@AX25RTCTL@@@/$$name_AX25RTCTL/g" \ $(srcdir)/ax25rtd.conf.man > ax25rtd.conf.5.tmp && \ mv ax25rtd.conf.5.tmp ax25rtd.conf.5; installconf: $(mkinstalldirs) $(DESTDIR)$(AX25_SYSCONFDIR) @list='$(etcfiles)'; for p in $$list; do \ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p"; \ $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p; \ done $(mkinstalldirs) $(DESTDIR)$(AX25_LOCALSTATEDIR)/ax25rtd @list='$(varfiles)'; for p in $$list; do \ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_LOCALSTATEDIR)/ax25rtd/$$p"; \ $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_LOCALSTATEDIR)/ax25rtd/$$p; \ done sbin_PROGRAMS = ax25rtd ax25rtctl LDADD = $(AX25_LIB) EXTRA_DIST = $(etcfiles) ax25rtd.man ax25rtctl.man ax25rtd.conf.man $(doc_DATA) doc_DATA= TODO.ax25rtd README.ax25rtd ax25rtd_SOURCES = \ ax25rtd.c \ ax25rtd.h \ cache_ctl.c \ cache_dump.c \ config.c \ listener.c AX25_SYSCONFDIR=$(sysconfdir)/ax25 AX25_LOCALSTATEDIR=$(localstatedir)/ax25 AM_CPPFLAGS = -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" ax25-apps/ax25rtd/README.ax25rtd0000644000175000017500000002120213521356721014377 0ustar irlirlax25rtd with DG2FEF/DG1KJD Linux-AX.25 Kernel NOTES: - Here is an example ax25rtd.conf file: ax25-maxroutes 256 ip-maxroutes 256 ip-encaps-dev ipax0 [sp0] ax25-learn-routes yes ax25-learn-only-mine yes ip-adjust-mode no ip-learn-routes no arp-add yes [ipax0] arp-add yes ip-learn-routes yes - ax25-learn-only-mine has to be on for now, since ax25rtd is not able to recognize digipeater paths yet when this node is part of it. - ax25rtd will be replaced as soon as possible by something reasonable, so this is only a temporary solution - ipax0 must be up when ax25rtd is startet - all other listeners (ax25d et al) must be registrated at this point of time, too - There must be an entry for ipax0 in /etc/axports, here is an example: ipax0 dg1kjd-10 76800 256 7 IP-encap device ---- Jens ***************************************************************** * Please send all comments and suggestions regarding ax25rtd to * * Klaus Kudielka (klaus.kudielka@ieee.org). * ***************************************************************** Just a quick small README... This will hopefully go into the man pages ASAP. Ax25routed ---------- /usr/sbin/ax25rtd This is a daemon that: - emulates the ceased "autorouter" of Linux Kernel AX.25 - sets up ARP entries automagically - may adjust IP routes and encapsulation mode (although I really do not recommend to use this feature...) The "autorouter" is not really an autorouter. It just listens to the AX.25 traffic on your ports and uses this information to setup AX.25 routes. This can be turned on or off by altering the configuration file /etc/ax25/ax25rtd.conf. Ax25routed provides a socket /var/ax25/ax25rtd/control which is used for runtime maintainance through ax25rtctl or to set up new routes by other daemons (a Flexnet router, perhaps?) On startup ax25rtd reads the configuration file and afterwards preloads the caches from the files /var/ax25/ax25rtd/ax25_route and /var/ax25/ax25rtd/ip_route. On SIGTERM or ax25rtctl --save it saves the caches to those files. ax25rtd.conf --------------- The file /etc/ax25/ax25rtd.conf is the configuration file for ax25rtd. The parameters of the options shown here are the default values except the ones marked with (example) ax25-maxroutes 256 ip-maxroutes 256 The maximum size of the three lists / caches. On overflow, ax25rtd will substitute the oldest entry with the new one. [1k2] This marks the beginning of per-port definitions. Note that you have to use port names as defined in axports(5) here, anywhere else you may use the port or the device name. ax25-learn-routes no Set this to "yes", ax25rtd will add the routing information for every heard frame (with complete digipeater path) to the kernel AX.25 routing table. Note that ax25rtd's internal cache will be updated anyway, regardless of this option. ax25-learn-only-mine no If you set it to "yes", only frames that are sent to (1) the interface callsign, (2) any of the listeners on this device, or (3) the callsigns specified by ax25-more-mycalls will be used to update the internal cache and (depending on ax25-learn-routes) the kernel routing table. ax25-add-path db0ach (example) This is useful on DAMA digipeaters. In this case, the DAMA master has to be used for _every_ AX.25 connection, thus ax25rtd will add this digipeater(s) to every target we learn that has no digipeater path (yet). "db0ach" is just an example here. ax25-more-mycalls dl1bke dl1bke-8 (example) You can specify more calls as calls here that belong to this interface... "dl1bke" and "dl1bke-8" are examples. ip-learn-routes no If set to "yes", ax25rtd will modify the IP routing table if it receives an IP frame (directed to us). This is dangerous! It should not screw up your routing table, though. Ax25rtd recognizes the netmask of the device and will adjust the route only if it fits the netmask and the old route points to one of the devices ax25rtd knows about (hence an AX.25 device). The problems begin if you have more than one port and a user is able to hear your outgoing traffic on at least two of them. Due to technical reasons ax25rtd adjusts the route _after_ the kernel has sent the reply to the received TCP frame already. This has technical reasons. If the remote does the same both are switching between the two ports. Don't use this feature unless you know what you are doing. It _should_ be safe do enable this on one-port machines, although I strongly recommend to set a network route instead, i.e.: route add -net 44.0.0.0 scc3 Note that ax25rtd's internal cache will be updated anyway, regardless of this option. irtt If ip-learn-routes is enabled this will assign newly added routes an initial round trip time (IRTT) for TCP. is measured in msec, hence irtt 10000 sets the irtt to 10 seconds. A value of 0 disables this feature (default). ip-adjust-mode no If you set this option to "yes" ax25rtd will change the IP encapsulation mode according to the last received IP frame. The problem with this option is that the kernel AX.25 sends a received IP frame to the IP layer regardless if it was sent in UI frame encapsulation "mode datagram (dg)" or in I frame encaps, hence in an AX.25 connection, "mode virtual connect (vc)". The Linux kernel will respond to this frame before ax25rtd can adjust the mode. If the remote does the same... You get the picture. Don't use this feature unless you know what you are doing. arp-add no This option, if set to "yes", changes the ARP table to the source callsign of the received frame. It should be harmless, just has the the effect that if it is a new entry, the Linux ARP code will send one ARP request before ax25rtd has adjust the ARP table. If there was already an existing ARP entry for this IP route, one IP datagram will be sent to the old address. Not really a problem, I hope. Ax25rtctl --------- /usr/sbin/ax25rtctl