├── .github └── workflows │ └── tests.yml ├── .gitignore ├── README.md ├── build └── build-all.pl ├── doc ├── BUILDING.md ├── CONFIGURATION.md ├── CONTRIBUTING.md ├── DEBUGGING.md ├── DESIGN.md ├── HOME.md ├── IGATE-HINTS.md ├── INSTALLING.md ├── LICENSE ├── MONITORING.md ├── MULTIPLE.md ├── README.md ├── TIPS.md ├── TODO ├── TRANSLATING.md ├── WINDOWS.md ├── devel │ └── test-packets ├── web-footer.html └── web-header.html ├── src ├── .gitignore ├── LICENSE ├── Makefile.in ├── README.sources ├── VERSION ├── ac-hdrs.h.in ├── accept.c ├── accept.h ├── acl.c ├── acl.h ├── aclocal.m4 ├── apparmor.aprsc ├── aprsc-prepare-chroot.sh ├── aprsc.8.in ├── aprsc.c ├── aprsc.conf ├── aprsc_munin ├── cJSON.c ├── cJSON.h ├── cellmalloc.c ├── cellmalloc.h ├── cfgfile.c ├── cfgfile.h ├── client_heard.c ├── client_heard.h ├── clientlist.c ├── clientlist.h ├── config.c ├── config.h ├── configure ├── configure.ac ├── counterdata.c ├── counterdata.h ├── debian │ ├── .gitignore │ ├── aprsc-chroot.service │ ├── aprsc.default │ ├── aprsc.init │ ├── aprsc.service │ ├── aprsc@.service │ ├── changelog.release │ ├── compat │ ├── conffiles │ ├── control │ ├── copyright │ ├── dirs │ ├── docs │ ├── links │ ├── postinst │ ├── postrm │ ├── preinst │ ├── prerm │ └── rules ├── dupecheck.c ├── dupecheck.h ├── errno_aprsc.c ├── errno_aprsc.h ├── filter.c ├── filter.h ├── gai_strerror.c ├── getnameinfo.c ├── gfx │ ├── aprsc-joulukissa-2013.xcf │ ├── aprsc-logo-pekka.jpg │ ├── aprsc-logo-shadow.xcf │ ├── aprsc-logo.xcf │ ├── aprsc-logo1.png │ ├── aprsc-logo2.png │ ├── aprsc-logo2.xcf │ ├── aprsc-logo3.xcf │ ├── aprsc-logo4.xcf │ ├── aprsc-minus.png │ └── aprsc-plus.png ├── historydb.c ├── historydb.h ├── hlog.c ├── hlog.h ├── hmalloc.c ├── hmalloc.h ├── http.c ├── http.h ├── incoming.c ├── incoming.h ├── inet_ntop.c ├── inet_pton.c ├── install-sh ├── keyhash.c ├── keyhash.h ├── login.c ├── login.h ├── m4 │ └── ax_check_gnu_make.m4 ├── messaging.c ├── messaging.h ├── netdb6.h ├── netlib.c ├── netlib.h ├── outgoing.c ├── outgoing.h ├── parse_aprs.c ├── parse_aprs.h ├── parse_qc.c ├── parse_qc.h ├── passcode.c ├── passcode.h ├── random.c ├── random.h ├── rpm │ ├── .gitignore │ ├── aprsc.init │ └── aprsc.spec.in ├── rwlock.c ├── rwlock.h ├── sctp.c ├── sctp.h ├── status.c ├── status.h ├── tls.c ├── tls.h ├── tools │ ├── .gitignore │ ├── coverity-build-submit.sh │ ├── eventload.c │ ├── floodconnect.c │ ├── sleeptest.c │ └── t2acl.pl ├── uplink.c ├── uplink.h ├── version.c ├── version.h ├── version_branch.h ├── web │ ├── angular-translate-loader-url.min.js │ ├── angular-translate.min.js │ ├── angular.min.js │ ├── aprsc-graph.js │ ├── aprsc-joulukissa.jpg │ ├── aprsc-logo4.png │ ├── aprsc-logo4@2x.png │ ├── aprsc.css │ ├── aprsc.js │ ├── bootstrap │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap-theme.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.min.js │ │ │ └── npm.js │ ├── excanvas.min.js │ ├── favicon.ico │ ├── index.html │ ├── jquery.flot.min.js │ ├── jquery.flot.resize.min.js │ ├── jquery.flot.selection.min.js │ ├── jquery.flot.time.min.js │ ├── jquery.min.js │ ├── ngDialog-theme-plain.min.css │ ├── ngDialog.min.css │ ├── ngDialog.min.js │ ├── strings-en.json │ └── strings-fi.json ├── worker.c ├── worker.h ├── xpoll.c └── xpoll.h ├── tests ├── Makefile ├── aggregator │ ├── 10aggr_join.t │ └── 20socket-timeouts.t ├── ca │ ├── cert-selfsigned.sh │ └── openssl.conf ├── cfg-aprsc │ ├── acl-all.acl │ ├── aggregator │ ├── basic │ ├── qconstr-protocols │ ├── sctp-hub │ ├── sctp-leaf │ ├── tls1 │ └── uplinks ├── cfg-javap │ ├── basic │ └── uplinks ├── libperl │ ├── Ham │ │ └── APRS │ │ │ ├── IS.pm │ │ │ ├── IS_Fake.pm │ │ │ └── IS_Fake_UDP.pm │ ├── istest.pm │ └── runproduct.pm ├── t │ ├── 00startstop.t │ ├── 01login.t │ ├── 02login-reject.t │ ├── 10dupecheck.t │ ├── 11misc-drops.t │ ├── 12quirks-mode.t │ ├── 13thirdparty.t │ ├── 20qconstr-clientonly.t │ ├── 20qconstr-verified.t │ ├── 21qconstr-unver.t.disabled │ ├── 22path-trace.t │ ├── 22qconstr-uplink.t │ ├── 23qconstr-long.t │ ├── 24qconstr-protocols.t │ ├── 27digipath-long.t │ ├── 30filter-cmd.t │ ├── 30parser-filter.t │ ├── 31parser-nmea.t │ ├── 32filter-negative.t │ ├── 33filter-pref-buddy-obj.t │ ├── 34filter-type-symbol.t │ ├── 35filter-digi-entry-unproto.t │ ├── 36filter-area-my-friend.t │ ├── 37filter-qconstr.t │ ├── 38filter-verylong.t │ ├── 40messaging.t │ ├── 50disc-blobs.t │ ├── 51load.t │ ├── 51uplink-reconnect.t │ ├── 60udp-client.t │ ├── 61udp-peer.t │ ├── 62http-client.t │ ├── 63udp-submit.t │ ├── 64udp-load.t │ ├── 65dupeclient.t │ ├── 66http-status.t │ ├── 69sctp.t │ └── 70live-upgrade.t ├── testdata │ └── status-urls.txt └── tls-openssl.conf └── tools ├── Markdown.license ├── Markdown.pl ├── Markdown.readme ├── aprs-is-copy ├── aprs-is-coresimurx ├── aprs-is-file-feed ├── aprs-is-multirx ├── aprs-is-rx ├── aprs-is-xmit └── aprsis-http-post /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Build Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | os: [ ubuntu-latest, ubuntu-22.04 ] 14 | runs-on: ${{ matrix.os }} 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: install deps 20 | run: | 21 | sudo apt-get install -y libevent-dev \ 22 | perl perl-modules libio-socket-inet6-perl libjson-xs-perl \ 23 | libwww-perl \ 24 | libparse-recdescent-perl \ 25 | libprotobuf-c-dev protobuf-c-compiler libprotoc-dev \ 26 | libprotobuf-dev protobuf-compiler cmake \ 27 | libsctp-dev \ 28 | openssl 29 | 30 | - name: configure 31 | run: | 32 | cd src 33 | ./configure 34 | 35 | - name: make 36 | run: | 37 | cd src 38 | make -j4 39 | 40 | - name: make testinstall 41 | run: | 42 | cd src 43 | make testinstall 44 | 45 | - name: Run tests 46 | run: | 47 | cd tests 48 | make test aggrtest 49 | 50 | 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Backups 2 | *~ 3 | 4 | # Object files 5 | *.o 6 | *.d 7 | 8 | # Libraries 9 | *.lib 10 | *.a 11 | 12 | # Shared objects (inc. Windows DLLs) 13 | *.dll 14 | *.so 15 | *.so.* 16 | *.dylib 17 | 18 | # build products for dpkg 19 | aprsc_*.changes 20 | aprsc_*.deb 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | aprsc - an APRS-IS server in C 3 | ============================== 4 | 5 | ![Build Status](https://github.com/hessu/aprsc/actions/workflows/tests.yml/badge.svg) 6 | 7 | 8 | You're looking at the source code of aprsc, an open-source APRS-IS 9 | server. 10 | 11 | For more information, please refer to the following resources: 12 | 13 | * [Home page](http://he.fi/aprsc/) 14 | * [Installation instructions](http://he.fi/aprsc/INSTALLING.html) 15 | * [Source code downloads](http://he.fi/aprsc/down/) 16 | * [Conference paper, Digital Communications Conference 2012, Atlanta, GA](http://he.fi/aprsc/dcc-2012-aprsc.pdf) 17 | * [Contributing to the aprsc project](http://he.fi/aprsc/CONTRIBUTING.html) 18 | 19 | Have fun! 20 | 21 | - Hessu, OH7LZB 22 | 23 | -------------------------------------------------------------------------------- /doc/DEBUGGING.md: -------------------------------------------------------------------------------- 1 | 2 | Debugging aprsc issues, tips and tricks 3 | ======================================= 4 | 5 | Enabling debug logging 6 | ------------------------- 7 | 8 | Change the log level to debug by changing "-e info" to "-e debug" on the 9 | aprsc command line. Insert "-e debug" if you don't have an -e parameter. 10 | 11 | On Debian and Ubuntu the parameter goes in /etc/default/aprsc, on the 12 | DAEMON_OPTS line. On CentOS it goes to /etc/sysconfig/aprsc. On other 13 | systems it typically goes in the init script. 14 | 15 | Enabling core dumps on Linux 16 | ------------------------------- 17 | 18 | Allow binary doing setuid() to dump core: 19 | 20 | echo 2 > /proc/sys/fs/suid_dumpable 21 | 22 | Tune core file name pattern: 23 | 24 | echo "/var/core/core.%e.%p.%s" > /proc/sys/kernel/core_pattern 25 | 26 | Create a directory within the aprsc chroot where the core can be 27 | dumped: 28 | 29 | mkdir -p /opt/aprsc/var/core 30 | chmod a+w /opt/aprsc/var/core 31 | 32 | -------------------------------------------------------------------------------- /doc/DESIGN.md: -------------------------------------------------------------------------------- 1 | 2 | Design 3 | --------- 4 | 5 | aprsc's basic design was drawn out in a pizza session in early 2008. The 6 | design goals were: 7 | 8 | * High throughput and small enough latency 9 | * Support for thousands of clients per server 10 | * Support for heavy bursts of new clients (CWOP hits every 5 or 10 minutes) 11 | * Scalability over multiple CPUs 12 | * Low context switch overhead 13 | * Low lock contention between threads 14 | 15 | A modern hybrid threaded / event-driven approach was selected. All recently 16 | developed high-performance Internet servers work in this mode (some with 17 | multiple event-driven processes, some with event-driven threads). There is 18 | a small, fixed number of threads, close to the number of CPU cores on the 19 | server, so that multiple CPU cores can be utilized, but the relatively 20 | expensive context switches between a high number of threads will not cause 21 | serious overhead. 22 | 23 | When the server is under heavy load, data transfers between threads happen 24 | in blocks of multiple data units, so that contention on mutexes and 25 | read-write locks will not block concurrent execution of the threads. Lock 26 | contention makes many multi-threaded servers effectively single-threaded and 27 | unable to utilize more than a single CPU core. 28 | 29 | Main work is done by 1 to N worker threads. In real-world APRS-IS today, 1 30 | worker thread is enough, but if a server was really heavily loaded with 31 | thousands of clients, 1 less than the number of CPU cores would be optimal. 32 | 33 | A worker thread's workflow goes like this: 34 | 35 | 1. Read data from connected clients 36 | 2. Do initial APRS-IS packet parsing (SRCCALL>DSTCALL,PATH:DATA) 37 | 3. Do Q-construct processing (,qAx in the PATH) 38 | 4. Parse APRS formatted information in the DATA to extract enough details 39 | to support filtering in the outgoing / filtering phase 40 | 5. Pass on received packets to the dupecheck thread for duplicate removal 41 | 6. Get packets, sorted to unique and duplicate packets, from the dupecheck 42 | thread 43 | 7. Send out packets to clients as instructed by the listening port's 44 | configuration and the client's filter settings 45 | 46 | The Dupecheck thread maintains a cache of packets heard during the past 30 47 | seconds. There is a dedicated thread for this cache, so that the worker 48 | threads do not need to compete for access to the shared resource. The thread 49 | gets packets from the worker threads, does dupe checking, and puts the 50 | unique and duplicate packets in two global ordered buffer queues. The 51 | workers then walk through those buffers and do filtering to decide which 52 | packets should be sent to which clients. 53 | 54 | An Uplink threads initiates connections to upstream servers and reconnects 55 | them as needed. After a successful connection the socket will be passed on 56 | to a Worker thread which will proceed to exchange traffic with the remote 57 | server for the duration of the connection. 58 | 59 | An Accept thread listens on the TCP ports for new incoming connections, does 60 | access list checks, and distributes allowed connections evenly across worker 61 | threads. 62 | 63 | An HTTP thread runs an event-driven HTTP server (libevent2 based) to support 64 | the status page and HTTP position uploads. Since implementing nice web user 65 | interfaces in plain C is not very convenient or effective, the status page 66 | is produced using modern Web 2.0 methods. The HTTP server can only generate 67 | a dynamic JSON-encoded status file and serve static files. An empty 68 | index.html file loads a static JavaScript file, which then periodically 69 | loads the JSON status data and formats the contents of the status page 70 | within the client's browser. This approach allowed clean separation of 71 | server code (C) and web presentation (HTML5/JavaScript/jQuery/flot). 72 | 73 | Both developers are experienced professional Unix C programmers, so the 74 | programming language was easy to select. We also had plenty of existing 75 | code that could be re-used in this project. 76 | 77 | -------------------------------------------------------------------------------- /doc/HOME.md: -------------------------------------------------------------------------------- 1 | 2 | aprsc - an APRS-IS server in C 3 | ============================== 4 | 5 | aprsc (pronounced a-purrs-c) is a plain APRS-IS server intended to be used 6 | on the core and Tier2 APRS-IS servers. It is written in the C language, and 7 | it runs on Linux and Unix servers. 8 | 9 | If you need igate or other radio-interfacing features, aprsc is not for you. 10 | 11 | 12 | Status of the project 13 | ------------------------ 14 | 15 | aprsc was released in 2012, and it has since been in continuous use on a 16 | large percentage of APRS-IS servers. It has been found to be easy to set up 17 | and stable in production use. Since it is pretty much feature complete by 18 | now, new major versions come out rarely, but smaller bugs get fixed at 19 | times. 20 | 21 | 22 | Features (and lack of) 23 | ------------------------- 24 | 25 | aprsc has been designed strictly for use within the APRS-IS core, hub and 26 | Tier2 servers. It includes only the basic functionality required by those 27 | servers. 28 | 29 | It does not, and will not, have any additional functions such as igating, 30 | digipeating, interfacing to radios, D-PRS or other gateway functions, or 31 | object generation. The idea is to keep aprsc relatively simple and lean, 32 | and leave the more specialized features for more specialized software. 33 | 34 | If you need a nice, compact igate software for Linux, please take a look at 35 | either aprx or aprs4r. If you need to run an APRS-IS server on some 36 | platform not supported by aprsc, or if you need the features existing in 37 | javAPRSSrvr which are missing from aprsc, javAPRSSrvr is the right choice 38 | for you - it's got a lot of good features that many of you need, and it 39 | works on virtually all operating systems. If you need an igate for Windows, 40 | APRSIS32 should be good. 41 | 42 | 43 | Licensing, environments and requirements 44 | ------------------------------------------- 45 | 46 | aprsc is open source, licensed under the BSD license. It has about 11000 47 | lines of relatively clean C code, built using the usual ./configure && make 48 | && make install method. The embedded HTTP status server is powered by the 49 | libevent2 library, no other extra libraries are needed. 50 | 51 | Linux and OS X are the main development environments and will receive 52 | premium support, but FreeBSD and Solaris 11 are known to work too. Packaged 53 | binaries for Debian, Ubuntu and CentOS are available for super-easy 54 | installations and automatic upgrades using APT and YUM. 55 | 56 | 57 | Discussion group 58 | ------------------- 59 | 60 | aprsc has it's own [discussion group][aprsc-group] which also functions as 61 | a mailing list. If you run aprsc, please subscribe to the group to keep 62 | updated on new versions. 63 | 64 | [aprsc-group]: https://groups.google.com/forum/#!forum/aprsc 65 | 66 | 67 | Getting and installing aprsc 68 | ------------------------------- 69 | 70 | aprsc is currently best supported on Debian and Ubuntu. 71 | Please refer to the [INSTALLING](INSTALLING.html) document 72 | for instructions. 73 | 74 | After the software is installed, please go through the 75 | [CONFIGURATION](CONFIGURATION.html) document. 76 | 77 | You may also look into translating the aprsc status page to your 78 | language. The process is described in the 79 | [TRANSLATING](TRANSLATING.html) document. 80 | 81 | 82 | Other documentation 83 | ---------------------- 84 | 85 | * [README](README.html) 86 | * [Paper on aprsc for TAPR DCC 2012](dcc-2012-aprsc.pdf) 87 | * Presentation slides from TAPR DCC 2012 (will be here shortly) 88 | 89 | 90 | Contributing to aprsc 91 | ------------------------ 92 | 93 | aprsc is an open source project, so you're welcome to contribute bug fixes 94 | and improvements. Please see [CONTRIBUTING](CONTRIBUTING.html) for details! 95 | 96 | 97 | -------------------------------------------------------------------------------- /doc/MONITORING.md: -------------------------------------------------------------------------------- 1 | 2 | Monitoring aprsc 3 | ================ 4 | 5 | It's a good practice to monitor the performance and utilisation of your 6 | server, and to generate alarms when it's not working as it should. 7 | 8 | aprsc comes with a munin plugin which makes it very easy to set up 9 | statistics graphs. Comparing graphs for aprsc against the graphs for the 10 | rest of the system can be very helpful in diagnosing performance issues. 11 | Seeing all those statistics also gives a nice warm fuzzy feeling from 12 | knowing how how the server is doing and what it is spending all those 13 | electrons on. 14 | 15 | For example, please take a look at my [aprsc-specific Munin graphs from 16 | T2FINLAND](http://he.fi/aprsc/munin/). 17 | 18 | 19 | Setting up munin on Debian or Ubuntu 20 | --------------------------------------- 21 | 22 | Munin consists of two pieces, an agent running on all servers (munin-node), 23 | collecting raw numbers from the operating system and the software running on 24 | it, and the master data collector (munin). If you have many servers, put 25 | the master data collector on just one of them (that server needs to have a 26 | web server to publish the statistics). If you have a single server, put a 27 | web server, the agent and the master on that. 28 | 29 | Installing the agent, the master and the standard Apache web server: 30 | 31 | sudo apt-get install munin-node munin apache2-mpm-worker 32 | 33 | It will pull up quite a few other packages as dependencies, but that's OK. 34 | 35 | 36 | Setting up munin on other Linux distributions 37 | ------------------------------------------------ 38 | 39 | Instructions for each distribution can be found [on munin's home 40 | page](http://munin-monitoring.org/wiki/LinuxInstallation). 41 | 42 | 43 | Setting up the munin plugin 44 | ------------------------------ 45 | 46 | If munin is installed when installing or upgrading aprsc, aprsc's 47 | post-install script will automatically configure aprsc's munin plugin. 48 | 49 | If you have aprsc running already, reconfigure it: 50 | 51 | sudo dpkg-reconfigure aprsc 52 | 53 | That'll set up the munin plugin (among a few other things, like triggering a 54 | live upgrade to the current version). Now, you'll need to give munin a kick 55 | to make it notice the new plugins: 56 | 57 | sudo /etc/init.d/munin-node restart 58 | 59 | Future versions of aprsc might do that automatically for you. 60 | 61 | 62 | Wait! 63 | -------- 64 | 65 | Wait for some 10-15 minutes. Have a nice cup of coffee or tea, or some 66 | other beverage according to your personal preference. Munin updates every 5 67 | minutes, but after installation or adding new plugins, it'll take a couple 68 | of rounds before it starts generating graphs for those. 69 | 70 | 71 | See. 72 | ------- 73 | 74 | Surf to http://yourserver.example.com/munin/ and browse around! 75 | Ooops, got a "403 Forbidden" error? Proceed to the next step. 76 | 77 | 78 | Edit the web server's config file 79 | ------------------------------------ 80 | 81 | At least on debian, the web server is configured by default to only allow a 82 | local web browser to access the Munin subdirectory. Open up 83 | /etc/apache2/conf.d/munin in your favourite text editor, and put your own IP 84 | address, network or domain on and allow line. You can add new Allow lines 85 | next to the one that's already there, like this: 86 | 87 | Allow from 44.0.0.0/8 88 | Allow from .ampr.org 89 | 90 | After editing apache's config file, tell it to re-read configuration by 91 | doing a graceful restart: 92 | 93 | sudo apache2ctl graceful 94 | 95 | And surf to http://yourserver.example.com/munin/ again. 96 | 97 | 98 | Setting up nagios alarms 99 | --------------------------- 100 | 101 | TODO: write 102 | -------------------------------------------------------------------------------- /doc/TIPS.md: -------------------------------------------------------------------------------- 1 | aprsc tricks and tips 2 | ===================== 3 | 4 | 5 | Providing access on low TCP ports (like 23) 6 | ---------------------------------------------- 7 | 8 | For security reasons aprsc drops root privileges as soon as possible after 9 | starting up (if it ever had them in the first place). Listening on 10 | privileged "low" ports below 1024 normally requires root privileges, which 11 | aprsc no longer has when it comes to the point where it would start binding 12 | those ports. 13 | 14 | You can use a NAT based method to redirect traffic from port 23 to port 15 | 14580 (or some other high unprivileged port your server is listening on). 16 | Replace *youripaddress* with your external IP address. The local listening 17 | address (to-destination) cannot be localhost, so use the same IP address. 18 | These two commands need to go somewhere in your startup scripts or firewall 19 | configurations. 20 | 21 | root@box:~# iptables -t nat -A PREROUTING -d *youripaddress* 22 | -p tcp --dport 23 -m addrtype --dst-type LOCAL -j DNAT 23 | --to-destination *youripaddress*:14580 24 | 25 | root@box:~# iptables -t nat -A OUTPUT -d *youripaddress* 26 | -p tcp --dport 29 -m addrtype --dst-type LOCAL -j DNAT 27 | --to-destination *youripaddress*:14580 28 | 29 | When you wish to view your current NAT configuration on Linux, remember to 30 | specifically ask for the nat tables: 31 | 32 | root@box:~# iptables -t nat -L 33 | -------------------------------------------------------------------------------- /doc/TODO: -------------------------------------------------------------------------------- 1 | TODO ITEMS LIST 2 | 3 | - UDP communication 4 | - "Client" version exists -- unidirectional data flow from server to client, 5 | server discards all data sent to it 6 | - Future todo: 7 | - network performance buffering of data into UDP packets is needed 8 | - client traffic flow in UDP form ? 9 | 10 | - complete output filters 11 | - s-filter is still missing 12 | 13 | To Consider: 14 | 15 | - SCTP sockets 16 | 17 | - UDP with multiple messages in same datagram (up to about 1400 bytes 18 | in single UDP frame), AND with timestamp telling how long a message 19 | has been in transit, AND binary transparency capability... 20 | ( network byte order 32-bit value telling seconds since 2000-01-01 ? ) 21 | 22 | 23 | -------------------------------------------------------------------------------- /doc/TRANSLATING.md: -------------------------------------------------------------------------------- 1 | 2 | Translating aprsc status view to other languages 3 | =================================================== 4 | 5 | Create a new language file 6 | ---------------------------- 7 | 8 | Go to /opt/aprsc/web, and make a copy of the master English strings file. 9 | Look up your two-letter language code from 10 | [the list](https://www.w3.org/International/articles/language-tags/) 11 | ('sv' is for Swedish, for example), and use it to construct the file name. 12 | 13 | cd /opt/aprsc/web 14 | cp strings-en.json strings-sv.json 15 | 16 | Edit strings-sv.json with your favourite editor, translate all 17 | the strings. Make sure you use UTF-8 encoding in your editor 18 | and terminal. 19 | 20 | 21 | Reconfigure or restart aprsc 22 | ------------------------------- 23 | 24 | The aprsc process will scan for string files at startup and when 25 | reloading configuration. This step will only need to be done once 26 | after creating the new string file - after further edits of strings, 27 | simply reload the status web page. 28 | 29 | To reload configuration, execute the `reload` option of the startup script. 30 | 31 | On Ubuntu or Debian: 32 | 33 | sudo service aprsc reload 34 | 35 | On Centos (and others): 36 | 37 | sudo /etc/init.d/aprsc reload 38 | 39 | 40 | Add new language mapping 41 | --------------------------- 42 | 43 | Edit /opt/aprsc/web/aprsc.js - in the beginning you'll find two 44 | statements which need to be adjusted: 45 | 46 | // add additional language codes here, in the end of the list: 47 | var lang_list = ['en', 'fi']; 48 | // and, if necessary, add one new line in the beginning here, 49 | // for dialect mapping (en_US, en_GB to en): 50 | var lang_map = { 51 | 'en_*': 'en', 52 | 'fi_*': 'fi', 53 | '*': 'en' // DO NOT remove or change the default mapping to 'en' 54 | }; 55 | 56 | For Swedish translation, the first list would become: 57 | 58 | var lang_list = ['en', 'fi', 'sv']; 59 | 60 | If Swedish would have dialects ('sv_FI' for Swedish spoken in Finland) 61 | then the mapping for the variants can be added. Otherwise, only clients 62 | requesting plain 'sv' for language will get the translation. 63 | 64 | var lang_map = { 65 | 'en_*': 'en', 66 | 'fi_*': 'fi', 67 | 'sv_*': 'sv', 68 | '*': 'en' 69 | }; 70 | 71 | Pass new translation to upstream aprsc code 72 | ---------------------------------------------- 73 | 74 | Give your new translation back to the aprsc project, so that it will be 75 | included in future versions automatically, and that it'll be installed 76 | on other servers too. 77 | 78 | -------------------------------------------------------------------------------- /doc/devel/test-packets: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/doc/devel/test-packets -------------------------------------------------------------------------------- /doc/web-footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /doc/web-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 16 |
17 | 18 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | # Build products 2 | aprsc 3 | aprsc.exe 4 | aprsc.8 5 | version_data.h 6 | 7 | # configure products 8 | Makefile 9 | ac-hdrs.h 10 | config.log 11 | config.status 12 | SVNVERSION 13 | GITVERSION 14 | autom4te.cache 15 | 16 | # build temp files 17 | build-stamp 18 | configure-stamp 19 | 20 | -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2012, Matti Aarnio 2 | Copyright (c) 2007-2012, Heikki Hannikainen 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the names of Matti Aarnio and Heikki Hannikainen 15 | nor the names of its contributors may be used to endorse or promote 16 | products derived from this software without specific prior written 17 | permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/VERSION: -------------------------------------------------------------------------------- 1 | 2.1.19 2 | -------------------------------------------------------------------------------- /src/ac-hdrs.h.in: -------------------------------------------------------------------------------- 1 | /* ac-hdrs.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* Define to 1 if you have the header file. */ 4 | #undef HAVE_ALLOCA_H 5 | 6 | /* Define to 1 if you have the `atan2f' function. */ 7 | #undef HAVE_ATAN2F 8 | 9 | /* POSIX capabilities */ 10 | #undef HAVE_CAPABILITY_H 11 | 12 | /* Have clock_gettime */ 13 | #undef HAVE_CLOCK_GETTIME 14 | 15 | /* Linux event fd support */ 16 | #undef HAVE_EVENTFD_H 17 | 18 | /* Define to 1 if you have the `gai_strerror' function. */ 19 | #undef HAVE_GAI_STRERROR 20 | 21 | /* Define to 1 if you have the `getnameinfo' function. */ 22 | #undef HAVE_GETNAMEINFO 23 | 24 | /* Define to 1 if you have the `inet_ntop' function. */ 25 | #undef HAVE_INET_NTOP 26 | 27 | /* Define to 1 if you have the `inet_pton' function. */ 28 | #undef HAVE_INET_PTON 29 | 30 | /* Define to 1 if you have the header file. */ 31 | #undef HAVE_INTTYPES_H 32 | 33 | /* Have libz */ 34 | #undef HAVE_LIBZ 35 | 36 | /* Define to 1 if you have the header file. */ 37 | #undef HAVE_MEMORY_H 38 | 39 | /* Define to 1 if you have the header file. */ 40 | #undef HAVE_NETINET_SCTP_H 41 | 42 | /* Define to 1 if you have the header file. */ 43 | #undef HAVE_OPENSSL_SSL_H 44 | 45 | /* Define to 1 if you have the header file. */ 46 | #undef HAVE_POLL_H 47 | 48 | /* Linux process control prctl.h */ 49 | #undef HAVE_PRCTL_H 50 | 51 | /* Define to 1 if you have the `putenv' function. */ 52 | #undef HAVE_PUTENV 53 | 54 | /* Define to 1 if you have the `setenv' function. */ 55 | #undef HAVE_SETENV 56 | 57 | /* Define to 1 if you have the `socket' function. */ 58 | #undef HAVE_SOCKET 59 | 60 | /* Define to 1 if you have the `socketpair' function. */ 61 | #undef HAVE_SOCKETPAIR 62 | 63 | /* Define to 1 if you have the header file. */ 64 | #undef HAVE_STDINT_H 65 | 66 | /* Define to 1 if you have the header file. */ 67 | #undef HAVE_STDLIB_H 68 | 69 | /* Define to 1 if you have the header file. */ 70 | #undef HAVE_STRINGS_H 71 | 72 | /* Define to 1 if you have the header file. */ 73 | #undef HAVE_STRING_H 74 | 75 | /* Define if you have the __sync_fetch_and_add function */ 76 | #undef HAVE_SYNC_FETCH_AND_ADD 77 | 78 | /* Define to 1 if you have the header file. */ 79 | #undef HAVE_SYS_CAPABILITY_H 80 | 81 | /* Define to 1 if you have the header file. */ 82 | #undef HAVE_SYS_EPOLL_H 83 | 84 | /* Define to 1 if you have the header file. */ 85 | #undef HAVE_SYS_EVENTFD_H 86 | 87 | /* Define to 1 if you have the header file. */ 88 | #undef HAVE_SYS_PRCTL_H 89 | 90 | /* Define to 1 if you have the header file. */ 91 | #undef HAVE_SYS_STAT_H 92 | 93 | /* Define to 1 if you have the header file. */ 94 | #undef HAVE_SYS_TYPES_H 95 | 96 | /* OpenSSL 0.9.7 or later */ 97 | #undef HAVE_TLSV1_SERVER_METHOD 98 | 99 | /* Define to 1 if you have the header file. */ 100 | #undef HAVE_UNISTD_H 101 | 102 | /* OpenSSL 0.9.7 or later */ 103 | #undef HAVE_X509_FREE 104 | 105 | /* Define to 1 if you have the header file. */ 106 | #undef HAVE_ZLIB_H 107 | 108 | /* Define to the address where bug reports for this package should be sent. */ 109 | #undef PACKAGE_BUGREPORT 110 | 111 | /* Define to the full name of this package. */ 112 | #undef PACKAGE_NAME 113 | 114 | /* Define to the full name and version of this package. */ 115 | #undef PACKAGE_STRING 116 | 117 | /* Define to the one symbol short name of this package. */ 118 | #undef PACKAGE_TARNAME 119 | 120 | /* Define to the home page for this package. */ 121 | #undef PACKAGE_URL 122 | 123 | /* Define to the version of this package. */ 124 | #undef PACKAGE_VERSION 125 | 126 | /* Define to 1 if you have the ANSI C header files. */ 127 | #undef STDC_HEADERS 128 | -------------------------------------------------------------------------------- /src/accept.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef ACCEPT_H 12 | #define ACCEPT_H 13 | 14 | #include "config.h" 15 | #include "cJSON.h" 16 | 17 | /* 18 | * The listen_t structure holds data for a currently open 19 | * listener. It's allocated when a listener is created 20 | * based on the configuration (listen_config_t). 21 | */ 22 | 23 | struct listen_t { 24 | struct listen_t *next; 25 | struct listen_t **prevp; 26 | 27 | int id; /* random id */ 28 | int listener_id; /* hash of protocol and local bound address */ 29 | int fd; 30 | int client_flags; 31 | int portnum; 32 | int clients_max; 33 | int corepeer; 34 | int hidden; 35 | int ai_protocol; 36 | 37 | struct client_udp_t *udp; 38 | struct portaccount_t *portaccount; 39 | struct acl_t *acl; 40 | #ifdef USE_SSL 41 | struct ssl_t *ssl; 42 | #endif 43 | 44 | char *name; 45 | char *addr_s; 46 | char *filters[LISTEN_MAX_FILTERS]; // up to 10 filter definitions 47 | char *filter_s; 48 | }; 49 | 50 | 51 | extern int accept_reconfiguring; 52 | extern int accept_shutting_down; 53 | 54 | extern struct worker_t *udp_worker; 55 | 56 | extern void accept_thread(void *asdf); 57 | 58 | extern int accept_listener_status(cJSON *listeners, cJSON *totals); 59 | 60 | extern int connections_accepted; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/acl.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ACL_H 3 | #define ACL_H 4 | 5 | #include 6 | 7 | struct acl_e4_t { 8 | uint32_t addr; 9 | uint32_t mask; 10 | int allow; 11 | struct acl_e4_t *next; 12 | }; 13 | 14 | struct acl_e6_t { 15 | uint32_t addr[4]; 16 | uint32_t mask[4]; 17 | int prefixlen; 18 | int allow; 19 | struct acl_e6_t *next; 20 | }; 21 | 22 | struct acl_t { 23 | struct acl_e4_t *entries4; 24 | struct acl_e6_t *entries6; 25 | }; 26 | 27 | extern struct acl_t *acl_new(void); 28 | extern void acl_free(struct acl_t *acl); 29 | extern struct acl_t *acl_dup(struct acl_t *acl); 30 | 31 | extern int acl_add(struct acl_t *acl, char *netspec, int allow); 32 | 33 | extern struct acl_t *acl_load(char *s); 34 | 35 | extern int acl_check(struct acl_t *acl, struct sockaddr *sa, int addr_len); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/aclocal.m4: -------------------------------------------------------------------------------- 1 | dnl aclocal.m4 generated automatically by aclocal 1.4-p6 2 | 3 | dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. 4 | dnl This file is free software; the Free Software Foundation 5 | dnl gives unlimited permission to copy and/or distribute it, 6 | dnl with or without modifications, as long as this notice is preserved. 7 | 8 | dnl This program is distributed in the hope that it will be useful, 9 | dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without 10 | dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | dnl PARTICULAR PURPOSE. 12 | 13 | # =========================================================================== 14 | # http://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html 15 | # =========================================================================== 16 | # 17 | # SYNOPSIS 18 | # 19 | # AX_CHECK_GNU_MAKE() 20 | # 21 | # DESCRIPTION 22 | # 23 | # This macro searches for a GNU version of make. If a match is found, the 24 | # makefile variable `ifGNUmake' is set to the empty string, otherwise it 25 | # is set to "#". This is useful for including a special features in a 26 | # Makefile, which cannot be handled by other versions of make. The 27 | # variable _cv_gnu_make_command is set to the command to invoke GNU make 28 | # if it exists, the empty string otherwise. 29 | # 30 | # Here is an example of its use: 31 | # 32 | # Makefile.in might contain: 33 | # 34 | # # A failsafe way of putting a dependency rule into a makefile 35 | # $(DEPEND): 36 | # $(CC) -MM $(srcdir)/*.c > $(DEPEND) 37 | # 38 | # @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND))) 39 | # @ifGNUmake@ include $(DEPEND) 40 | # @ifGNUmake@ endif 41 | # 42 | # Then configure.in would normally contain: 43 | # 44 | # AX_CHECK_GNU_MAKE() 45 | # AC_OUTPUT(Makefile) 46 | # 47 | # Then perhaps to cause gnu make to override any other make, we could do 48 | # something like this (note that GNU make always looks for GNUmakefile 49 | # first): 50 | # 51 | # if ! test x$_cv_gnu_make_command = x ; then 52 | # mv Makefile GNUmakefile 53 | # echo .DEFAULT: > Makefile ; 54 | # echo \ $_cv_gnu_make_command \$@ >> Makefile; 55 | # fi 56 | # 57 | # Then, if any (well almost any) other make is called, and GNU make also 58 | # exists, then the other make wraps the GNU make. 59 | # 60 | # LICENSE 61 | # 62 | # Copyright (c) 2008 John Darrington 63 | # 64 | # Copying and distribution of this file, with or without modification, are 65 | # permitted in any medium without royalty provided the copyright notice 66 | # and this notice are preserved. This file is offered as-is, without any 67 | # warranty. 68 | 69 | #serial 7 70 | 71 | AC_DEFUN([AX_CHECK_GNU_MAKE], [ AC_CACHE_CHECK( for GNU make,_cv_gnu_make_command, 72 | _cv_gnu_make_command='' ; 73 | dnl Search all the common names for GNU make 74 | for a in "$MAKE" make gmake gnumake ; do 75 | if test -z "$a" ; then continue ; fi ; 76 | if ( sh -c "$a --version" 2> /dev/null | grep GNU 2>&1 > /dev/null ) ; then 77 | _cv_gnu_make_command=$a ; 78 | break; 79 | fi 80 | done ; 81 | ) ; 82 | dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise 83 | if test "x$_cv_gnu_make_command" != "x" ; then 84 | ifGNUmake='' ; 85 | else 86 | ifGNUmake='#' ; 87 | AC_MSG_RESULT("Not found"); 88 | fi 89 | AC_SUBST(ifGNUmake) 90 | ] ) 91 | 92 | -------------------------------------------------------------------------------- /src/apparmor.aprsc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /opt/aprsc/sbin/aprsc { 4 | #include 5 | #include 6 | 7 | 8 | capability setgid, 9 | capability setuid, 10 | capability sys_chroot, 11 | capability sys_resource, 12 | 13 | /opt/aprsc/sbin/aprsc rpx, 14 | /sbin/aprsc rpx, 15 | /opt/aprsc/etc/* r, 16 | /opt/aprsc/web/* r, 17 | /opt/aprsc/web/ r, 18 | /opt/aprsc/logs/aprsc* rwk, 19 | owner /opt/aprsc/data/** rwk, 20 | /opt/aprsc/lib/** rm, 21 | /opt/aprsc/lib64/** rm, 22 | /opt/aprsc/usr/lib/** rm, 23 | /opt/aprsc/var/core/* rwk, 24 | 25 | /dev/urandom r, 26 | /opt/aprsc/dev/urandom r, 27 | } 28 | -------------------------------------------------------------------------------- /src/aprsc-prepare-chroot.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # copy files required for chrooted operation, use bind mounts to expose 4 | # libraries 5 | 6 | BASEDIR=/opt/aprsc 7 | DIRNAME=aprsc 8 | 9 | prepare_chroot () { 10 | # config files 11 | /bin/cp -p /etc/resolv.conf /etc/nsswitch.conf /etc/hosts /etc/gai.conf $BASEDIR/etc/ 12 | # live upgrade requires libraries to be visible within chroot, so 13 | # set up a read-only bind mount of /lib 14 | grep -q "$DIRNAME/lib " /proc/mounts || \ 15 | ( mount --bind /lib $BASEDIR/lib \ 16 | && mount -o remount,ro,bind $BASEDIR/lib ) 17 | if [ -e /lib64 ]; then 18 | grep -q "$DIRNAME/lib64 " /proc/mounts || \ 19 | ( mount --bind /lib64 $BASEDIR/lib64 \ 20 | && mount -o remount,ro,bind $BASEDIR/lib64 ) 21 | fi 22 | grep -q "$DIRNAME/usr/lib " /proc/mounts || \ 23 | ( mount --bind /usr/lib $BASEDIR/usr/lib \ 24 | && mount -o remount,ro,bind $BASEDIR/usr/lib ) 25 | if [ -e /usr/lib64 ]; then 26 | grep -q "$DIRNAME/usr/lib64 " /proc/mounts || \ 27 | ( mount --bind /usr/lib64 $BASEDIR/usr/lib64 \ 28 | && mount -o remount,ro,bind $BASEDIR/usr/lib64 ) 29 | fi 30 | } 31 | 32 | prepare_chroot 33 | 34 | -------------------------------------------------------------------------------- /src/aprsc.8.in: -------------------------------------------------------------------------------- 1 | .TH aprsc 8 "@DATEVERSION@" 2 | .LO 8 3 | .SH NAME 4 | .B aprsc 5 | \- An APRS-IS network server 6 | .SH SYNOPSIS 7 | .B aprsc 8 | .RB [ \-t " \fIchrootdir\fR]" 9 | .RB [ \-u " \fIsetuid_username\fR]" 10 | .RB [ \-c " \fIconfig_file_name\fR]" 11 | .RB [ \-f ] 12 | .RB [ \-n " \fIlogname\fR]" 13 | .RB [ \-r " \fIlog_dir\fR]" 14 | .RB [ \-e " \fIloglevel_token\fR]" 15 | .RB [ \-o " \fIlogdestination_token\fR]" 16 | .RB [ \-h ] 17 | .SH DESCRIPTION 18 | The 19 | .B aprsc 20 | (pronounced 21 | .IR a-purrs-c ) 22 | is a plain APRS-IS server intended to be used on the core and Tier2 APRS-IS servers. 23 | It is written in the C language, and it runs on Linux and Unix servers. 24 | .PP 25 | If you need igate or other radio-interfacing features, the aprsc is not for you. 26 | .PP 27 | .SH OPTIONS 28 | The 29 | .B aprsc 30 | has following runtime options: 31 | .TP 32 | .BI "\-t " chrootdir 33 | Run the 34 | .B aprsc 35 | in a 36 | .IR chroot (2) 37 | subdirectory. 38 | Use also the \-u option below. 39 | .TP 40 | .BI "\-u " setuid_username 41 | When running the 42 | .B aprsc 43 | in 44 | .IR chroot (2) 45 | subdirectory, it should be running there with a non-privileged user account. 46 | This option defines that account, which must be found at the system account database. 47 | .TP 48 | .BI "\-c " config_file_name 49 | Define explicit configuration file name. 50 | Default is "aprsc.conf" in current directory. 51 | .TP 52 | .BI "\-f" 53 | .br 54 | .IR fork (2) 55 | the program ro tun in the background. 56 | .TP 57 | .BI "\-n " logname 58 | Define a name by which 59 | .I this 60 | instance of the 61 | .B aprsc 62 | is known in the log. 63 | Useful when you have multiple instances running. 64 | .TP 65 | .BI "\-r " log_dir 66 | For file based logging, do it all in given directory. 67 | There is no default log directory. 68 | .TP 69 | .BI "\-e " loglevel_token 70 | The supported tokens are following with their corresponding priority values 71 | .RI EMERG (0) , 72 | .RI ALERT (1) , 73 | .RI CRIT (2) , 74 | .RI ERROR (3) , 75 | .RI WARNING (4) , 76 | .RI NOTICE (5) , 77 | .RI INFO (6) , 78 | .RI DEBUG (7) . 79 | This option defines a logging cutoff where events with higher numeric value 80 | than the given cutoff are dropped internally. 81 | The default cutoff is INFO. 82 | .TP 83 | .BI "\-o " logdestination_token 84 | The supported tokens are following: 85 | .IR none , 86 | .IR stderr , 87 | .IR syslog ", and" 88 | .IR file . 89 | .PP 90 | .SH LOGGING 91 | The logging has multiple options: 92 | .TP 93 | .B none 94 | .br 95 | Disable logging completely 96 | .TP 97 | .B stderr 98 | .br 99 | Log to stderr of the running users console session 100 | Do not use this when runtime option 101 | .B "\-f" 102 | is being used. 103 | .TP 104 | .B syslog 105 | .br 106 | Use 107 | .IR syslog (3) 108 | mechanism to send logging events to system 109 | .IR syslogd (8), 110 | When running under 111 | .IR chroot (2) 112 | this may require that the syslogd is configured to have log event receiving 113 | named socket within that chroot area. 114 | .TP 115 | .B file 116 | .br 117 | The logging goes to configured directory using file names: 118 | .IR aprsc.log ", and" 119 | .IR aprsc.access.log . 120 | which autorotate with up to N older versions, 121 | each with M megabytes of size when the rotation happens. 122 | Configuration of N and M is done with 123 | .IR aprsc.conf (7). 124 | .PP 125 | .SH SEE ALSO 126 | To be written.. 127 | .PP 128 | .I https://groups.google.com/forum/#!forum/aprsc 129 | .PP 130 | .SH AUTHORS 131 | Matti Aarnio OH2MQK (man page), 132 | Heikki Hannikainen OH7LZB (main software), 133 | Javier Henderson K4JH (lots of feedback). 134 | -------------------------------------------------------------------------------- /src/cellmalloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Matti Aarnio, OH2MQK, 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef _FOR_VALGRIND_ /* This does not mix with valgrind... */ 12 | #ifndef _CELLMALLOC_H_ 13 | #define _CELLMALLOC_H_ 14 | 15 | /* 16 | * cellmalloc() -- manages arrays of cells of data 17 | * 18 | */ 19 | 20 | struct cellstatus_t { 21 | int cellsize; 22 | int alignment; 23 | int cellsize_aligned; 24 | int cellcount; 25 | int freecount; 26 | int blocks; 27 | int blocks_max; 28 | int block_size; 29 | }; 30 | 31 | typedef struct cellarena_t cellarena_t; 32 | 33 | extern cellarena_t *cellinit(const char *arenaname, const int cellsize, const int alignment, const int policy, const int createkb, const int minfree); 34 | 35 | #define CELLMALLOC_POLICY_FIFO 0 36 | #define CELLMALLOC_POLICY_LIFO 1 37 | #define CELLMALLOC_POLICY_NOMUTEX 2 38 | 39 | extern void *cellmalloc(cellarena_t *cellarena); 40 | extern int cellmallocmany(cellarena_t *cellarena, void **array, const int numcells); 41 | extern void cellfree(cellarena_t *cellarena, void *p); 42 | extern void cellfreemany(cellarena_t *cellarena, void **array, const int numcells); 43 | extern void cellstatus(cellarena_t *cellarena, struct cellstatus_t *status); 44 | 45 | #endif 46 | #else /* _FOR_VALGRIND_ .. normal malloc/free is better */ 47 | 48 | #include "hmalloc.h" 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/cfgfile.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Tomi Manninen 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef CFGFILE_H 12 | #define CFGFILE_H 13 | 14 | #define CFGLINE_LEN 102400 15 | 16 | struct cfgcmd { 17 | char *name; 18 | int (*function) (void *dest, int argc, char **argv); 19 | void *dest; 20 | }; 21 | 22 | extern int parse_args(char *argv[], char *cmd); 23 | extern char *argstr(int arg, int argc, char **argv); 24 | 25 | extern int read_cfgfile(char *f, struct cfgcmd *cmds); 26 | 27 | extern int do_string(char **dest, int argc, char **argv); 28 | extern int do_string_array(char ***dest, int argc, char **argv); 29 | extern void free_string_array(char **dest); 30 | extern int do_char(char *dest, int argc, char **argv); 31 | extern int do_int(int *dest, int argc, char **argv); 32 | extern int do_boolean(int *dest, int argc, char **argv); 33 | 34 | extern long long hatoll(char *s); 35 | extern char *strlwr(char *s); 36 | 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /src/client_heard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef CLIENT_HEARD_H 12 | #define CLIENT_HEARD_H 13 | 14 | #include "worker.h" 15 | #include "cellmalloc.h" 16 | #include "cJSON.h" 17 | 18 | extern void client_heard_update(struct client_t *c, struct pbuf_t *pb); 19 | extern void client_courtesy_update(struct client_t *c, struct pbuf_t *pb); 20 | extern int client_heard_check(struct client_t *c, const char *callsign, int call_len, uint32_t hash); 21 | extern int client_courtesy_needed(struct client_t *c, struct pbuf_t *pb); 22 | 23 | extern void client_heard_expire(struct client_t *c); 24 | extern void client_heard_free(struct client_t *c); 25 | extern void client_heard_init(void); 26 | 27 | extern struct cJSON *client_heard_json(struct client_heard_t **list); 28 | extern int client_heard_json_load(struct client_t *c, cJSON *dump); 29 | 30 | #ifndef _FOR_VALGRIND_ 31 | extern void client_heard_cell_stats(struct cellstatus_t *cellst); 32 | #endif 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /src/clientlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | #ifndef CLIENTLIST_H 11 | #define CLIENTLIST_H 12 | 13 | #include "worker.h" 14 | 15 | extern int clientlist_add(struct client_t *c); 16 | extern void clientlist_remove(struct client_t *c); 17 | 18 | extern int clientlist_check_if_validated_client(char *username, int len); 19 | 20 | #endif 21 | 22 | -------------------------------------------------------------------------------- /src/counterdata.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef COUNTERDATA_H 12 | #define COUNTERDATA_H 13 | 14 | /* store 48 hours, 1 sample per minute */ 15 | #define CDATA_SAMPLES 48*60 16 | #define CDATA_INTERVAL 60 17 | 18 | struct cdata_t; 19 | 20 | extern struct cdata_t *cdata_alloc(const char *name); 21 | extern void cdata_free(struct cdata_t *cd); 22 | extern void cdata_counter_sample(struct cdata_t *cd, long long value); 23 | extern void cdata_gauge_sample(struct cdata_t *cd, long long value); 24 | extern long cdata_get_last_value(const char *name); 25 | extern char *cdata_json_string(const char *name); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/debian/.gitignore: -------------------------------------------------------------------------------- 1 | files 2 | changelog 3 | aprsc.debhelper.log 4 | aprsc.postinst.debhelper 5 | aprsc.postrm.debhelper 6 | aprsc.prerm.debhelper 7 | aprsc.substvars 8 | -------------------------------------------------------------------------------- /src/debian/aprsc-chroot.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=APRS-IS server chroot environment 3 | After=network.target 4 | Documentation=http://he.fi/aprsc/, man:aprsc(8) 5 | 6 | [Service] 7 | Type=oneshot 8 | ExecStart=/opt/aprsc/sbin/aprsc-prepare-chroot.sh 9 | User=root 10 | Group=root 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | Alias=aprsc-chroot.service 15 | -------------------------------------------------------------------------------- /src/debian/aprsc.default: -------------------------------------------------------------------------------- 1 | # 2 | # STARTAPRSC: start aprsc on boot. Should be set to "yes" once you have 3 | # configured aprsc. 4 | # 5 | STARTAPRSC="no" 6 | 7 | # 8 | # Additional options that are passed to the Daemon. 9 | # Description of used options (don't change these unless 10 | # you're sure what you're doing): 11 | # -u aprsc: switch to user 'aprsc' as soon as possible 12 | # -t /opt/aprsc: chroot to the given directory 13 | # -f: fork to a daemon 14 | # -e info: log at level info 15 | # -o file: log to file 16 | # -r logs: log files are placed in /opt/aprsc/logs 17 | # -c etc/aprsc.conf: configuration file location 18 | # 19 | # Since the daemon chroots to /opt/aprsc, all paths are relative to 20 | # that directory and the daemon cannot access any files outside 21 | # the chroot. 22 | # 23 | # aprsc can log to syslog too, but that'd require bringing the 24 | # syslog socket within the chroot. 25 | # 26 | 27 | DAEMON_OPTS="-u aprsc -t /opt/aprsc -f -e info -o file -r logs -c etc/aprsc.conf" 28 | -------------------------------------------------------------------------------- /src/debian/aprsc.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=APRS-IS server 3 | After=network-online.target aprsc-chroot.service 4 | Wants=network-online.target 5 | Requires=aprsc-chroot.service 6 | Documentation=http://he.fi/aprsc/, man:aprsc(8) 7 | 8 | [Service] 9 | Type=simple 10 | ExecStart=/opt/aprsc/sbin/aprsc -u aprsc -t /opt/aprsc -e info -o file -r logs -c etc/aprsc.conf 11 | ExecReload=/bin/kill -USR1 $MAINPID 12 | PIDFile=/opt/aprsc/logs/aprsc.pid 13 | TimeoutStopSec=5 14 | RestartSec=5 15 | Restart=always 16 | User=root 17 | Group=root 18 | LimitNOFILE=65535 19 | #PrivateDevices=yes 20 | #ProtectHome=yes 21 | #ReadOnlyDirectories=/ 22 | #ReadWriteDirectories=-/var/lib/aprsc 23 | 24 | #NoNewPrivileges=true 25 | #CapabilityBoundingSet=CAP_SETGID CAP_SETUID CAP_SYS_RESOURCE 26 | MemoryDenyWriteExecute=true 27 | ProtectKernelModules=true 28 | ProtectKernelTunables=true 29 | ProtectControlGroups=true 30 | RestrictRealtime=true 31 | RestrictNamespaces=true 32 | RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX 33 | ProtectSystem=true 34 | #ReadWriteDirectories=-/etc/aprsc 35 | 36 | [Install] 37 | WantedBy=multi-user.target 38 | Alias=aprsc.service 39 | -------------------------------------------------------------------------------- /src/debian/aprsc@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description="APRS-IS server #%i" 3 | After=network-online.target aprsc-chroot.service 4 | Wants=network-online.target 5 | Requires=aprsc-chroot.service 6 | Documentation=http://he.fi/aprsc/, man:aprsc(8) 7 | 8 | [Service] 9 | Type=simple 10 | ExecStart=/opt/aprsc/sbin/aprsc -n aprsc-%i -u aprsc -t /opt/aprsc -e info -o file -r logs -c etc/aprsc-%i.conf 11 | ExecReload=/bin/kill -USR1 $MAINPID 12 | PIDFile=/opt/aprsc/logs/aprsc-%i.pid 13 | TimeoutStopSec=5 14 | RestartSec=5 15 | Restart=always 16 | User=root 17 | Group=root 18 | LimitNOFILE=65535 19 | #PrivateDevices=yes 20 | #ProtectHome=yes 21 | #ReadOnlyDirectories=/ 22 | #ReadWriteDirectories=-/var/lib/aprsc 23 | 24 | #NoNewPrivileges=true 25 | #CapabilityBoundingSet=CAP_SETGID CAP_SETUID CAP_SYS_RESOURCE 26 | MemoryDenyWriteExecute=true 27 | ProtectKernelModules=true 28 | ProtectKernelTunables=true 29 | ProtectControlGroups=true 30 | RestrictRealtime=true 31 | RestrictNamespaces=true 32 | RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX 33 | ProtectSystem=true 34 | #ReadWriteDirectories=-/etc/aprsc 35 | 36 | [Install] 37 | WantedBy=multi-user.target 38 | -------------------------------------------------------------------------------- /src/debian/changelog.release: -------------------------------------------------------------------------------- 1 | aprsc (@VERSION@) stable; urgency=low 2 | 3 | * See main ChangeLog. 4 | 5 | -- aprsc maintainer @RFCDATE@ 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/debian/compat: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /src/debian/conffiles: -------------------------------------------------------------------------------- 1 | /opt/aprsc/etc/aprsc.conf 2 | -------------------------------------------------------------------------------- /src/debian/control: -------------------------------------------------------------------------------- 1 | Source: aprsc 2 | Section: hamradio 3 | Priority: extra 4 | Maintainer: Heikki Hannikainen 5 | Build-Depends: debhelper (>= 4), fakeroot (>= 1), lsb-release (>= 3), lsb-base (>= 3), libevent-dev, libssl-dev, libcap-dev, libz-dev, libsctp-dev 6 | Standards-Version: 3.7.2 7 | 8 | Package: aprsc 9 | Architecture: any 10 | Depends: ${shlibs:Depends}, adduser (>= 2), libwww-perl (>= 5), libjson-xs-perl (>= 2), libcap2-bin (>= 2), libsctp1 (>= 1) 11 | Description: APRS-IS server 12 | aprsc is an APRS-IS server intended to be used on core and Tier2 servers. 13 | . 14 | This software requires a valid (and unique) ham radio callsign to 15 | operate fully and is therefore useful mainly for licensed radio 16 | amateurs. 17 | 18 | -------------------------------------------------------------------------------- /src/debian/copyright: -------------------------------------------------------------------------------- 1 | See LICENSE. 2 | -------------------------------------------------------------------------------- /src/debian/dirs: -------------------------------------------------------------------------------- 1 | opt/ 2 | opt/aprsc/ 3 | opt/aprsc/etc 4 | opt/aprsc/sbin 5 | opt/aprsc/data 6 | opt/aprsc/web 7 | opt/aprsc/logs 8 | opt/aprsc/lib 9 | opt/aprsc/lib64 10 | opt/aprsc/usr 11 | opt/aprsc/usr/lib 12 | opt/aprsc/usr/lib64 13 | opt/aprsc/dev 14 | etc/apparmor.d 15 | -------------------------------------------------------------------------------- /src/debian/docs: -------------------------------------------------------------------------------- 1 | LICENSE 2 | aprsc.conf 3 | -------------------------------------------------------------------------------- /src/debian/links: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/debian/links -------------------------------------------------------------------------------- /src/debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | action="$1" 4 | oldversion="$2" 5 | 6 | . /usr/share/debconf/confmodule 7 | db_version 2.0 8 | 9 | umask 022 10 | 11 | if [ "$action" != configure ] 12 | then 13 | exit 0 14 | fi 15 | 16 | # functions 17 | 18 | setup_aprsc_user() { 19 | if ! getent passwd aprsc >/dev/null; then 20 | echo "Creating user account: 'aprsc'" 21 | adduser --quiet --system --no-create-home --home /var/run/aprsc --shell /usr/sbin/nologin --group aprsc 22 | fi 23 | } 24 | 25 | fix_permissions() { 26 | chown aprsc:aprsc /opt/aprsc/logs /opt/aprsc/data 27 | setcap cap_net_bind_service=+eip /opt/aprsc/sbin/aprsc || true 28 | } 29 | 30 | setup_chroot_devices() { 31 | ( cd /opt/aprsc/dev && cp -a /dev/urandom /dev/random /dev/null /dev/zero . ) 32 | } 33 | 34 | apparmor_config() { 35 | # Reload AppArmor profile 36 | APP_PROFILE="/etc/apparmor.d/opt.aprsc.sbin.aprsc" 37 | if [ -f "$APP_PROFILE" ] && aa-status --enabled 2>/dev/null; then 38 | echo "Installing apparmor profile..." 39 | apparmor_parser -r -T -W "$APP_PROFILE" || true 40 | fi 41 | } 42 | 43 | munin_config() { 44 | if [ -d "/etc/munin/plugins" ]; then 45 | echo "Setting up munin plugin..." 46 | ( cd /etc/munin/plugins && /opt/aprsc/sbin/aprsc_munin makelinks ) 47 | fi 48 | } 49 | 50 | # main 51 | 52 | setup_aprsc_user 53 | fix_permissions 54 | setup_chroot_devices 55 | apparmor_config 56 | munin_config 57 | 58 | # Finally, do a start or restart 59 | if [ -x "/etc/init.d/aprsc" ]; then 60 | # set up links 61 | update-rc.d aprsc defaults >/dev/null 62 | # start or upgrade 63 | if [ -n "$2" ]; then 64 | # Upgrading. If previous version is new enough, perform 65 | # a live upgrade. 66 | if dpkg --compare-versions "$2" gt "1.6.4"; then 67 | _dh_action=liveupgrade 68 | else 69 | _dh_action=restart 70 | fi 71 | else 72 | _dh_action=start 73 | fi 74 | if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then 75 | invoke-rc.d aprsc $_dh_action || exit $? 76 | else 77 | /etc/init.d/aprsc $_dh_action || exit $? 78 | fi 79 | fi 80 | 81 | exit 0 82 | 83 | -------------------------------------------------------------------------------- /src/debian/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # postrm script for aprsc 3 | 4 | # summary of how this script can be called: 5 | # * `remove' 6 | # * `purge' 7 | # * `upgrade' 8 | # * `failed-upgrade' 9 | # * `abort-install' 10 | # * `abort-install' 11 | # * `abort-upgrade' 12 | # * `disappear' overwrit>r> 13 | # for details, see http://www.debian.org/doc/debian-policy/ or 14 | # the debian-policy package 15 | 16 | case "$1" in 17 | 18 | purge) 19 | rm -rf /opt/aprsc/logs 20 | rm -rf /opt/aprsc/data 21 | deluser aprsc 22 | 23 | esac 24 | 25 | # dh_installdeb will replace this with shell code automatically 26 | # generated by other debhelper scripts. 27 | 28 | #DEBHELPER# 29 | 30 | exit 0 31 | -------------------------------------------------------------------------------- /src/debian/preinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | action="$1" 4 | 5 | . /usr/share/debconf/confmodule 6 | db_version 2.0 7 | 8 | umask 022 9 | 10 | # remove the old symlink if it exists 11 | if [ -h /opt/aprsc/lib64 ]; then 12 | rm /opt/aprsc/lib64 13 | fi 14 | 15 | exit 0 16 | 17 | -------------------------------------------------------------------------------- /src/debian/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | action="$1" 5 | version="$2" 6 | 7 | if [ "$action" = "upgrade" ]; then 8 | exit 0 9 | fi 10 | 11 | # shut it down 12 | if [ -x "/etc/init.d/aprsc" ]; then 13 | if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then 14 | invoke-rc.d aprsc stop || exit $? 15 | else 16 | /etc/init.d/aprsc stop || exit $? 17 | fi 18 | fi 19 | 20 | # delete device files from chroot 21 | rm -f /opt/aprsc/dev/* 22 | 23 | # unmount bind mounts 24 | umount /opt/aprsc/lib 2>/dev/null || true 25 | umount /opt/aprsc/lib64 2>/dev/null || true 26 | umount /opt/aprsc/usr/lib 2>/dev/null || true 27 | umount /opt/aprsc/usr/lib64 2>/dev/null || true 28 | -------------------------------------------------------------------------------- /src/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # Uncomment this to turn on verbose mode. 4 | #export DH_VERBOSE=1 5 | 6 | CFLAGS = -Wall -g 7 | 8 | ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) 9 | CFLAGS += -O0 10 | else 11 | CFLAGS += -O2 12 | endif 13 | 14 | configure: configure-stamp 15 | configure-stamp: 16 | dh_testdir 17 | # Add here commands to configure the package. 18 | ./configure --sbindir=/usr/sbin --sysconfdir=/etc \ 19 | --localstatedir=/var --mandir=/usr/share/man \ 20 | CC="gcc" \ 21 | CFLAGS="${CFLAGS}" \ 22 | AFLAGS="${CFLAGS} --noexecstack" \ 23 | LDFLAGS="${CFLAGS} -z noexecstack" 24 | touch configure-stamp 25 | 26 | 27 | build: build-stamp 28 | 29 | build-stamp: configure-stamp 30 | dh_testdir 31 | 32 | # Add here commands to compile the package. 33 | $(MAKE) -j3 34 | 35 | touch $@ 36 | 37 | clean: 38 | dh_testdir 39 | dh_testroot 40 | rm -f build-stamp configure-stamp 41 | 42 | # Add here commands to clean up after the build process. 43 | -$(MAKE) clean 44 | 45 | dh_clean 46 | 47 | install: build 48 | dh_testdir 49 | dh_testroot 50 | dh_prep 51 | dh_installdirs 52 | 53 | # Add here commands to install the package into debian/apsc. 54 | $(MAKE) DESTDIR=$(CURDIR)/debian/aprsc install 55 | install -m 644 apparmor.aprsc $(CURDIR)/debian/aprsc/etc/apparmor.d/opt.aprsc.sbin.aprsc 56 | 57 | # Build architecture-independent files here. 58 | binary-indep: build install 59 | # We have nothing to do by default. 60 | 61 | # Build architecture-dependent files here. 62 | binary-arch: build install 63 | dh_testdir 64 | dh_testroot 65 | # dh_installchangelogs ChangeLog 66 | dh_installdocs 67 | dh_installexamples 68 | # dh_install 69 | # dh_installmenu 70 | # dh_installdebconf 71 | dh_installlogrotate 72 | # dh_installpam 73 | # dh_installmime 74 | # dh_python 75 | dh_installsystemd 76 | dh_installsystemd --name=aprsc-chroot 77 | # dh_installcron 78 | # dh_installinfo 79 | dh_installman 80 | dh_link 81 | # dh_strip 82 | dh_compress 83 | dh_fixperms 84 | # dh_perl 85 | # dh_makeshlibs 86 | dh_installdeb 87 | # Since compat 12, dh_installdeb insists conffiles may only live in /etc. 88 | cat debian/conffiles >> $(CURDIR)/debian/aprsc/DEBIAN/conffiles 89 | dh_shlibdeps 90 | dh_gencontrol 91 | dh_md5sums 92 | dh_builddeb 93 | 94 | binary: binary-indep binary-arch 95 | .PHONY: build clean binary-indep binary-arch binary install configure 96 | -------------------------------------------------------------------------------- /src/dupecheck.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef DUPECHECK_H 12 | #define DUPECHECK_H 13 | 14 | #include "worker.h" 15 | #include "cellmalloc.h" 16 | 17 | struct dupe_record_t { 18 | struct dupe_record_t *next; 19 | uint32_t hash; 20 | time_t t; 21 | int dtype; // dupecheck dupe type 22 | int len; // address + payload length 23 | char *packet; 24 | char packetbuf[220]; /* 99.9+ % of time this is enough.. */ 25 | }; 26 | 27 | #define DUPECHECK_CELL_SIZE sizeof(struct dupe_record_t) 28 | 29 | #define DTYPE_SPACE_TRIM 1 30 | #define DTYPE_STRIP_8BIT 2 31 | #define DTYPE_CLEAR_8BIT 3 32 | #define DTYPE_SPACED_8BIT 4 33 | #define DTYPE_LOWDATA_STRIP 5 34 | #define DTYPE_LOWDATA_SPACED 6 35 | #define DTYPE_DEL_STRIP 7 36 | #define DTYPE_DEL_SPACED 8 37 | #define DTYPE_MAX 8 38 | 39 | extern long long dupecheck_outcount; /* statistics counter */ 40 | extern long long dupecheck_dupecount; /* statistics counter */ 41 | extern long long dupecheck_dupetypes[DTYPE_MAX+1]; 42 | extern long dupecheck_cellgauge; /* statistics gauge */ 43 | 44 | extern int dupecheck_eventfd; 45 | 46 | extern int outgoing_lag_report(struct worker_t *self, int*lag, int*dupelag); 47 | 48 | extern void dupecheck_init(void); 49 | extern void dupecheck_start(void); 50 | extern void dupecheck_stop(void); 51 | extern void dupecheck_atend(void); 52 | 53 | /* cellmalloc status */ 54 | #ifndef _FOR_VALGRIND_ 55 | extern void dupecheck_cell_stats(struct cellstatus_t *cellst); 56 | #endif 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/errno_aprsc.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * aprsc 4 | * 5 | * (c) Heikki Hannikainen, OH7LZB 6 | * 7 | * This program is licensed under the BSD license, which can be found 8 | * in the file LICENSE. 9 | * 10 | */ 11 | 12 | /* 13 | * Error codes and their descriptions 14 | */ 15 | 16 | #include 17 | 18 | #include "errno_aprsc.h" 19 | 20 | const char *aprsc_errs[] = { 21 | "aprsc success", 22 | "Unknown error", 23 | "All peers being closed", 24 | "aprsc thread shutdown", 25 | "Client fd number invalid", 26 | "EOF - remote end closed connection", 27 | "Output buffer full", 28 | "Output write timeout", 29 | "Uplink server protocol error", 30 | "Uplink server says we're not verified", 31 | "Client login retry count exceeded", 32 | "Client login timed out", 33 | "Inactivity timeout", 34 | "Uplink server certificate validation failed" 35 | }; 36 | 37 | const char *aprsc_strerror(int er) 38 | { 39 | if (er >= 0) 40 | return strerror(er); 41 | 42 | er *= -1; 43 | 44 | if (er > APRSC_ERRNO_MAX) 45 | er = 1; 46 | 47 | return aprsc_errs[er]; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/errno_aprsc.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef APRSC_ERRNO_H 3 | #define APRSC_ERRNO_H 4 | 5 | #define APRSC_ERRNO_MAX 13 6 | #define APRSC_UNKNOWN_ERROR -1 7 | #define CLIOK_PEERS_CLOSING -2 8 | #define CLIOK_THREAD_SHUTDOWN -3 9 | #define CLIERR_FD_NUM_INVALID -4 10 | #define CLIERR_EOF -5 11 | #define CLIERR_OUTPUT_BUFFER_FULL -6 12 | #define CLIERR_OUTPUT_WRITE_TIMEOUT -7 13 | #define CLIERR_UPLINK_LOGIN_PROTO_ERR -8 14 | #define CLIERR_UPLINK_LOGIN_NOT_VERIFIED -9 15 | #define CLIERR_LOGIN_RETRIES -10 16 | #define CLIERR_LOGIN_TIMEOUT -11 17 | #define CLIERR_INACTIVITY -12 18 | #define CLIERR_UPLINK_PEER_CERT_FAIL -13 19 | 20 | extern const char *aprsc_strerror(int er); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/filter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Matti Aarnio, OH2MQK, 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef FILTER_H 12 | #define FILTER_H 1 13 | 14 | #include "worker.h" 15 | #include "cellmalloc.h" 16 | 17 | extern void filter_init(void); 18 | extern int filter_parse(struct client_t *c, const char *filt, int is_user_filter); 19 | extern void filter_free(struct filter_t *c); 20 | extern int filter_process(struct worker_t *self, struct client_t *c, struct pbuf_t *pb); 21 | extern int filter_commands(struct worker_t *self, struct client_t *c, int in_message, const char *s, const int len); 22 | 23 | extern void filter_preprocess_dupefilter(struct pbuf_t *pb); 24 | extern void filter_postprocess_dupefilter(struct pbuf_t *pb); 25 | 26 | extern void filter_entrycall_cleanup(void); 27 | extern void filter_entrycall_atend(void); 28 | extern int filter_entrycall_cellgauge; 29 | extern void filter_entrycall_dump(FILE *fp); 30 | 31 | extern void filter_wx_cleanup(void); 32 | extern void filter_wx_atend(void); 33 | extern int filter_wx_cellgauge; 34 | extern void filter_wx_dump(FILE *fp); 35 | 36 | extern int filter_cellgauge; 37 | 38 | extern int have_filtered_listeners; 39 | 40 | extern float filter_lat2rad(float lat); 41 | extern float filter_lon2rad(float lon); 42 | 43 | #ifndef _FOR_VALGRIND_ 44 | extern void filter_cell_stats(struct cellstatus_t *filter_cellst, 45 | struct cellstatus_t *filter_entrycall_cellst, 46 | struct cellstatus_t *filter_wx_cellst); 47 | #endif 48 | 49 | #endif /* FILTER_H */ 50 | -------------------------------------------------------------------------------- /src/gai_strerror.c: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Matti Aarnio, OH2MQK, 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | /* 11 | Libc fill-in for ZMailer using IPv6 API 12 | by Matti Aarnio 1997, 2001 13 | 14 | The original Craig Metz code is deeply Linux specific, 15 | this adaptation tries to be way more generic.. 16 | */ 17 | 18 | #include "../config.h" 19 | #include 20 | #include 21 | #include 22 | #ifndef EAI_BADFLAGS 23 | # include "netdb6.h" 24 | #endif 25 | 26 | #ifndef __STDC__ 27 | # define const 28 | #endif 29 | 30 | #ifndef HAVE_GAI_STRERROR 31 | 32 | /* 33 | %%% copyright-cmetz-97 34 | This software is Copyright 1997 by Craig Metz, All Rights Reserved. 35 | The Inner Net License Version 2 applies to this software. 36 | You should have received a copy of the license with this software. If 37 | you didn't get a copy, you may request one from . 38 | 39 | */ 40 | 41 | const char *gai_strerror(errnum) 42 | int errnum; 43 | { 44 | static char buffer[24]; 45 | switch(errnum) { 46 | case 0: 47 | return "no error"; 48 | case EAI_BADFLAGS: 49 | return "invalid value for ai_flags"; 50 | case EAI_NONAME: 51 | return "name or service is not known"; 52 | case EAI_AGAIN: 53 | return "temporary failure in name resolution"; 54 | case EAI_FAIL: 55 | return "non-recoverable failure in name resolution"; 56 | case EAI_NODATA: 57 | return "no address associated with name"; 58 | case EAI_FAMILY: 59 | return "ai_family not supported"; 60 | case EAI_SOCKTYPE: 61 | return "ai_socktype not supported"; 62 | case EAI_SERVICE: 63 | return "service not supported for ai_socktype"; 64 | case EAI_ADDRFAMILY: 65 | return "address family for name not supported"; 66 | case EAI_MEMORY: 67 | return "memory allocation failure"; 68 | case EAI_SYSTEM: 69 | return "system error"; 70 | default: 71 | sprintf(buffer,"gai_error_%02x", errnum); 72 | return buffer; 73 | } 74 | } 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/gfx/aprsc-joulukissa-2013.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-joulukissa-2013.xcf -------------------------------------------------------------------------------- /src/gfx/aprsc-logo-pekka.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-logo-pekka.jpg -------------------------------------------------------------------------------- /src/gfx/aprsc-logo-shadow.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-logo-shadow.xcf -------------------------------------------------------------------------------- /src/gfx/aprsc-logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-logo.xcf -------------------------------------------------------------------------------- /src/gfx/aprsc-logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-logo1.png -------------------------------------------------------------------------------- /src/gfx/aprsc-logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-logo2.png -------------------------------------------------------------------------------- /src/gfx/aprsc-logo2.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-logo2.xcf -------------------------------------------------------------------------------- /src/gfx/aprsc-logo3.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-logo3.xcf -------------------------------------------------------------------------------- /src/gfx/aprsc-logo4.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-logo4.xcf -------------------------------------------------------------------------------- /src/gfx/aprsc-minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-minus.png -------------------------------------------------------------------------------- /src/gfx/aprsc-plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/gfx/aprsc-plus.png -------------------------------------------------------------------------------- /src/historydb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Matti Aarnio, OH2MQK, 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | 12 | 13 | /* 14 | * The historydb contains positional packet data in form of: 15 | * - position packet 16 | * - objects 17 | * - items 18 | * Keying varies, origination callsign of positions, name 19 | * for object/item. 20 | * 21 | * Uses RW-locking, W for inserts/cleanups, R for lookups. 22 | * 23 | * Inserting does incidential cleanup scanning while traversing 24 | * hash chains. 25 | * 26 | * In APRS-IS there are about 25 000 distinct callsigns or 27 | * item or object names with position information PER WEEK. 28 | * DB lifetime of 48 hours cuts that down a bit more. 29 | * Memory usage is around 3-4 MB 30 | */ 31 | 32 | #ifndef __HISTORYDB_H__ 33 | #define __HISTORYDB_H__ 34 | 35 | #include "cellmalloc.h" 36 | 37 | struct history_cell_t { 38 | struct history_cell_t *next; 39 | 40 | time_t arrivaltime; 41 | int keylen; 42 | char key[CALLSIGNLEN_MAX+2]; 43 | uint32_t hash1; 44 | 45 | float lat, coslat, lon; 46 | 47 | int packettype; 48 | int flags; 49 | }; 50 | 51 | #define HISTORYDB_CELL_SIZE sizeof(struct history_cell_t) 52 | 53 | extern long historydb_inserts; 54 | extern long historydb_lookups; 55 | extern long historydb_hashmatches; 56 | extern long historydb_keymatches; 57 | extern long historydb_cellgauge; 58 | extern long historydb_noposcount; 59 | extern long historydb_cleanup_cleaned; 60 | 61 | extern void historydb_init(void); 62 | 63 | extern int historydb_dump(FILE *fp); 64 | extern int historydb_load(FILE *fp); 65 | 66 | extern void historydb_cleanup(void); 67 | extern void historydb_atend(void); 68 | 69 | /* insert and lookup... interface yet unspecified */ 70 | extern int historydb_insert(struct pbuf_t*); 71 | extern int historydb_lookup(const char *keybuf, const int keylen, struct history_cell_t **result); 72 | 73 | /* cellmalloc status */ 74 | #ifndef _FOR_VALGRIND_ 75 | extern void historydb_cell_stats(struct cellstatus_t *cellst); 76 | #endif 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/hlog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef LOG_H 12 | #define LOG_H 13 | 14 | #define LOG_LEN 2048 15 | 16 | #define L_STDERR 1 /* Log to stderror */ 17 | #define L_SYSLOG (1 << 1) /* Log to syslog */ 18 | #define L_FILE (1 << 2) /* Log to a file */ 19 | 20 | #ifdef __CYGWIN__ 21 | #define L_DEFDEST L_FILE 22 | #else 23 | #define L_DEFDEST L_STDERR 24 | #endif 25 | 26 | #define LOG_LEVELS "emerg alert crit err warning notice info debug" 27 | #define LOG_DESTS "syslog stderr file" 28 | 29 | #include 30 | 31 | extern char *log_levelnames[]; 32 | extern char *log_destnames[]; 33 | 34 | extern int log_dest; /* Logging destination */ 35 | extern int log_level; /* Logging level */ 36 | extern char *log_dir; /* Log directory */ 37 | 38 | extern int log_rotate_size; /* Rotate log when it reaches a given size */ 39 | extern int log_rotate_num; /* How many logs to keep around */ 40 | 41 | 42 | extern char *str_append(char *s, const char *fmt, ...); 43 | 44 | extern int pick_loglevel(char *s, char **names); 45 | extern int open_log(char *name, int reopen); 46 | extern int close_log(int reopen); 47 | extern int hlog(int priority, const char *fmt, ...); 48 | extern int hlog_packet(int priority, const char *packet, int packetlen, const char *fmt, ...); 49 | 50 | extern int accesslog_open(char *logd, int reopen); 51 | extern int accesslog_close(char *reopenpath); 52 | extern int accesslog(const char *fmt, ...); 53 | 54 | extern int writepid(char *name); 55 | extern int closepid(void); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/hmalloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | /* 12 | * Replacements for malloc, realloc and free, which never fail, 13 | * and might keep statistics on memory allocation... 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "hmalloc.h" 22 | 23 | int mem_panic = 0; 24 | 25 | void *hmalloc(size_t size) 26 | { 27 | void *p; 28 | 29 | if (!(p = malloc(size))) { 30 | if (mem_panic) 31 | exit(1); /* To prevent a deadlock */ 32 | mem_panic = 1; 33 | fprintf(stderr, "hmalloc: Out of memory! Could not allocate %d bytes.", (int)size); 34 | exit(1); 35 | } 36 | 37 | return p; 38 | } 39 | 40 | void *hrealloc(void *ptr, size_t size) 41 | { 42 | void *p; 43 | 44 | if (!(p = realloc(ptr, size))) { 45 | if (mem_panic) 46 | exit(1); 47 | mem_panic = 1; 48 | fprintf(stderr, "hrealloc: Out of memory! Could not reallocate %d bytes.", (int)size); 49 | exit(1); 50 | } 51 | 52 | return p; 53 | } 54 | 55 | void hfree(void *ptr) 56 | { 57 | if (ptr) 58 | free(ptr); 59 | } 60 | 61 | char *hstrdup(const char *s) 62 | { 63 | char *p; 64 | 65 | p = hmalloc(strlen(s)+1); 66 | strcpy(p, s); 67 | 68 | return p; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/hmalloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef HMALLOC_H 12 | #define HMALLOC_H 13 | 14 | #include 15 | #include 16 | 17 | /* 18 | * Replacements for malloc, realloc and free, which never fail, 19 | * and might keep statistics on memory allocation... 20 | */ 21 | 22 | extern void *hmalloc(size_t size); 23 | extern void *hrealloc(void *ptr, size_t size); 24 | extern void hfree(void *ptr); 25 | 26 | extern char *hstrdup(const char *s); 27 | 28 | #endif 29 | 30 | -------------------------------------------------------------------------------- /src/http.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef HTTP_H 12 | #define HTTP_H 13 | 14 | #include "worker.h" 15 | 16 | extern struct worker_t *http_worker; 17 | 18 | extern int http_reconfiguring; 19 | extern int http_shutting_down; 20 | 21 | extern int loginpost_split(char *post, int len, char **login_string, char **packet); 22 | extern int pseudoclient_push_packet(struct worker_t *worker, struct client_t *pseudoclient, const char *username, char *packet, int packet_len); 23 | 24 | extern void http_thread(void *asdf); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/incoming.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef INCOMING_H 12 | #define INCOMING_H 13 | 14 | #include "worker.h" 15 | #include "cellmalloc.h" 16 | 17 | extern const char *inerr_labels[]; 18 | 19 | extern int check_invalid_src_dst(const char *call, int len); 20 | extern int check_call_match(const char **set, const char *call, int len); 21 | extern int check_call_glob_match(char **set, const char *call, int len); 22 | extern int check_path_calls(const char *via_start, const char *path_end); 23 | 24 | extern void incoming_flush(struct worker_t *self); 25 | extern int incoming_handler(struct worker_t *self, struct client_t *c, int l4proto, char *s, int len); 26 | extern int incoming_parse(struct worker_t *self, struct client_t *c, char *s, int len); 27 | 28 | #ifndef _FOR_VALGRIND_ 29 | extern void incoming_cell_stats(struct cellstatus_t *cellst_pbuf_small, 30 | struct cellstatus_t *cellst_pbuf_medium, 31 | struct cellstatus_t *cellst_pbuf_large); 32 | #endif 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /src/keyhash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Matti Aarnio, OH2MQK, 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | /* 12 | * Keyhash routines for the system. 13 | * 14 | * What is needed is _fast_ hash function. Preferrably arithmethic one, 15 | * which does not need table lookups, and can work with aligned 32 bit 16 | * data -- but also on unaligned, and on any byte counts... 17 | * 18 | * Contenders: 19 | * http://burtleburtle.net/bob/c/lookup3.c 20 | * http://www.ibiblio.org/pub/Linux/devel/lang/c/mph-1.2.tar.gz 21 | * http://www.concentric.net/~Ttwang/tech/inthash.htm 22 | * http://isthe.com/chongo/tech/comp/fnv/ 23 | * 24 | * Currently using FNV-1a 25 | * 26 | */ 27 | 28 | /* 29 | // FNV-1a hash from http://isthe.com/chongo/tech/comp/fnv/ 30 | // 31 | // It is algorithmic hash without memory lookups. 32 | // Compiler seems to prefer actual multiplication over a bunch of 33 | // fixed shifts and additions. 34 | */ 35 | 36 | 37 | /* 38 | * ON A HIGHLY OPTIMIZED CRC32 HASH CALCULATION PROCESSING ALONE 39 | * THE SYSTEM IS PUSHING AROUND 8-12 % OF CPU TIME! 40 | * 41 | * ... but the previous top waster, the aprsc output filters, are optimized 42 | * to the hilt... 43 | * 44 | * There exists alternate implementations of CRC32 which are 1.7 - 2.3 times 45 | * faster than this one with an expense of using 4kB / 8 kB / 16 kB of tables, 46 | * which of course fill caches... 47 | * 48 | */ 49 | 50 | #include 51 | #include 52 | 53 | #include "keyhash.h" 54 | 55 | #ifdef __GNUC__ // compiling with GCC ? 56 | 57 | #define likely(x) __builtin_expect(!!(x), 1) 58 | #define unlikely(x) __builtin_expect(!!(x), 0) 59 | 60 | #else 61 | 62 | #define likely(x) (x) 63 | #define unlikely(x) (x) 64 | 65 | #define __attribute__(x) 66 | 67 | #endif 68 | 69 | void keyhash_init(void) { } 70 | 71 | uint32_t __attribute__((pure)) keyhash(const void *p, int len, uint32_t hash) 72 | { 73 | const uint8_t *u = p; 74 | int i; 75 | #define FNV_32_PRIME 16777619U 76 | #define FVN_32_OFFSET 2166136261U 77 | 78 | if (hash == 0) 79 | hash = (uint32_t)FVN_32_OFFSET; 80 | 81 | for (i = 0; i < len; ++i, ++u) { 82 | #if defined(NO_FNV_GCC_OPTIMIZATION) 83 | hash *= FNV_32_PRIME; 84 | #else 85 | hash += (hash<<1) + (hash<<4) + (hash<<7) + 86 | (hash<<8) + (hash<<24); 87 | #endif 88 | hash ^= (uint32_t) *u; 89 | } 90 | return hash; 91 | } 92 | 93 | /* The data material is known to contain ASCII, and if any value in there 94 | * is a lower case letter, it is first converted to upper case one. 95 | */ 96 | uint32_t __attribute__((pure)) keyhashuc(const void *p, int len, uint32_t hash) 97 | { 98 | const uint8_t *u = p; 99 | int i; 100 | 101 | if (hash == 0) 102 | hash = (uint32_t)FVN_32_OFFSET; 103 | 104 | for (i = 0; i < len; ++i, ++u) { 105 | #if defined(NO_FNV_GCC_OPTIMIZATION) 106 | hash *= FNV_32_PRIME; 107 | #else 108 | hash += (hash<<1) + (hash<<4) + (hash<<7) + 109 | (hash<<8) + (hash<<24); 110 | #endif 111 | uint32_t c = *u; 112 | // Is it lower case ASCII letter ? 113 | if ('a' <= c && c <= 'z') { 114 | // convert to upper case. 115 | c -= ('a' - 'A'); 116 | } 117 | hash ^= c; 118 | } 119 | return hash; 120 | } 121 | -------------------------------------------------------------------------------- /src/keyhash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Matti Aarnio, OH2MQK, 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef KEYHASH_H 12 | #define KEYHASH_H 13 | 14 | extern void keyhash_init(void); 15 | extern uint32_t keyhash(const void *s, int slen, uint32_t hash0); 16 | extern uint32_t keyhashuc(const void *s, int slen, uint32_t hash0); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/login.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef LOGIN_H 12 | #define LOGIN_H 13 | 14 | #include "worker.h" 15 | 16 | extern int http_udp_upload_login(const char *addr_rem, char *s, char **username, const char *log_source); 17 | extern int login_handler(struct worker_t *self, struct client_t *c, int l4proto, char *s, int len); 18 | extern void login_set_app_name(struct client_t *c, const char *app_name, const char *app_ver); 19 | extern int login_setup_udp_feed(struct client_t *c, int port); 20 | 21 | #endif 22 | 23 | -------------------------------------------------------------------------------- /src/m4/ax_check_gnu_make.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_GNU_MAKE() 8 | # 9 | # DESCRIPTION 10 | # 11 | # This macro searches for a GNU version of make. If a match is found, the 12 | # makefile variable `ifGNUmake' is set to the empty string, otherwise it 13 | # is set to "#". This is useful for including a special features in a 14 | # Makefile, which cannot be handled by other versions of make. The 15 | # variable _cv_gnu_make_command is set to the command to invoke GNU make 16 | # if it exists, the empty string otherwise. 17 | # 18 | # Here is an example of its use: 19 | # 20 | # Makefile.in might contain: 21 | # 22 | # # A failsafe way of putting a dependency rule into a makefile 23 | # $(DEPEND): 24 | # $(CC) -MM $(srcdir)/*.c > $(DEPEND) 25 | # 26 | # @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND))) 27 | # @ifGNUmake@ include $(DEPEND) 28 | # @ifGNUmake@ endif 29 | # 30 | # Then configure.in would normally contain: 31 | # 32 | # AX_CHECK_GNU_MAKE() 33 | # AC_OUTPUT(Makefile) 34 | # 35 | # Then perhaps to cause gnu make to override any other make, we could do 36 | # something like this (note that GNU make always looks for GNUmakefile 37 | # first): 38 | # 39 | # if ! test x$_cv_gnu_make_command = x ; then 40 | # mv Makefile GNUmakefile 41 | # echo .DEFAULT: > Makefile ; 42 | # echo \ $_cv_gnu_make_command \$@ >> Makefile; 43 | # fi 44 | # 45 | # Then, if any (well almost any) other make is called, and GNU make also 46 | # exists, then the other make wraps the GNU make. 47 | # 48 | # LICENSE 49 | # 50 | # Copyright (c) 2008 John Darrington 51 | # 52 | # Copying and distribution of this file, with or without modification, are 53 | # permitted in any medium without royalty provided the copyright notice 54 | # and this notice are preserved. This file is offered as-is, without any 55 | # warranty. 56 | 57 | #serial 7 58 | 59 | AC_DEFUN([AX_CHECK_GNU_MAKE], [ AC_CACHE_CHECK( for GNU make,_cv_gnu_make_command, 60 | _cv_gnu_make_command='' ; 61 | dnl Search all the common names for GNU make 62 | for a in "$MAKE" make gmake gnumake ; do 63 | if test -z "$a" ; then continue ; fi ; 64 | if ( sh -c "$a --version" 2> /dev/null | grep GNU 2>&1 > /dev/null ) ; then 65 | _cv_gnu_make_command=$a ; 66 | break; 67 | fi 68 | done ; 69 | ) ; 70 | dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise 71 | if test "x$_cv_gnu_make_command" != "x" ; then 72 | ifGNUmake='' ; 73 | else 74 | ifGNUmake='#' ; 75 | AC_MSG_RESULT("Not found"); 76 | fi 77 | AC_SUBST(ifGNUmake) 78 | ] ) 79 | -------------------------------------------------------------------------------- /src/messaging.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * aprsc 4 | * 5 | * (c) Heikki Hannikainen, OH7LZB 6 | * 7 | * This program is licensed under the BSD license, which can be found 8 | * in the file LICENSE. 9 | * 10 | * 11 | * The messaging module implements utility functions for processing and 12 | * generating APRS messages. 13 | */ 14 | 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "messaging.h" 21 | #include "version.h" 22 | #include "config.h" 23 | 24 | /* 25 | * Generate a message ID 26 | */ 27 | 28 | void messaging_generate_msgid(char *buf, int buflen) 29 | { 30 | int i, c; 31 | 32 | for (i = 0; i < buflen-1; i++) { 33 | // coverity[dont_call] // squelch warning: not security sensitive use of random(): APRS message-id 34 | c = random() % (2*26 + 10); /* letters and numbers */ 35 | 36 | if (c < 10) 37 | buf[i] = c + 48; /* number */ 38 | else if (c < 26+10) 39 | buf[i] = c - 10 + 97; /* lower-case letter */ 40 | else 41 | buf[i] = c - 36 + 65; /* upper-case letter */ 42 | } 43 | 44 | /* null-terminate */ 45 | buf[i] = 0; 46 | } 47 | 48 | 49 | /* 50 | * Ack an incoming message 51 | */ 52 | 53 | int messaging_ack(struct worker_t *self, struct client_t *c, struct pbuf_t *pb, struct aprs_message_t *am) 54 | { 55 | return client_printf(self, c, "SERVER>" APRSC_TOCALL ",TCPIP*,qAZ,%s::%-9.*s:ack%.*s\r\n", 56 | serverid, pb->srcname_len, pb->srcname, am->msgid_len, am->msgid); 57 | } 58 | 59 | /* 60 | * Send a message to a local logged-in client 61 | */ 62 | 63 | extern int messaging_message_client(struct worker_t *self, struct client_t *c, const char *fmt, ...) 64 | { 65 | va_list args; 66 | char s[PACKETLEN_MAX]; 67 | char msgid[5]; 68 | 69 | va_start(args, fmt); 70 | vsnprintf(s, PACKETLEN_MAX, fmt, args); 71 | va_end(args); 72 | 73 | messaging_generate_msgid(msgid, sizeof(msgid)); 74 | 75 | return client_printf(self, c, "SERVER>" APRSC_TOCALL ",TCPIP*,qAZ,%s::%-9s:%s{%s\r\n", 76 | serverid, c->username, s, msgid); 77 | } 78 | 79 | -------------------------------------------------------------------------------- /src/messaging.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * aprsc 4 | * 5 | * (c) Heikki Hannikainen, OH7LZB 6 | * 7 | * This program is licensed under the BSD license, which can be found 8 | * in the file LICENSE. 9 | * 10 | */ 11 | 12 | #ifndef MESSAGING_H 13 | #define MESSAGING_H 14 | 15 | #include "worker.h" 16 | #include "parse_aprs.h" 17 | 18 | extern void messaging_generate_msgid(char *buf, int buflen); 19 | extern int messaging_ack(struct worker_t *self, struct client_t *c, struct pbuf_t *pb, struct aprs_message_t *am); 20 | extern int messaging_message_client(struct worker_t *self, struct client_t *c, const char *fmt, ...); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/netdb6.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Matti Aarnio, OH2MQK, 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | /* 11 | IPv6 API additions for the ZMailer at those machines 12 | without proper libraries and includes. 13 | By Matti Aarnio 1997,2004 14 | */ 15 | 16 | #ifndef __ 17 | # ifdef __STDC__ 18 | # define __(x) x 19 | # else 20 | # define __(x) () 21 | # endif 22 | #endif 23 | 24 | #ifndef HAVE_GETADDRINFO 25 | #ifndef AI_PASSIVE 26 | 27 | struct addrinfo { 28 | int ai_flags; /* AI_PASSIVE | AI_CANONNAME */ 29 | int ai_family; /* PF_xxx */ 30 | int ai_socktype; /* SOCK_xxx */ 31 | int ai_protocol; /* 0, or IPPROTO_xxx for IPv4 and IPv6 */ 32 | size_t ai_addrlen; /* Length of ai_addr */ 33 | char *ai_canonname; /* canonical name for hostname */ 34 | struct sockaddr *ai_addr; /* binary address */ 35 | struct addrinfo *ai_next; /* next structure in linked list */ 36 | }; 37 | 38 | 39 | extern int getaddrinfo __(( const char *node, const char *service, 40 | const struct addrinfo *hints, 41 | struct addrinfo **res )); 42 | extern void freeaddrinfo __(( struct addrinfo *res )); 43 | extern const char *gai_strerror __((int errcode)); 44 | 45 | #define AI_PASSIVE 1 /* Socket address is intended for `bind'. */ 46 | #endif 47 | #ifndef AI_CANONNAME 48 | #define AI_CANONNAME 2 /* Request for canonical name. */ 49 | #endif 50 | #ifndef AI_NUMERICHOST 51 | #define AI_NUMERICHOST 4 /* Don't use name resolution. */ 52 | #endif 53 | 54 | #ifndef EAI_ADDRFAMILY 55 | /* Error values for `getaddrinfo' function. */ 56 | #define EAI_BADFLAGS -1 /* Invalid value for `ai_flags' field. */ 57 | #define EAI_NONAME -2 /* NAME or SERVICE is unknown. */ 58 | #define EAI_AGAIN -3 /* Temporary failure in name resolution. */ 59 | #define EAI_FAIL -4 /* Non-recoverable failure in name res. */ 60 | #define EAI_NODATA -5 /* No address associated with NAME. */ 61 | #define EAI_FAMILY -6 /* `ai_family' not supported. */ 62 | #define EAI_SOCKTYPE -7 /* `ai_socktype' not supported. */ 63 | #define EAI_SERVICE -8 /* SERVICE not supported for `ai_socktype'. */ 64 | #define EAI_ADDRFAMILY -9 /* Address family for NAME not supported. */ 65 | #define EAI_MEMORY -10 /* Memory allocation failure. */ 66 | #define EAI_SYSTEM -11 /* System error returned in `errno'. */ 67 | #endif 68 | 69 | #ifndef NI_MAXHOST 70 | #define NI_MAXHOST 1025 71 | #define NI_MAXSERV 32 72 | 73 | #define NI_NUMERICHOST 0x01 74 | #define NI_NUMERICSERV 0x02 75 | #define NI_NAMEREQD 0x04 76 | #define NI_NOFQDN 0x08 77 | #define NI_DGRAM 0x10 78 | #endif 79 | #endif /* ndef HAVE_GETADDRINFO */ 80 | -------------------------------------------------------------------------------- /src/netlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "netlib.h" 18 | 19 | #if 0 20 | /* 21 | * Convert address & port to string 22 | */ 23 | 24 | int aptoa(struct in_addr sin_addr, int sin_port, char *s, int len) 25 | { 26 | int l; 27 | 28 | if (sin_addr.s_addr == INADDR_ANY) 29 | return snprintf(s, len, "*:%d", sin_port); 30 | else { 31 | if (inet_ntop(AF_INET, (void *)&sin_addr, s, len) == NULL) { 32 | return snprintf(s, len, "ERROR:%d", sin_port); 33 | } else { 34 | l = strlen(s); 35 | return snprintf(s + l, len - l, ":%d", sin_port) + l; 36 | } 37 | } 38 | } 39 | 40 | /* 41 | * Convert return values of gethostbyname() to a string 42 | */ 43 | 44 | int h_strerror(int i, char *s, int len) 45 | { 46 | switch (i) { 47 | case HOST_NOT_FOUND: 48 | return snprintf(s, len, "Host not found"); 49 | case NO_ADDRESS: 50 | return snprintf(s, len, "No IP address found for name"); 51 | case NO_RECOVERY: 52 | return snprintf(s, len, "A non-recovable name server error occurred"); 53 | case TRY_AGAIN: 54 | return snprintf(s, len, "A temporary error on an authoritative name server"); 55 | default: 56 | return snprintf(s, len, "%d", i); 57 | } 58 | } 59 | #endif 60 | 61 | -------------------------------------------------------------------------------- /src/netlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef NETLIB_H 12 | #define NETLIB_H 13 | 14 | // extern int aptoa(struct in_addr sin_addr, int sin_port, char *s, int len); 15 | // extern int h_strerror(int i, char *s, int len); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/outgoing.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef OUTGOING_H 12 | #define OUTGOING_H 13 | 14 | #include "worker.h" 15 | extern void process_outgoing(struct worker_t *self); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/parse_aprs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef PARSE_APRS_H 12 | #define PARSE_APRS_H 13 | 14 | #include "worker.h" 15 | #include "parse_aprs.h" 16 | 17 | struct aprs_message_t { 18 | const char *body; /* message body */ 19 | const char *msgid; 20 | 21 | int body_len; 22 | int msgid_len; 23 | int is_ack; 24 | }; 25 | 26 | extern int parse_aprs(struct pbuf_t *pb); 27 | extern int parse_aprs_message(struct pbuf_t *pb, struct aprs_message_t *am); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/parse_qc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef PARSE_QC_H 12 | #define PARSE_QC_H 13 | 14 | #include "worker.h" /* struct client_t */ 15 | 16 | extern int check_invalid_q_callsign(const char *call, int len); 17 | 18 | extern int q_process(struct client_t *c, const char *pdata, 19 | char *new_q, int new_q_size, char *via_start, 20 | char **path_end, int pathlen, char **new_q_start, char **q_replace, 21 | int originated_by_client); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/passcode.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "passcode.h" 6 | 7 | /* As of April 11 2000 Steve Dimse has released this code to the open 8 | * source aprs community 9 | * 10 | * (from aprsd sources) 11 | */ 12 | 13 | #define kKey 0x73e2 // This is the key for the data 14 | 15 | short aprs_passcode(const char* theCall) 16 | { 17 | char rootCall[10]; // need to copy call to remove ssid from parse 18 | char *p1 = rootCall; 19 | 20 | while ((*theCall != '-') && (*theCall != 0) && (p1 < rootCall + 9)) 21 | *p1++ = toupper(*theCall++); 22 | 23 | *p1 = 0; 24 | 25 | short hash = kKey; // Initialize with the key value 26 | short i = 0; 27 | short len = strlen(rootCall); 28 | char *ptr = rootCall; 29 | 30 | while (i < len) { // Loop through the string two bytes at a time 31 | hash ^= (*ptr++)<<8; // xor high byte with accumulated hash 32 | hash ^= (*ptr++); // xor low byte with accumulated hash 33 | i += 2; 34 | } 35 | 36 | return hash & 0x7fff; // mask off the high bit so number is always positive 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/passcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef PASSCODE_H 12 | #define PASSCODE_H 13 | 14 | extern short aprs_passcode(const char* theCall); 15 | 16 | #endif 17 | 18 | -------------------------------------------------------------------------------- /src/random.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "random.h" 11 | #include "hlog.h" 12 | 13 | int urandom_open(void) 14 | { 15 | int fd; 16 | 17 | if ((fd = open("/dev/urandom", O_RDONLY)) == -1) { 18 | hlog(LOG_ERR, "open(/dev/urandom) failed: %s", strerror(errno)); 19 | } 20 | 21 | return fd; 22 | } 23 | 24 | int urandom_alphanumeric(int fd, unsigned char *buf, int buflen) 25 | { 26 | int l; 27 | int len = buflen - 1; 28 | unsigned char c; 29 | 30 | if (fd >= 0) { 31 | /* generate instance id */ 32 | l = read(fd, buf, len); 33 | if (l != len) { 34 | hlog(LOG_ERR, "read(/dev/urandom, %d) failed: %s", len, strerror(errno)); 35 | close(fd); 36 | fd = -1; 37 | } 38 | } 39 | 40 | if (fd < 0) { 41 | /* urandom failed for us, use something inferior */ 42 | for (l = 0; l < len; l++) { 43 | // coverity[dont_call] // squelch warning: not security sensitive use of random() 44 | buf[l] = random() % (26+10); 45 | } 46 | } 47 | 48 | for (l = 0; l < len; l++) { 49 | /* 256 is not divisible by 36, the distribution is slightly skewed, 50 | * but that's not serious. 51 | */ 52 | c = buf[l] % (26 + 10); /* letters and numbers */ 53 | if (c < 10) 54 | c += 48; /* number */ 55 | else 56 | c = c - 10 + 97; /* letter */ 57 | buf[l] = c; 58 | } 59 | 60 | buf[len] = 0; 61 | 62 | return len; 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/random.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RANDOM_H 3 | #define RANDOM_H 4 | 5 | extern int urandom_open(void); 6 | extern int urandom_alphanumeric(int fd, unsigned char *buf, int buflen); 7 | 8 | #endif 9 | 10 | -------------------------------------------------------------------------------- /src/rpm/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # build products 3 | aprsc.spec 4 | -------------------------------------------------------------------------------- /src/rwlock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is from the book "Programming with POSIX Threads", by 3 | * David R. Butenhof. Appears in modified and GPL'ed form in at least 4 | * the Bacula sources. 5 | */ 6 | 7 | #ifndef RWLOCK_H 8 | #define RWLOCK_H 9 | 10 | /* 11 | * rwlock.h 12 | * 13 | * This header file describes the "reader/writer lock" synchronization 14 | * construct. The type rwlock_t describes the full state of the lock 15 | * including the POSIX 1003.1c synchronization objects necessary. 16 | * 17 | * A reader/writer lock allows a thread to lock shared data either for shared 18 | * read access or exclusive write access. 19 | * 20 | * The rwl_init() and rwl_destroy() functions, respectively, allow you to 21 | * initialize/create and destroy/free the reader/writer lock. 22 | */ 23 | 24 | #include 25 | 26 | #ifdef PTHREAD_RWLOCK_INITIALIZER 27 | // #ifdef HAVE_PTHREAD_RWLOCK 28 | 29 | #define rwlock_t pthread_rwlock_t 30 | 31 | #define RWL_INITIALIZER PTHREAD_RWLOCK_INITIALIZER 32 | 33 | #define rwl_init(tag) pthread_rwlock_init(tag, NULL) 34 | #define rwl_destroy pthread_rwlock_destroy 35 | #define rwl_rdlock pthread_rwlock_rdlock 36 | #define rwl_tryrdlock pthread_rwlock_tryrdlock 37 | #define rwl_rdunlock pthread_rwlock_unlock 38 | #define rwl_wrlock pthread_rwlock_wrlock 39 | #define rwl_trywrlock pthread_rwlock_trywrlock 40 | #define rwl_wrunlock pthread_rwlock_unlock 41 | 42 | #else /* HAVE_PTHREAD_RWLOCK */ 43 | 44 | /* 45 | * Structure describing a read-write lock. 46 | */ 47 | typedef struct rwlock_tag { 48 | pthread_mutex_t mutex; 49 | pthread_cond_t read; /* wait for read */ 50 | pthread_cond_t write; /* wait for write */ 51 | int valid; /* set when valid */ 52 | int r_active; /* readers active */ 53 | int w_active; /* writer active */ 54 | int r_wait; /* readers waiting */ 55 | int w_wait; /* writers waiting */ 56 | } rwlock_t; 57 | 58 | #define RWLOCK_VALID 0xfacade 59 | 60 | /* 61 | * Support static initialization of read-write locks. 62 | */ 63 | #define RWL_INITIALIZER \ 64 | {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \ 65 | PTHREAD_COND_INITIALIZER, RWLOCK_VALID, 0, 0, 0, 0} 66 | 67 | /* 68 | * Define read-write lock functions 69 | */ 70 | extern int rwl_init (rwlock_t *rwlock); 71 | extern int rwl_destroy (rwlock_t *rwlock); 72 | extern int rwl_rdlock (rwlock_t *rwlock); 73 | extern int rwl_tryrdlock (rwlock_t *rwlock); 74 | extern int rwl_rdunlock (rwlock_t *rwlock); 75 | extern int rwl_wrlock (rwlock_t *rwlock); 76 | extern int rwl_trywrlock (rwlock_t *rwlock); 77 | extern int rwl_wrunlock (rwlock_t *rwlock); 78 | 79 | #endif /* not HAVE_POSIX_RWLOCK */ 80 | 81 | #endif /* RWLOCK_H */ 82 | 83 | -------------------------------------------------------------------------------- /src/sctp.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SCTP_H 3 | #define SCTP_H 4 | 5 | #ifdef USE_SCTP 6 | 7 | #include "accept.h" 8 | 9 | extern int sctp_set_client_sockopt(struct client_t *c); 10 | extern int sctp_set_listen_params(struct listen_t *l); 11 | extern int sctp_readable(struct worker_t *self, struct client_t *c); 12 | extern int sctp_writable(struct worker_t *self, struct client_t *c); 13 | extern int sctp_client_write(struct worker_t *self, struct client_t *c, char *p, int len); 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/status.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef STATUS_H 12 | #define STATUS_H 13 | 14 | #include 15 | #include "cJSON.h" 16 | 17 | extern time_t startup_tick, startup_time; 18 | extern cJSON *liveupgrade_status; 19 | 20 | extern void status_error(int ttl, const char *err); 21 | 22 | extern char *status_json_string(int no_cache, int periodical); 23 | extern int status_dump_file(void); 24 | extern int status_dump_liveupgrade(void); 25 | extern int status_read_liveupgrade(void); 26 | extern void status_init(void); 27 | extern void status_atend(void); 28 | 29 | extern char *hex_encode(const char *buf, int len); 30 | extern int hex_decode(char *obuf, int olen, const char *hex); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/tls.h: -------------------------------------------------------------------------------- 1 | 2 | #include "ac-hdrs.h" 3 | 4 | #ifdef HAVE_OPENSSL_SSL_H 5 | #define USE_SSL 6 | #endif 7 | 8 | #ifndef SSL_H 9 | #define SSL_H 10 | 11 | #ifdef USE_SSL 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | /* ssl error codes, must match ssl_err_labels order */ 20 | #define SSL_VALIDATE_INTERNAL_ERROR -1 21 | #define SSL_VALIDATE_CLIENT_CERT_UNVERIFIED -2 22 | #define SSL_VALIDATE_NO_CLIENT_CERT -3 23 | #define SSL_VALIDATE_CERT_NO_SUBJECT -4 24 | #define SSL_VALIDATE_CERT_NO_CALLSIGN -5 25 | #define SSL_VALIDATE_CERT_CALLSIGN_MISMATCH -6 26 | 27 | struct client_t; 28 | struct worker_t; 29 | 30 | struct ssl_t { 31 | SSL_CTX *ctx; 32 | 33 | unsigned validate; 34 | }; 35 | 36 | struct ssl_connection_t { 37 | SSL *connection; 38 | 39 | unsigned handshaked:1; 40 | 41 | unsigned renegotiation:1; 42 | unsigned buffer:1; 43 | unsigned no_wait_shutdown:1; 44 | unsigned no_send_shutdown:1; 45 | 46 | unsigned validate; 47 | int ssl_err_code; 48 | }; 49 | 50 | #define NGX_SSL_SSLv2 0x0002 51 | #define NGX_SSL_SSLv3 0x0004 52 | #define NGX_SSL_TLSv1 0x0008 53 | #define NGX_SSL_TLSv1_1 0x0010 54 | #define NGX_SSL_TLSv1_2 0x0020 55 | 56 | 57 | #define NGX_SSL_BUFFER 1 58 | #define NGX_SSL_CLIENT 2 59 | 60 | #define NGX_SSL_BUFSIZE 16384 61 | 62 | /* string representations for error codes */ 63 | extern const char *ssl_strerror(int code); 64 | 65 | /* initialize and deinit the library */ 66 | extern int ssl_init(void); 67 | extern void ssl_atend(void); 68 | 69 | /* per-listener structure allocators */ 70 | extern struct ssl_t *ssl_alloc(void); 71 | extern void ssl_free(struct ssl_t *ssl); 72 | 73 | /* create context for listener, load certs */ 74 | extern int ssl_create(struct ssl_t *ssl, void *data); 75 | extern int ssl_certificate(struct ssl_t *ssl, const char *certfile, const char *keyfile); 76 | extern int ssl_ca_certificate(struct ssl_t *ssl, const char *cafile, int depth); 77 | 78 | /* create / free connection */ 79 | extern int ssl_create_connection(struct ssl_t *ssl, struct client_t *c, int i_am_client); 80 | extern void ssl_free_connection(struct client_t *c); 81 | 82 | /* validate a client certificate */ 83 | extern int ssl_validate_peer_cert_phase1(struct client_t *c); 84 | extern int ssl_validate_peer_cert_phase2(struct client_t *c); 85 | 86 | extern int ssl_write(struct worker_t *self, struct client_t *c); 87 | extern int ssl_writable(struct worker_t *self, struct client_t *c); 88 | extern int ssl_readable(struct worker_t *self, struct client_t *c); 89 | 90 | 91 | #else 92 | 93 | struct ssl_t { 94 | }; 95 | 96 | 97 | #define ssl_init(...) { } 98 | #define ssl_atend(...) { } 99 | 100 | #endif /* USE_SSL */ 101 | #endif /* SSL_H */ 102 | 103 | -------------------------------------------------------------------------------- /src/tools/.gitignore: -------------------------------------------------------------------------------- 1 | sleeptest 2 | floodconnect 3 | eventload 4 | -------------------------------------------------------------------------------- /src/tools/coverity-build-submit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export PATH=$PATH:/opt/src/c/cov-analysis-linux64-8.5.0.2/bin 6 | 7 | make clean 8 | nice cov-build --dir cov-int make -j4 9 | tar cvfz aprsc.tgz cov-int 10 | rm -rf cov-int 11 | 12 | VERSION=`cat VERSION` 13 | PASSWORD=`cat ~/.covpw` 14 | 15 | echo "Uploading version $VERSION to Coverity..." 16 | 17 | curl --form token="$PASSWORD" \ 18 | --form email=hessu@hes.iki.fi \ 19 | --form file=@aprsc.tgz \ 20 | --form version="$VERSION" \ 21 | --form description="" \ 22 | https://scan.coverity.com/builds?project=aprsc 23 | 24 | rm -f aprsc.tgz 25 | 26 | -------------------------------------------------------------------------------- /src/tools/sleeptest.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static double timeval_diff(struct timeval start, struct timeval end) 8 | { 9 | double diff; 10 | 11 | diff = (end.tv_sec - start.tv_sec) 12 | + ((end.tv_usec - start.tv_usec) / 1000000.0); 13 | 14 | return diff; 15 | } 16 | 17 | 18 | int main(int argc, char **argv) 19 | { 20 | time_t tick; 21 | time_t previous_tick; 22 | struct timespec sleep_req; 23 | struct timeval sleep_start, sleep_end; 24 | struct tm lt; 25 | 26 | time(&previous_tick); 27 | 28 | sleep_req.tv_sec = 0; 29 | sleep_req.tv_nsec = 210 * 1000 * 1000; /* 210 ms */ 30 | 31 | while (1) { 32 | gettimeofday(&sleep_start, NULL); 33 | nanosleep(&sleep_req, NULL); 34 | gettimeofday(&sleep_end, NULL); 35 | time(&tick); 36 | gmtime_r(&sleep_start.tv_sec, <); 37 | 38 | double slept = timeval_diff(sleep_start, sleep_end); 39 | if (slept > 0.50) { 40 | printf("%4d/%02d/%02d %02d:%02d:%02d.%06d ", lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec, (int)sleep_start.tv_usec); 41 | printf("time keeping: sleep of %ld ms took %.6f s!\n", sleep_req.tv_nsec / 1000 / 1000, slept); 42 | } 43 | 44 | /* catch some oddities with time keeping */ 45 | if (tick != previous_tick) { 46 | if (previous_tick > tick) { 47 | printf("%4d/%02d/%02d %02d:%02d:%02d.%06d ", lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec, (int)sleep_start.tv_usec); 48 | printf("time keeping: Time jumped backward by %ld seconds!\n", previous_tick - tick); 49 | } else if (previous_tick < tick-1) { 50 | printf("%4d/%02d/%02d %02d:%02d:%02d.%06d ", lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec, (int)sleep_start.tv_usec); 51 | printf("time keeping: Time jumped forward by %ld seconds!\n", tick - previous_tick); 52 | } 53 | 54 | previous_tick = tick; 55 | } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/uplink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Matti Aarnio, OH2MQK, 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef UPLINK_H 12 | #define UPLINK_H 1 13 | 14 | #include "worker.h" 15 | 16 | extern int uplink_reconfiguring; 17 | extern int uplink_shutting_down; 18 | 19 | extern void uplink_thread(void *asdf); 20 | extern void uplink_close(struct client_t *c, int errnum); 21 | extern void uplink_start(void); 22 | extern void uplink_stop(void); 23 | extern int uplink_login_handler(struct worker_t *self, struct client_t *c, int l4proto, char *s, int len); 24 | 25 | #endif /* FILTER_H */ 26 | -------------------------------------------------------------------------------- /src/version.c: -------------------------------------------------------------------------------- 1 | 2 | #include "version.h" 3 | #include "version_data.h" 4 | #include "version_branch.h" 5 | #include "config.h" 6 | #include "xpoll.h" 7 | #include "tls.h" 8 | 9 | const char version_build[] = VERSION "-" SRCVERSION VERSION_BRANCH; 10 | const char verstr[] = PROGNAME " " VERSION "-" SRCVERSION VERSION_BRANCH; 11 | const char verstr_aprsis[] = PROGNAME " " VERSION "-" SRCVERSION VERSION_BRANCH; 12 | const char verstr_http[] = PROGNAME "/" VERSION; 13 | 14 | const char verstr_build_time[] = BUILD_TIME; 15 | const char verstr_build_user[] = BUILD_USER; 16 | 17 | const char verstr_features[] = 18 | #ifdef XP_USE_EPOLL 19 | " epoll" 20 | #endif 21 | #ifdef XP_USE_POLL 22 | " poll" 23 | #endif 24 | #ifdef USE_EVENTFD 25 | " eventfd" 26 | #endif 27 | #ifdef USE_POSIX_CAP 28 | " posix_cap" 29 | #endif 30 | #ifdef USE_CLOCK_GETTIME 31 | " clock_gettime" 32 | #endif 33 | #ifdef HAVE_SYNC_FETCH_AND_ADD 34 | " gcc_atomics" 35 | #endif 36 | #ifdef HAVE_LIBZ 37 | " zlib" 38 | #endif 39 | #ifdef _FOR_VALGRIND_ 40 | " valgrind" 41 | #endif 42 | #ifdef USE_SSL 43 | " tls" 44 | #endif 45 | #ifdef USE_SCTP 46 | " sctp" 47 | #endif 48 | ; 49 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Version / build information generated automatically 4 | * 5 | * Please mark your branch in version_branch.h ! 6 | */ 7 | 8 | #ifndef VERSION_H 9 | #define VERSION_H 10 | 11 | /* 12 | * If you're making modifications, put your own variant version 13 | * identification in version_branch.h. Thanks! 14 | */ 15 | 16 | #define APRSC_TOCALL "APSC20" 17 | 18 | extern const char version_build[]; 19 | extern const char verstr[]; 20 | extern const char verstr_aprsis[]; 21 | extern const char verstr_http[]; 22 | extern const char verstr_build_time[]; 23 | extern const char verstr_build_user[]; 24 | extern const char verstr_features[]; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/version_branch.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef VERSION_BRANCH_H 3 | #define VERSION_BRANCH_H 4 | 5 | /* 6 | * If you're building your own version of aprsc, and have actually 7 | * made modifications in the code, please insert a branch ID here 8 | * so that your variant can be identified. 9 | * 10 | * Examples: 11 | * 12 | * #define VERSION_BRANCH "-mycall-5" 13 | * (for version 5 of modified tree made by MYCALL) 14 | * 15 | * Don't forget to put a - in the beginning, as the string will be 16 | * concated with the version string (aprsc 1.0-725-mycall-5). 17 | * 18 | * Thanks! 19 | */ 20 | 21 | #define VERSION_BRANCH "" 22 | 23 | #endif 24 | 25 | -------------------------------------------------------------------------------- /src/web/angular-translate-loader-url.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-translate - v2.11.1 - 2016-07-17 3 | * 4 | * Copyright (c) 2016 The angular-translate team, Pascal Precht; Licensed MIT 5 | */ 6 | !function(a,b){"function"==typeof define&&define.amd?define([],function(){return b()}):"object"==typeof exports?module.exports=b():b()}(this,function(){function a(a,b){"use strict";return function(c){if(!c||!c.url)throw new Error("Couldn't use urlLoader since no url is given!");var d={};return d[c.queryParameter||"lang"]=c.key,b(angular.extend({url:c.url,params:d,method:"GET"},c.$http)).then(function(a){return a.data},function(){return a.reject(c.key)})}}return a.$inject=["$q","$http"],angular.module("pascalprecht.translate").factory("$translateUrlLoader",a),a.displayName="$translateUrlLoader","pascalprecht.translate"}); -------------------------------------------------------------------------------- /src/web/aprsc-joulukissa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/web/aprsc-joulukissa.jpg -------------------------------------------------------------------------------- /src/web/aprsc-logo4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/web/aprsc-logo4.png -------------------------------------------------------------------------------- /src/web/aprsc-logo4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/web/aprsc-logo4@2x.png -------------------------------------------------------------------------------- /src/web/aprsc.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family: verdana, arial, helvetica, sans-serif; 4 | font-size: 12px; 5 | } 6 | 7 | div.sprite_logo { 8 | background: url(aprsc-logo4.png); 9 | width: 233px; 10 | height: 69px; 11 | } 12 | 13 | @media 14 | only screen and (min-device-pixel-ratio: 2), 15 | only screen and (-webkit-min-device-pixel-ratio: 2), 16 | only screen and (-moz-min-device-pixel-ratio: 2), 17 | only screen and (-o-min-device-pixel-ratio: 2/1) { 18 | div.sprite_logo { 19 | background: url(aprsc-logo4@2x.png); 20 | background-size: 233px 69px; 21 | } 22 | } 23 | 24 | @brand-primary: #0024ff; 25 | .text-primary { color: #0024ff; } 26 | 27 | .grey { color: #666; } 28 | .bold { font-weight: bold; } 29 | .red { color: #ff5370; } 30 | .rxerr { color: #0000ff; cursor: pointer; } 31 | .rxerr_red { color: #ff5370; cursor: pointer; } 32 | .link { color: #0000ff; cursor: pointer; } 33 | /* show/hide buttons */ 34 | .link { color: #0000ff; } 35 | .show { cursor: pointer; } 36 | .hide { cursor: pointer; display: none; } 37 | 38 | div.page_title { 39 | color: #000; 40 | margin-top: 5px; 41 | margin-bottom: 5px; 42 | font-weight: bold; 43 | } 44 | 45 | #graph { cursor: pointer; } 46 | 47 | table { 48 | } 49 | 50 | /* 51 | td { 52 | background: #e1f6ff; 53 | padding: 1px 4px 1px 4px; 54 | margin: 1px 3px 1px 10px; 55 | border: 1px solid white; 56 | vertical-align: top; 57 | }*/ 58 | 59 | td.grtd { 60 | background: #e1f6ff; color: #0000ff; cursor: pointer; 61 | } 62 | 63 | td.grtd_sel { 64 | background: #f6e1ff; cursor: pointer; 65 | } 66 | 67 | td.username { 68 | white-space: nowrap; 69 | } 70 | 71 | /* 72 | th { 73 | vertical-align: top; 74 | }*/ 75 | 76 | td.ar { 77 | text-align: right; 78 | } 79 | 80 | 81 | /* message */ 82 | div.msg_e { 83 | background-color: #f2d2d2; 84 | } 85 | 86 | div.msg_i { 87 | background-color: #ecf581; 88 | } 89 | 90 | div.msg_s { 91 | background-color: #c8ffc4; 92 | } 93 | 94 | div.msg_e, div.msg_i, div.msg_s { 95 | border: thin solid #bdbdbd; 96 | padding: 5px; 97 | margin: 5px 25px 5px 0px; 98 | border-radius: 5px; 99 | width: 480px; 100 | } 101 | 102 | /* tooltip */ 103 | 104 | div.ttip { 105 | position: absolute; 106 | left: 100px; 107 | top: 100px; 108 | background: #ffffff; 109 | font-size: 11px; 110 | width: 300px; 111 | z-index: 80; 112 | border: 1px solid #ff0000; 113 | padding: 7px; 114 | border-radius: 5px; 115 | box-shadow: 8px 8px 3px rgba(0,0,0,0.5); 116 | display: none; 117 | } 118 | 119 | /* specific tables */ 120 | 121 | div#dupecheck_more { 122 | display: none; 123 | margin-left: 20px; 124 | } 125 | 126 | div#motd { display: none; } 127 | 128 | /* buttons */ 129 | 130 | button { 131 | padding: 5px; 132 | } 133 | 134 | -------------------------------------------------------------------------------- /src/web/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/web/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/web/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/web/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/web/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/web/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/web/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/web/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/web/bootstrap/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /src/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hessu/aprsc/a4efaa54f75e6d7f53235720001ed76a01cda4e4/src/web/favicon.ico -------------------------------------------------------------------------------- /src/web/jquery.flot.resize.min.js: -------------------------------------------------------------------------------- 1 | /* Javascript plotting library for jQuery, version 0.8.3. 2 | 3 | Copyright (c) 2007-2014 IOLA and Ole Laursen. 4 | Licensed under the MIT license. 5 | 6 | */ 7 | (function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this);(function($){var options={};function init(plot){function onResize(){var placeholder=plot.getPlaceholder();if(placeholder.width()==0||placeholder.height()==0)return;plot.resize();plot.setupGrid();plot.draw()}function bindEvents(plot,eventHolder){plot.getPlaceholder().resize(onResize)}function shutdown(plot,eventHolder){plot.getPlaceholder().unbind("resize",onResize)}plot.hooks.bindEvents.push(bindEvents);plot.hooks.shutdown.push(shutdown)}$.plot.plugins.push({init:init,options:options,name:"resize",version:"1.0"})})(jQuery); -------------------------------------------------------------------------------- /src/web/ngDialog-theme-plain.min.css: -------------------------------------------------------------------------------- 1 | /*! ng-dialog - v0.4.0 (https://github.com/likeastore/ngDialog) */ 2 | 3 | .ngdialog.ngdialog-theme-plain{padding-bottom:160px;padding-top:160px}.ngdialog.ngdialog-theme-plain .ngdialog-content{background:#fff;color:#444;font-family:'Helvetica Neue',sans-serif;font-size:1.1em;line-height:1.5em;margin:0 auto;max-width:100%;padding:1em;position:relative;width:450px}.ngdialog.ngdialog-theme-plain .ngdialog-content h1,.ngdialog.ngdialog-theme-plain .ngdialog-content h2,.ngdialog.ngdialog-theme-plain .ngdialog-content h3,.ngdialog.ngdialog-theme-plain .ngdialog-content h4,.ngdialog.ngdialog-theme-plain .ngdialog-content h5,.ngdialog.ngdialog-theme-plain .ngdialog-content h6,.ngdialog.ngdialog-theme-plain .ngdialog-content li,.ngdialog.ngdialog-theme-plain .ngdialog-content p,.ngdialog.ngdialog-theme-plain .ngdialog-content ul{color:inherit}.ngdialog.ngdialog-theme-plain .ngdialog-close{cursor:pointer;position:absolute;right:0;top:0}.ngdialog.ngdialog-theme-plain .ngdialog-close:before{background:0 0;color:#bbb;content:"\00D7";font-size:26px;font-weight:400;height:30px;line-height:26px;position:absolute;right:3px;text-align:center;top:3px;width:30px}.ngdialog.ngdialog-theme-plain .ngdialog-close:active:before,.ngdialog.ngdialog-theme-plain .ngdialog-close:hover:before{color:#777}.ngdialog.ngdialog-theme-plain .ngdialog-message{margin-bottom:.5em}.ngdialog.ngdialog-theme-plain .ngdialog-input{margin-bottom:1em}.ngdialog.ngdialog-theme-plain .ngdialog-input input[type=email],.ngdialog.ngdialog-theme-plain .ngdialog-input input[type=password],.ngdialog.ngdialog-theme-plain .ngdialog-input input[type=text],.ngdialog.ngdialog-theme-plain .ngdialog-input input[type=url],.ngdialog.ngdialog-theme-plain .ngdialog-input textarea{background:#f0f0f0;border:0;font-family:inherit;font-size:inherit;font-weight:inherit;margin:0 0 .25em;min-height:2.5em;padding:.25em .67em;width:100%}.ngdialog.ngdialog-theme-plain .ngdialog-input input[type=email]:focus,.ngdialog.ngdialog-theme-plain .ngdialog-input input[type=password]:focus,.ngdialog.ngdialog-theme-plain .ngdialog-input input[type=text]:focus,.ngdialog.ngdialog-theme-plain .ngdialog-input input[type=url]:focus,.ngdialog.ngdialog-theme-plain .ngdialog-input textarea:focus{-webkit-box-shadow:inset 0 0 0 2px rgba(0,0,0,.2);box-shadow:inset 0 0 0 2px rgba(0,0,0,.2);outline:0}.ngdialog.ngdialog-theme-plain .ngdialog-buttons:after{clear:both;content:'';display:table}.ngdialog.ngdialog-theme-plain .ngdialog-button{border:0;cursor:pointer;float:right;font-family:inherit;font-size:.8em;letter-spacing:.1em;line-height:1em;margin:0 0 0 .5em;padding:.75em 2em;text-transform:uppercase}.ngdialog.ngdialog-theme-plain .ngdialog-button:focus{-webkit-animation:ngdialog-pulse 1.1s infinite;animation:ngdialog-pulse 1.1s infinite;outline:0}@media (max-width:568px){.ngdialog.ngdialog-theme-plain .ngdialog-button:focus{-webkit-animation:none;animation:none}}.ngdialog.ngdialog-theme-plain .ngdialog-button.ngdialog-button-primary{background:#3288e6;color:#fff}.ngdialog.ngdialog-theme-plain .ngdialog-button.ngdialog-button-secondary{background:#e0e0e0;color:#777} -------------------------------------------------------------------------------- /src/web/ngDialog.min.css: -------------------------------------------------------------------------------- 1 | /*! ng-dialog - v0.4.0 (https://github.com/likeastore/ngDialog) */ 2 | 3 | @-webkit-keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}@keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}.ngdialog{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ngdialog *,.ngdialog :after,.ngdialog :before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}.ngdialog{position:fixed;overflow:auto;-webkit-overflow-scrolling:touch;z-index:10000;top:0;right:0;bottom:0;left:0}.ngdialog-overlay{position:fixed;background:rgba(0,0,0,.4);top:0;right:0;bottom:0;left:0;-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s}.ngdialog.ngdialog-closing .ngdialog-overlay{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-content{background:#fff;-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadein .5s;animation:ngdialog-fadein .5s}.ngdialog.ngdialog-closing .ngdialog-content{-webkit-backface-visibility:hidden;-webkit-animation:ngdialog-fadeout .5s;animation:ngdialog-fadeout .5s}.ngdialog-close:before{font-family:Helvetica,Arial,sans-serif;content:'\00D7';cursor:pointer}body.ngdialog-open{overflow:hidden} -------------------------------------------------------------------------------- /src/xpoll.h: -------------------------------------------------------------------------------- 1 | /* 2 | * aprsc 3 | * 4 | * (c) Heikki Hannikainen, OH7LZB 5 | * 6 | * This program is licensed under the BSD license, which can be found 7 | * in the file LICENSE. 8 | * 9 | */ 10 | 11 | #ifndef XPOLL_H 12 | #define XPOLL_H 13 | 14 | // Lots of subsystems use poll(2) call for short timeouts 15 | #include 16 | 17 | #include "ac-hdrs.h" 18 | 19 | #ifdef HAVE_SYS_EPOLL_H 20 | // Have Linux 21 | #define XP_USE_EPOLL 1 22 | #include 23 | 24 | #else 25 | 26 | #define XP_USE_POLL 1 27 | #define XP_INCREMENT 64 // The "struct pollfd" is _small_, avoid mem fragment 28 | #endif 29 | 30 | 31 | #define XP_IN 1 32 | #define XP_OUT 2 33 | #define XP_ERR 4 34 | 35 | struct xpoll_fd_t { 36 | int fd; 37 | void *p; /* a fd-specific pointer, which will be passed to handlers */ 38 | 39 | int result; 40 | 41 | #ifdef XP_USE_EPOLL 42 | struct epoll_event ev; // event flags for this fd. 43 | #else 44 | #ifdef XP_USE_POLL 45 | int pollfd_n; /* index to xp->pollfd[] */ 46 | #endif 47 | #endif 48 | 49 | struct xpoll_fd_t *next; 50 | struct xpoll_fd_t **prevp; 51 | }; 52 | 53 | struct xpoll_t { 54 | void *tp; /* an xpoll_t-specific pointer, which will be passed to handlers */ 55 | struct xpoll_fd_t *fds; 56 | 57 | int (*handler) (struct xpoll_t *xp, struct xpoll_fd_t *xfd); 58 | 59 | #ifdef XP_USE_EPOLL 60 | int epollfd; 61 | // #define MAX_EPOLL_EVENTS 32 62 | // struct epoll_event events[MAX_EPOLL_EVENTS]; 63 | 64 | #else 65 | #ifdef XP_USE_POLL 66 | struct pollfd *pollfd; 67 | int pollfd_len; 68 | #endif 69 | #endif 70 | int pollfd_used; 71 | }; 72 | 73 | extern struct xpoll_t *xpoll_initialize(struct xpoll_t *xp, void *tp, int (*handler) (struct xpoll_t *xp, struct xpoll_fd_t *xfd)); 74 | extern int xpoll_free(struct xpoll_t *xp); 75 | extern struct xpoll_fd_t *xpoll_add(struct xpoll_t *xp, int fd, void *p); 76 | extern int xpoll_remove(struct xpoll_t *xp, struct xpoll_fd_t *xfd); 77 | extern void xpoll_outgoing(struct xpoll_t *xp, struct xpoll_fd_t *xfd, int have_outgoing); 78 | extern int xpoll(struct xpoll_t *xp, int timeout); 79 | extern void xpoll_init(void); 80 | 81 | extern const char xpoll_implementation[]; 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Makefile for running the test suite 3 | 4 | TEST_FILES = t/*.t 5 | LIGHT_TEST_IGNORE = t/64udp-load.t 6 | LIGHT_TEST_FILES = $(filter-out $(LIGHT_TEST_IGNORE), $(wildcard t/*.t)) 7 | 8 | TEST_VERBOSE = 0 9 | INST_LIB = libperl 10 | INST_ARCHLIB = libperl 11 | 12 | PERL=/usr/bin/perl 13 | 14 | TESTID=`echo $$$$_$$RANDOM$$RANDOM$$RANDOM` 15 | TEST_SRCCALLS="XX7FIT-1,XX7FIT-2" 16 | TEST_IGATE="XX7IG" 17 | 18 | test: tls-testcert 19 | @rm -f logs/aprsc.log 20 | PERL_DL_NONLAZY=1 $(PERL) "-MExtUtils::Command::MM" "-e" "test_harness($(TEST_VERBOSE), '$(INST_LIB)', '$(INST_ARCHLIB)')" $(TEST_FILES) 21 | 22 | travistest: 23 | @rm -f logs/aprsc.log 24 | PERL_DL_NONLAZY=1 $(PERL) "-MExtUtils::Command::MM" "-e" "test_harness($(TEST_VERBOSE), '$(INST_LIB)', '$(INST_ARCHLIB)')" $(LIGHT_TEST_FILES) 25 | 26 | AGGREGATOR_FILES = aggregator/*.t 27 | 28 | aggrtest: 29 | @rm -f logs/aprsc.log 30 | TEST_ID=$(TESTID) TEST_IGATE=$(TEST_IGATE) TEST_SRCCALLS=$(TEST_SRCCALLS) \ 31 | PERL_DL_NONLAZY=1 $(PERL) "-MExtUtils::Command::MM" "-e" "test_harness($(TEST_VERBOSE), '$(INST_LIB)', '$(INST_ARCHLIB)')" $(AGGREGATOR_FILES) 32 | 33 | tls-testca: tls-testca/cacert.pem 34 | tls-testcert: tls-testca cfg-aprsc/tls1-cert.pem cfg-aprsc/tls-client-cert.pem 35 | 36 | tls-testca/serial: 37 | mkdir -p tls-testca tmp 38 | (cd tls-testca && mkdir -p certs newcerts private) 39 | touch tls-testca/index.txt tls-testca/index.txt.attr 40 | echo 00 > tls-testca/serial 41 | 42 | tls-testca/cacert.pem: tls-testca/serial 43 | openssl req -x509 -new -nodes -newkey rsa:2048 -keyout tls-testca/private/cakey.pem -sha256 -days 7300 \ 44 | -subj "/O=aprsc TEST/OU=Testing Department/CN=Testing CA" -out tls-testca/cacert.pem 45 | 46 | cfg-aprsc/tls1-cert.pem: tls-testca/cacert.pem 47 | openssl req -new -config tls-openssl.conf \ 48 | -keyout cfg-aprsc/tls1-key.pem -out tmp/tls1-req.pem \ 49 | -newkey rsa:2048 -nodes -sha256 \ 50 | -subj "/O=aprsc TEST/callSign=TLS1/CN=tls1host.example.com" 51 | 52 | openssl ca -config tls-openssl.conf -batch \ 53 | -in tmp/tls1-req.pem -out cfg-aprsc/tls1-cert.pem \ 54 | -days 3650 55 | 56 | cfg-aprsc/tls-client-cert.pem: tls-testca/cacert.pem 57 | openssl req -new -config tls-openssl.conf \ 58 | -keyout cfg-aprsc/tls-client-key.pem -out tmp/tls-client-req.pem \ 59 | -newkey rsa:2048 -nodes -sha256 \ 60 | -subj "/O=aprsc TEST/callSign=N5CAL" 61 | 62 | openssl ca -config tls-openssl.conf -batch \ 63 | -in tmp/tls-client-req.pem -out cfg-aprsc/tls-client-cert.pem \ 64 | -days 3650 65 | 66 | -------------------------------------------------------------------------------- /tests/aggregator/20socket-timeouts.t: -------------------------------------------------------------------------------- 1 | ######################### 2 | # Test the client and server socket timeouts 3 | ######################### 4 | 5 | use Test; 6 | 7 | my $id_ok; 8 | 9 | BEGIN { 10 | plan tests => 15 11 | }; 12 | 13 | use Ham::APRS::IS_Fake; 14 | use Ham::APRS::IS; 15 | use runproduct; 16 | use Time::HiRes qw(time sleep); 17 | 18 | my $ret; 19 | 20 | ok(1); # modules load fine 21 | 22 | my $iss1 = new Ham::APRS::IS_Fake('127.0.0.1:57153', 'CORE1'); 23 | ok(1); # there's a working socket 24 | 25 | $iss1->bind_and_listen(); 26 | ok(1); 27 | 28 | # initialize the product runner using the basic configuration 29 | my $p = new runproduct('aggregator'); 30 | ok(defined $p, 1, "Failed to initialize product runner"); 31 | ok($p->start(), 1, "Failed to start product"); 32 | 33 | my $is1 = $iss1->accept(); 34 | ok(defined $is1, (1), "Failed to accept connection 1 from server"); 35 | ok($iss1->process_login($is1), 'ok', "Failed to accept login 1 from server"); 36 | 37 | my $t_start = time(); 38 | 39 | $read1 = $is1->getline_noncomment(30); 40 | ok($read1, undef, "Ouch, got data from server when none should have been available"); 41 | 42 | my $t_end = time(); 43 | my $t_dif = $t_end - $t_start; 44 | warn sprintf("\ntimeout on uplink socket took %.3f s, should take 10s\n", $t_dif); 45 | ok($t_dif > 9 && $t_dif < 15); 46 | 47 | # create client connection 48 | my $cl = new Ham::APRS::IS("localhost:56152", 'CL1ENT'); 49 | ok(defined $cl, 1, "Failed to initialize Ham::APRS::IS"); 50 | $ret = $cl->connect('retryuntil' => 8); 51 | ok($ret, 1, "Failed to connect to the server: " . $cl->{'error'}); 52 | 53 | $t_start = time(); 54 | 55 | $read1 = $cl->getline_noncomment(60); 56 | ok($read1, undef, "Ouch, received data from server on client connection when none should have been available"); 57 | 58 | $t_end = time(); 59 | $t_dif = $t_end - $t_start; 60 | warn sprintf("\ntimeout on client socket took %.3f s, should take 20s\n", $t_dif); 61 | ok($t_dif > 19 && $t_dif < 25); 62 | 63 | $ret = $is1->disconnect(); 64 | ok($ret, 1, "Failed to disconnect 1: " . $is1->{'error'}); 65 | 66 | ok($p->stop(), 1, "Failed to stop product"); 67 | 68 | 69 | ######################### 70 | 71 | 72 | -------------------------------------------------------------------------------- /tests/ca/cert-selfsigned.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # create new key and cert request 4 | openssl req -new -out ssl.req -subj "/callSign=TESTING/CN=Testing" -newkey rsa:4096 -days 3650 -nodes -config openssl.conf 5 | 6 | # self-sign 7 | openssl x509 -req -in ssl.req -out ssl.crt -signkey privkey.pem 8 | 9 | -------------------------------------------------------------------------------- /tests/cfg-aprsc/acl-all.acl: -------------------------------------------------------------------------------- 1 | 2 | # acl 3 | 4 | allow 127.0.0.0/24 5 | allow 44.0.0.0/8 6 | allow 83.145.252.0/24 7 | allow 85.188.1.0/24 8 | 9 | allow ::1/126 10 | allow 2001:1bc8:101:1::/64 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/cfg-aprsc/aggregator: -------------------------------------------------------------------------------- 1 | 2 | ServerId TESTING 3 | PassCode 31421 4 | MyEmail email@example.com 5 | MyAdmin "Admin, N0CALL" 6 | 7 | ### Directories ######### 8 | # Data directory (for database files) 9 | RunDir data 10 | 11 | ### Intervals ######### 12 | # Interval specification format examples: 13 | # 600 (600 seconds), 5m, 2h, 1h30m, 1d3h15m24s, etc... 14 | 15 | # NOTE: THESE SHORT TIMEOUTS ARE USED BY THE TIMEOUT TEST, DON'T CHANGE IN THIS CONFIG 16 | # When no data is received from an upstream server in N seconds, switch to 17 | # another server 18 | UpstreamTimeout 10s 19 | 20 | # When no data is received from a downstream server in N seconds, disconnect 21 | ClientTimeout 20s 22 | 23 | ### TCP listener ########## 24 | # Listen tcp
25 | # socketname: any name you wish to show up in logs and statistics 26 | # porttype: one of: 27 | # fullfeed - everything, after dupe filtering 28 | # dupefeed - everything that comes in - with dupes! 29 | # msgonly - messages only 30 | # userfilter - user-specified filters 31 | # 32 | Listen "Full feed with CWOP" fullfeed tcp 0.0.0.0 56152 33 | Listen "Igate port" igate tcp 0.0.0.0 56580 34 | Listen "Client-only port" clientonly tcp 0.0.0.0 56581 35 | 36 | Uplink full1 multiro tcp 127.0.0.1 57153 37 | Uplink full2 multiro tcp 127.0.0.1 57154 38 | 39 | ### Internals ############ 40 | # Only use 3 threads in these basic tests, to keep startup/shutdown times 41 | # short. 42 | WorkerThreads 3 43 | 44 | # When running this server as super-user, the server can (in many systems) 45 | # increase several resource limits, and do other things that less privileged 46 | # server can not do. 47 | # 48 | # The FileLimit is resource limit on how many simultaneous connections and 49 | # some other internal resources the system can use at the same time. 50 | # If the server is not being run as super-user, this setting has no effect. 51 | # 52 | FileLimit 10000 53 | 54 | -------------------------------------------------------------------------------- /tests/cfg-aprsc/basic: -------------------------------------------------------------------------------- 1 | 2 | ServerId TESTING 3 | PassCode 31421 4 | MyEmail email@example.com 5 | MyAdmin "Admin, N0CALL" 6 | 7 | ### Directories ######### 8 | # Data directory (for database files) 9 | RunDir data 10 | 11 | ### Intervals ######### 12 | # Interval specification format examples: 13 | # 600 (600 seconds), 5m, 2h, 1h30m, 1d3h15m24s, etc... 14 | 15 | # When no data is received from an upstream server in N seconds, switch to 16 | # another server 17 | UpstreamTimeout 10s 18 | 19 | # When no data is received from a downstream server in N seconds, disconnect 20 | ClientTimeout 48h 21 | 22 | ### TCP listener ########## 23 | # Listen tcp
24 | # socketname: any name you wish to show up in logs and statistics 25 | # porttype: one of: 26 | # fullfeed - everything, after dupe filtering 27 | # dupefeed - everything that comes in - with dupes! 28 | # msgonly - messages only 29 | # userfilter - user-specified filters 30 | # 31 | Listen "Full feed with" fullfeed tcp ::0 55152 acl "cfg-aprsc/acl-all.acl" 32 | Listen "Full feed with, UDP" fullfeed udp ::0 55152 33 | Listen "Full feed, TLS" fullfeed tcp ::0 55154 acl "cfg-aprsc/acl-all.acl" \ 34 | tlskey cfg-aprsc/tls1-key.pem tlscert cfg-aprsc/tls1-cert.pem tlsca tls-testca/cacert.pem 35 | Listen "Igate port" igate tcp 0.0.0.0 55580 acl "cfg-aprsc/acl-all.acl" 36 | Listen "Igate port, UDP" igate udp 0.0.0.0 55580 37 | Listen "Client-only port" clientonly tcp 0.0.0.0 55581 38 | Listen "Igate port, TLS" igate tcp 0.0.0.0 55582 \ 39 | tlskey cfg-aprsc/tls1-key.pem tlscert cfg-aprsc/tls1-cert.pem tlsca tls-testca/cacert.pem 40 | Listen "Client-only port, TLS" clientonly tcp 0.0.0.0 55583 \ 41 | tlskey cfg-aprsc/tls1-key.pem tlscert cfg-aprsc/tls1-cert.pem tlsca tls-testca/cacert.pem 42 | Listen "Duplicates" dupefeed tcp 0.0.0.0 55153 43 | Listen "UDP submit port" udpsubmit udp ::0 55080 44 | 45 | ### Uplink configuration ######## 46 | # Uplink tcp
47 | # name: a name of the server or service you're connecting 48 | # type: one of: 49 | # full - full feed 50 | # ro - read-only, do not transmit anything upstream 51 | # 52 | Uplink full1 full tcp 127.0.0.1 10153 53 | 54 | # UDP peering, first address is my local address, the rest are remote. 55 | PeerGroup TEST udp 127.0.0.1:16404 \ 56 | SELF 127.0.0.1:16404 \ 57 | PEER1 127.0.0.1:16405 \ 58 | PEER2 127.0.0.1:16406 59 | PeerGroup TEST6 udp [::1]:16504 \ 60 | SELF6 [::1]:16504 \ 61 | PEER61 [::1]:16505 \ 62 | PEER62 [::1]:16506 63 | 64 | ### HTTP listener ########## 65 | # Status port provides a status view to web browsers. 66 | # It starts up by default on 0.0.0.0:14501. 67 | HTTPStatus 127.0.0.1 55501 68 | # Upload port allows position uploads. 69 | # It does not start up by default. 70 | HTTPUpload 127.0.0.1 55080 71 | 72 | ### Internals ############ 73 | # Only use 3 threads in these basic tests, to keep startup/shutdown times 74 | # short. 75 | WorkerThreads 3 76 | 77 | # When running this server as super-user, the server can (in many systems) 78 | # increase several resource limits, and do other things that less privileged 79 | # server can not do. 80 | # 81 | # The FileLimit is resource limit on how many simultaneous connections and 82 | # some other internal resources the system can use at the same time. 83 | # If the server is not being run as super-user, this setting has no effect. 84 | # 85 | FileLimit 10000 86 | 87 | # Additional callsigns blocked 88 | DisallowSourceCall N7CALL N8CALL* *DROP DRG* OH?DRU O*ZZZ 89 | DisallowLoginCall LOGINA LOGINB *prrej mi*rej sufre* 90 | 91 | -------------------------------------------------------------------------------- /tests/cfg-aprsc/qconstr-protocols: -------------------------------------------------------------------------------- 1 | # 2 | # USE RCS !!! 3 | # $Id$ 4 | # 5 | 6 | # Configuration for aprsc, an APRS-IS server for core servers 7 | 8 | ServerId TESTING 9 | PassCode 31421 10 | MyEmail email@example.com 11 | MyAdmin "Admin, N0CALL" 12 | 13 | ### Directories ######### 14 | # Data directory (for database files) 15 | RunDir data 16 | 17 | ### Intervals ######### 18 | # Interval specification format examples: 19 | # 600 (600 seconds), 5m, 2h, 1h30m, 1d3h15m24s, etc... 20 | 21 | # When no data is received from an upstream server in N seconds, switch to 22 | # another server 23 | UpstreamTimeout 10s 24 | 25 | # When no data is received from a downstream server in N seconds, disconnect 26 | ClientTimeout 48h 27 | 28 | ### TCP listener ########## 29 | # Listen tcp
30 | # socketname: any name you wish to show up in logs and statistics 31 | # porttype: one of: 32 | # fullfeed - everything, after dupe filtering 33 | # dupefeed - everything that comes in - with dupes! 34 | # msgonly - messages only 35 | # userfilter - user-specified filters 36 | # 37 | Listen "Full feed with CWOP" fullfeed tcp ::0 55152 acl "cfg-aprsc/acl-all.acl" 38 | Listen "Full feed with CWOP, UDP" fullfeed udp ::0 55152 39 | Listen "Igate port" igate tcp 0.0.0.0 55580 acl "cfg-aprsc/acl-all.acl" 40 | Listen "Igate port, UDP" igate udp 0.0.0.0 55580 41 | Listen "Client-only port" clientonly tcp 0.0.0.0 55581 42 | Listen "Duplicates" dupefeed tcp 0.0.0.0 55153 43 | Listen "UDP submit port" udpsubmit udp ::0 55080 44 | 45 | ### Uplink configuration ######## 46 | # Uplink tcp
47 | # name: a name of the server or service you're connecting 48 | # type: one of: 49 | # full - full feed 50 | # ro - read-only, do not transmit anything upstream 51 | # 52 | Uplink full1 full tcp 127.0.0.1 10153 53 | 54 | # UDP peering, first address is my local address, the rest are remote. 55 | PeerGroup TEST udp 127.0.0.1:16404 \ 56 | SELF 127.0.0.1:16404 \ 57 | PEER1 127.0.0.1:16405 \ 58 | PEER2 127.0.0.1:16406 59 | PeerGroup TEST6 udp [::1]:16504 \ 60 | SELF6 [::1]:16504 \ 61 | PEER61 [::1]:16505 \ 62 | PEER62 [::1]:16506 63 | 64 | ### HTTP listener ########## 65 | # Status port provides a status view to web browsers. 66 | # It starts up by default on 0.0.0.0:14501. 67 | HTTPStatus :: 55501 68 | # Upload port allows position uploads. 69 | # It does not start up by default. 70 | HTTPUpload :: 55080 71 | 72 | ### Internals ############ 73 | # Only use 3 threads in these basic tests, to keep startup/shutdown times 74 | # short. 75 | WorkerThreads 3 76 | 77 | # When running this server as super-user, the server can (in many systems) 78 | # increase several resource limits, and do other things that less privileged 79 | # server can not do. 80 | # 81 | # The FileLimit is resource limit on how many simultaneous connections and 82 | # some other internal resources the system can use at the same time. 83 | # If the server is not being run as super-user, this setting has no effect. 84 | # 85 | FileLimit 10000 86 | 87 | # Testing other protocol identifiers in Q construct 88 | q_protocol_id O 89 | 90 | -------------------------------------------------------------------------------- /tests/cfg-aprsc/sctp-hub: -------------------------------------------------------------------------------- 1 | 2 | # sctp hub 3 | 4 | ServerId TESTING-1 5 | PassCode 31421 6 | MyEmail email@example.com 7 | MyAdmin "Admin, N0CALL" 8 | 9 | ### Directories ######### 10 | # Data directory (for database files) 11 | RunDir data 12 | 13 | ### Intervals ######### 14 | # Interval specification format examples: 15 | # 600 (600 seconds), 5m, 2h, 1h30m, 1d3h15m24s, etc... 16 | 17 | # When no data is received from an upstream server in N seconds, switch to 18 | # another server 19 | UpstreamTimeout 90s 20 | 21 | # When no data is received from a downstream server in N seconds, disconnect 22 | ClientTimeout 48h 23 | 24 | ### TCP listener ########## 25 | # Listen tcp
26 | # socketname: any name you wish to show up in logs and statistics 27 | # porttype: one of: 28 | # fullfeed - everything, after dupe filtering 29 | # dupefeed - everything that comes in - with dupes! 30 | # msgonly - messages only 31 | # userfilter - user-specified filters 32 | # 33 | Listen "Full feed" fullfeed tcp ::0 55152 acl "cfg-aprsc/acl-all.acl" 34 | Listen "Full feed SCTP" fullfeed sctp ::0 55152 acl "cfg-aprsc/acl-all.acl" 35 | Listen "Igate port" igate tcp 0.0.0.0 55582 acl "cfg-aprsc/acl-all.acl" 36 | 37 | ### HTTP listener ########## 38 | # Status port provides a status view to web browsers. 39 | # It starts up by default on 0.0.0.0:14501. 40 | HTTPStatus 127.0.0.1 55501 41 | 42 | 43 | -------------------------------------------------------------------------------- /tests/cfg-aprsc/sctp-leaf: -------------------------------------------------------------------------------- 1 | 2 | # sctp hub 3 | 4 | ServerId TESTING-2 5 | PassCode 31421 6 | MyEmail email@example.com 7 | MyAdmin "Admin, N0CALL" 8 | 9 | ### Directories ######### 10 | # Data directory (for database files) 11 | RunDir data 12 | 13 | ### Intervals ######### 14 | # Interval specification format examples: 15 | # 600 (600 seconds), 5m, 2h, 1h30m, 1d3h15m24s, etc... 16 | 17 | # When no data is received from an upstream server in N seconds, switch to 18 | # another server 19 | UpstreamTimeout 90s 20 | 21 | # When no data is received from a downstream server in N seconds, disconnect 22 | ClientTimeout 48h 23 | 24 | ### TCP/SCTP listener ########## 25 | # Listen tcp
26 | # socketname: any name you wish to show up in logs and statistics 27 | # porttype: one of: 28 | # fullfeed - everything, after dupe filtering 29 | # dupefeed - everything that comes in - with dupes! 30 | # msgonly - messages only 31 | # userfilter - user-specified filters 32 | # 33 | Listen "Full feed" fullfeed tcp ::0 55153 acl "cfg-aprsc/acl-all.acl" 34 | Listen "Full feed SCTP" fullfeed sctp ::0 55153 acl "cfg-aprsc/acl-all.acl" 35 | Listen "Igate port" igate tcp 0.0.0.0 55583 acl "cfg-aprsc/acl-all.acl" 36 | 37 | ### SCTP uplink 38 | Uplink fullsctp full sctp 127.0.0.1 55152 39 | 40 | ### HTTP listener ########## 41 | # Status port provides a status view to web browsers. 42 | # It starts up by default on 0.0.0.0:14501. 43 | HTTPStatus 127.0.0.1 55502 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/cfg-aprsc/tls1: -------------------------------------------------------------------------------- 1 | 2 | ServerId TLS1 3 | PassCode 29855 4 | MyEmail email@example.com 5 | MyAdmin "Admin, N0CALL" 6 | 7 | ### Directories ######### 8 | # Data directory (for database files) 9 | RunDir data 10 | 11 | ### Intervals ######### 12 | # Interval specification format examples: 13 | # 600 (600 seconds), 5m, 2h, 1h30m, 1d3h15m24s, etc... 14 | 15 | # When no data is received from an upstream server in N seconds, switch to 16 | # another server 17 | UpstreamTimeout 60s 18 | 19 | # When no data is received from a downstream server in N seconds, disconnect 20 | ClientTimeout 48h 21 | 22 | ### TCP listener ########## 23 | # Listen tcp
24 | # socketname: any name you wish to show up in logs and statistics 25 | # porttype: one of: 26 | # fullfeed - everything, after dupe filtering 27 | # dupefeed - everything that comes in - with dupes! 28 | # msgonly - messages only 29 | # userfilter - user-specified filters 30 | # 31 | Listen "Full feed TLS" fullfeed tcp ::0 55152 acl "cfg-aprsc/acl-all.acl" \ 32 | tlskey cfg-aprsc/tls1-key.pem tlscert cfg-aprsc/tls1-cert.pem tlsca tls-testca/cacert.pem 33 | Listen "Igate port TLS" igate tcp 0.0.0.0 55580 acl "cfg-aprsc/acl-all.acl" \ 34 | tlskey cfg-aprsc/tls1-key.pem tlscert cfg-aprsc/tls1-cert.pem tlsca tls-testca/cacert.pem 35 | 36 | ### Uplink configuration ######## 37 | # Uplink tcp
38 | # name: a name of the server or service you're connecting 39 | # type: one of: 40 | # full - full feed 41 | # ro - read-only, do not transmit anything upstream 42 | # 43 | Uplink full1 full tcp 127.0.0.1 10153 44 | 45 | # UDP peering, first address is my local address, the rest are remote. 46 | PeerGroup TLS1 udp 127.0.0.1:16404 \ 47 | SELF 127.0.0.1:16404 \ 48 | PEER1 127.0.0.1:16405 \ 49 | PEER2 127.0.0.1:16406 50 | 51 | ### HTTP listener ########## 52 | # Status port provides a status view to web browsers. 53 | # It starts up by default on 0.0.0.0:14501. 54 | HTTPStatus 127.0.0.1 55501 55 | # Upload port allows position uploads. 56 | # It does not start up by default. 57 | HTTPUpload 127.0.0.1 55080 58 | 59 | ### Internals ############ 60 | # Only use 3 threads in these basic tests, to keep startup/shutdown times 61 | # short. 62 | WorkerThreads 3 63 | 64 | 65 | -------------------------------------------------------------------------------- /tests/cfg-aprsc/uplinks: -------------------------------------------------------------------------------- 1 | # 2 | # USE RCS !!! 3 | # $Id$ 4 | # 5 | 6 | # Configuration for aprsc, an APRS-IS server for core servers 7 | 8 | ServerId TESTING 9 | PassCode 31421 10 | MyEmail email@example.com 11 | MyAdmin "Admin, N0CALL" 12 | 13 | ### Directories ######### 14 | # Data directory (for database files) 15 | RunDir data 16 | 17 | ### Intervals ######### 18 | # Interval specification format examples: 19 | # 600 (600 seconds), 5m, 2h, 1h30m, 1d3h15m24s, etc... 20 | 21 | # When no data is received from an upstream server in N seconds, switch to 22 | # another server 23 | UpstreamTimeout 10s 24 | 25 | # When no data is received from a downstream server in N seconds, disconnect 26 | ClientTimeout 48h 27 | 28 | ### TCP listener ########## 29 | # Listen tcp
30 | # socketname: any name you wish to show up in logs and statistics 31 | # porttype: one of: 32 | # fullfeed - everything, after dupe filtering 33 | # dupefeed - everything that comes in - with dupes! 34 | # msgonly - messages only 35 | # userfilter - user-specified filters 36 | # 37 | Listen "Full feed with CWOP" fullfeed tcp 0.0.0.0 55152 38 | Listen "Igate port" igate tcp 0.0.0.0 55580 39 | Listen "Client-only port" clientonly tcp 0.0.0.0 55581 40 | 41 | Uplink core1 multiro tcp 127.0.0.1 54153 42 | Uplink core2 multiro tcp ::1 54153 43 | 44 | ### Internals ############ 45 | # Only use 3 threads in these basic tests, to keep startup/shutdown times 46 | # short. 47 | WorkerThreads 3 48 | 49 | # When running this server as super-user, the server can (in many systems) 50 | # increase several resource limits, and do other things that less privileged 51 | # server can not do. 52 | # 53 | # The FileLimit is resource limit on how many simultaneous connections and 54 | # some other internal resources the system can use at the same time. 55 | # If the server is not being run as super-user, this setting has no effect. 56 | # 57 | FileLimit 10000 58 | 59 | -------------------------------------------------------------------------------- /tests/cfg-javap/basic: -------------------------------------------------------------------------------- 1 | userCall=TESTING 2 | AdminInfo=Admin, N0CALL 3 | isBackground=false 4 | stderr=logs/err.log 5 | # 6 | # Global Parameters 7 | # 8 | excludeCalls=NITRUS;N0CALL;NOCALL 9 | useHistoryQueue=false 10 | disallowUnverified=true 11 | # 12 | # Server Adjunct 13 | # 14 | ServerAdjunct= 15 | #ServerAdjunct=ServerAdjunct 16 | # 17 | # Console and Log Ports 18 | # 19 | # Core peers, first one is the local address to bind, the next ones remote 20 | srvrToSrvrPorts=127.0.0.1\:16404;127.0.0.1\:16405 21 | # 22 | consolePorts=127.0.0.1:14500 23 | logList=127.0.0.1 24 | logPorts=127.0.0.1:14502 25 | errorPorts=127.0.0.1:14503 26 | dupePorts=127.0.0.1:14504 27 | loopPorts=127.0.0.1:14505 28 | callLogPorts=127.0.0.1:14506 29 | connLogPorts=127.0.0.1:14507 30 | TNCLogPorts=127.0.0.1:14508 31 | # 32 | # Status Page Parameters 33 | # 34 | statusPorts=55501 35 | plainStatusPorts=55511 36 | portTable=10152;Full APRS-IS Feed;14580;User-defined Filtered Feed;14581;User-defined Filtered Feed;1314;Message-only Feed 37 | # 38 | # Client Port Properties 39 | # 40 | noEchoPorts=55152u 41 | msgOnlyPorts=55314u 42 | IGatePorts=55580u 43 | clientOnlyPorts=55581u 44 | # 45 | globalHTTPPorts=55080 46 | globalUDPPorts=55080 47 | # 48 | # servers 49 | upstreamHubs=127.0.0.1:10153 50 | # 51 | -------------------------------------------------------------------------------- /tests/cfg-javap/uplinks: -------------------------------------------------------------------------------- 1 | userCall=TESTING 2 | AdminInfo=Admin, N0CALL 3 | isBackground=false 4 | stderr=logs/err.log 5 | # 6 | # Global Parameters 7 | # 8 | excludeCalls=NITRUS;N0CALL;NOCALL 9 | useHistoryQueue=false 10 | disallowUnverified=false 11 | # 12 | # Server Adjunct 13 | # 14 | ServerAdjunct=ServerAdjunct 15 | # 16 | # Console and Log Ports 17 | # 18 | consolePorts=127.0.0.1:14500 19 | logList=127.0.0.1 20 | logPorts=127.0.0.1:14502 21 | errorPorts=127.0.0.1:14503 22 | dupePorts=127.0.0.1:14504 23 | loopPorts=127.0.0.1:14505 24 | callLogPorts=127.0.0.1:14506 25 | connLogPorts=127.0.0.1:14507 26 | TNCLogPorts=127.0.0.1:14508 27 | # 28 | # Status Page Parameters 29 | # 30 | statusPorts=55501 31 | plainStatusPorts=554511 32 | portTable=10152;Full APRS-IS Feed;14580;User-defined Filtered Feed;14581;User-defined Filtered Feed;1314;Message-only Feed 33 | # 34 | # Client Port Properties 35 | # 36 | noEchoPorts=55152u 37 | msgOnlyPorts=55314u 38 | IGatePorts=55580u 39 | clientOnlyPorts=55581u 40 | # 41 | # servers 42 | upstreamHubs=127.0.0.1:54153;[::1]:54153 43 | # 44 | 45 | -------------------------------------------------------------------------------- /tests/libperl/Ham/APRS/IS_Fake.pm: -------------------------------------------------------------------------------- 1 | 2 | package Ham::APRS::IS_Fake; 3 | 4 | use 5.006; 5 | use strict; 6 | use warnings; 7 | 8 | use IO::Handle '_IOFBF'; 9 | use IO::Socket::INET6; 10 | use IO::Select; 11 | 12 | use Ham::APRS::IS; 13 | 14 | =head1 new(hostport, mycall, optionshash) 15 | 16 | Initializes a new Ham::APRS::IS_Fake listening socket. Takes two mandatory arguments, 17 | the host:port pair to listen on and the server's callsign. 18 | 19 | my $is = new Ham::APRS::IS_Fake('*:12765', 'N0CALL'); 20 | 21 | =cut 22 | 23 | sub new($$$;%) 24 | { 25 | my $that = shift; 26 | my $class = ref($that) || $that; 27 | my $self = { }; 28 | bless ($self, $class); 29 | 30 | my($host_port, $mycall, %options) = @_; 31 | 32 | $self->{'host_port'} = $host_port; 33 | if (defined $mycall) { 34 | $self->{'mycall'} = $mycall; 35 | } else { 36 | $self->{'mycall'} = "FAKE" . sprintf("%d", rand(999)); 37 | } 38 | #$self->{'filter'} = $options{'filter'} if (defined $options{'filter'}); 39 | 40 | #warn "aprspass for $self->{mycall} is $self->{aprspass}\n"; 41 | 42 | $self->{'state'} = 'init'; 43 | $self->{'error'} = "No errors yet."; 44 | 45 | return $self; 46 | } 47 | 48 | sub bind_and_listen($) 49 | { 50 | my($self) = @_; 51 | 52 | my($localaddr, $localport) = split(':', $self->{'host_port'}); 53 | 54 | $self->{'lsock'} = IO::Socket::INET6->new( 55 | Listen => 10, 56 | LocalAddr => $self->{'host_port'}, 57 | Proto => 'tcp', 58 | ReuseAddr => 1, 59 | ReeusePort => 1); 60 | 61 | die "Could not create socket: $!\n" unless $self->{'lsock'}; 62 | } 63 | 64 | sub unbind($) 65 | { 66 | my($self) = @_; 67 | 68 | undef $self->{'lsock'}; 69 | } 70 | 71 | sub accept($) 72 | { 73 | my($self) = @_; 74 | 75 | my $sock = $self->{'lsock'}->accept(); 76 | 77 | return if (!$sock); 78 | 79 | my $is = new Ham::APRS::IS('client:0', $self->{'mycall'}); 80 | $is->accepted($sock); 81 | 82 | return $is; 83 | } 84 | 85 | ## javAPRSSrvr 3.15b07 86 | #user oh7lzb-af 87 | ## logresp oh7lzb-af unverified, server T2FINLAND 88 | 89 | sub send_login_prompt($$) 90 | { 91 | my($self, $is) = @_; 92 | 93 | return $is->sendline("# IS_Fake 1.00"); 94 | } 95 | 96 | sub send_login_ok($$) 97 | { 98 | my($self, $is) = @_; 99 | 100 | return $is->sendline("# logresp CALLSIGN unverified, server " . $self->{'mycall'} ); 101 | } 102 | 103 | sub process_login($$) 104 | { 105 | my($self, $is) = @_; 106 | 107 | my $r; 108 | 109 | $r = $is->sendline("# IS_Fake 1.00"); 110 | if (!$r) { 111 | return 'failed to transmit server software string'; 112 | } 113 | 114 | my $login = $is->getline_noncomment(1); 115 | 116 | if ($login !~ /^user\s+([^\s]+)\s+pass\s+(\d+)/) { 117 | return 'login command not understood: ' . $login; 118 | } 119 | 120 | $is->{'client_user'} = $1; 121 | my $pass_given = $2; 122 | 123 | my $passcode = Ham::APRS::IS::aprspass($1); 124 | 125 | my $verified = ($pass_given eq $passcode) ? 'verified' : 'unverified'; 126 | 127 | $r = $is->sendline("# logresp " . $is->{'client_user'} . " $verified, server " . $self->{'mycall'} ); 128 | if (!$r) { 129 | return 'failed to transmit logresp'; 130 | } 131 | 132 | return 'ok'; 133 | } 134 | 135 | 1; 136 | -------------------------------------------------------------------------------- /tests/libperl/Ham/APRS/IS_Fake_UDP.pm: -------------------------------------------------------------------------------- 1 | 2 | package Ham::APRS::IS_Fake_UDP; 3 | 4 | use 5.006; 5 | use strict; 6 | use warnings; 7 | 8 | use IO::Handle '_IOFBF'; 9 | use IO::Socket::INET; 10 | use IO::Select; 11 | 12 | use Ham::APRS::IS; 13 | 14 | =head1 new(hostport, mycall, optionshash) 15 | 16 | Initializes a new Ham::APRS::IS_Fake_UDP listening socket. Takes two mandatory arguments, 17 | the host:port pair to listen on and the server's callsign. 18 | 19 | my $udp = new Ham::APRS::IS_Fake_UDP('*:12765', 'N0CALL'); 20 | 21 | =cut 22 | 23 | sub new($$$;%) 24 | { 25 | my $that = shift; 26 | my $class = ref($that) || $that; 27 | my $self = { }; 28 | bless ($self, $class); 29 | 30 | my($host_port, $mycall, %options) = @_; 31 | 32 | $self->{'host_port'} = $host_port; 33 | if (defined $mycall) { 34 | $self->{'mycall'} = $mycall; 35 | } else { 36 | $self->{'mycall'} = "FAKE" . sprintf("%d", rand(999)); 37 | } 38 | #$self->{'filter'} = $options{'filter'} if (defined $options{'filter'}); 39 | 40 | #warn "aprspass for $self->{mycall} is $self->{aprspass}\n"; 41 | 42 | $self->{'state'} = 'init'; 43 | $self->{'error'} = "No errors yet."; 44 | 45 | return $self; 46 | } 47 | 48 | sub bind_and_listen($) 49 | { 50 | my($self) = @_; 51 | 52 | my($localaddr, $localport) = split(':', $self->{'host_port'}); # TODO: ipv6... 53 | 54 | $self->{'usock'} = IO::Socket::INET->new( 55 | Proto => 'udp', 56 | LocalPort => $localport, 57 | LocalAddr => $localaddr, 58 | ReuseAddr => 1, 59 | ReeusePort => 1); 60 | 61 | if (!defined($self->{'usock'})) { 62 | $self->{'error'} = "Failed to bind an UDP client socket: $@"; 63 | return 0; 64 | } 65 | 66 | $self->{'usock'}->sockopt(SO_RCVBUF, 32768); 67 | $self->{'usock'}->sockopt(SO_SNDBUF, 32768); 68 | 69 | warn "bound udp port $localaddr $localport, rcvbuf " . $self->{'usock'}->sockopt(SO_RCVBUF) 70 | . " sndbuf " . $self->{'usock'}->sockopt(SO_SNDBUF) . "\n"; 71 | 72 | $self->{'state'} = 'connected'; 73 | return 1; 74 | } 75 | 76 | sub set_destination($$) 77 | { 78 | my($self, $hostport) = @_; 79 | 80 | my($host, $port) = split(':', $hostport); # TODO: ipv6... 81 | 82 | my $hisiaddr = inet_aton($host) || die "unknown host"; 83 | my $hispaddr = sockaddr_in($port, $hisiaddr); 84 | 85 | $self->{'dest'} = $hispaddr; 86 | } 87 | 88 | sub unbind($) 89 | { 90 | my($self) = @_; 91 | 92 | undef $self->{'usock'}; 93 | } 94 | 95 | sub getline($;$) 96 | { 97 | my($self, $timeout) = @_; 98 | 99 | return undef if ($self->{'state'} ne 'connected'); 100 | 101 | $timeout = 5 if (!defined $timeout); 102 | 103 | my $end_t = time() + $timeout; 104 | my $sock = $self->{'usock'}; 105 | 106 | while (1) { 107 | my($rin, $rout, $ein, $eout) = ('', '', '', ''); 108 | vec($rin, fileno($self->{'usock'}), 1) = 1; 109 | my $nfound = select($rout = $rin, undef, $eout = $ein, $timeout); 110 | 111 | if ($nfound) { 112 | my $rbuf; 113 | if (($rout & $rin) eq $rin) { 114 | #warn "getline: got udp\n"; 115 | my $msg; 116 | my $raddr = $self->{'usock'}->recv($msg, 1500); 117 | my($port, $ipaddr) = sockaddr_in($raddr); 118 | my $hishost = inet_ntoa($ipaddr); 119 | #warn "got udp from $hishost $port: $msg\n"; 120 | return $msg; 121 | } 122 | } 123 | 124 | if (time() > $end_t) { 125 | #warn "getline: timeout\n"; 126 | return undef; 127 | } 128 | } 129 | } 130 | 131 | sub sendline($$) 132 | { 133 | my($self, $line) = @_; 134 | 135 | #warn "udp sendline: $line\n"; 136 | 137 | return undef if ($self->{'state'} ne 'connected'); 138 | 139 | #warn "sending\n"; 140 | $self->{'usock'}->send($line, 0, $self->{'dest'}); 141 | 142 | #warn "sent, returning 1\n"; 143 | return 1; 144 | } 145 | 146 | 147 | 1; 148 | -------------------------------------------------------------------------------- /tests/t/00startstop.t: -------------------------------------------------------------------------------- 1 | 2 | # Simple test to start and stop the product. 3 | 4 | use Test; 5 | BEGIN { plan tests => 4 }; 6 | use runproduct; 7 | ok(1); # If we made it this far, we're ok. 8 | 9 | my $p = new runproduct('basic'); 10 | 11 | ok(defined $p, 1, "Failed to initialize product runner"); 12 | ok($p->start(), 1, "Failed to start product"); 13 | sleep(1); 14 | ok($p->stop(), 1, "Failed to stop product"); 15 | 16 | -------------------------------------------------------------------------------- /tests/t/01login.t: -------------------------------------------------------------------------------- 1 | 2 | # Simple test to login to the server. 3 | 4 | use Test; 5 | BEGIN { plan tests => 7 }; 6 | use runproduct; 7 | use Ham::APRS::IS; 8 | ok(1); # If we made it this far, we're ok. 9 | 10 | # initialize the product runner using the basic configuration 11 | my $p = new runproduct('basic'); 12 | 13 | # start the software up 14 | ok(defined $p, 1, "Failed to initialize product runner"); 15 | ok($p->start(), 1, "Failed to start product"); 16 | 17 | # connect to the server 18 | my $is = new Ham::APRS::IS("localhost:55152", "OH7LZB"); 19 | ok(defined $is, 1, "Failed to initialize Ham::APRS::IS"); 20 | 21 | my $ret = $is->connect('retryuntil' => 8); 22 | ok($ret, 1, "Failed to connect to the server: " . $is->{'error'}); 23 | 24 | $ret = $is->disconnect(); 25 | ok($ret, 1, "Failed to disconnect from the server: " . $is->{'error'}); 26 | 27 | ok($p->stop(), 1, "Failed to stop product"); 28 | 29 | -------------------------------------------------------------------------------- /tests/t/02login-reject.t: -------------------------------------------------------------------------------- 1 | 2 | # Test login rejections 3 | 4 | use Test; 5 | BEGIN { plan tests => 5 }; 6 | use runproduct; 7 | use Ham::APRS::IS; 8 | ok(1); # If we made it this far, we're ok. 9 | 10 | # initialize the product runner using the basic configuration 11 | my $p = new runproduct('basic'); 12 | 13 | # start the software up 14 | ok(defined $p, 1, "Failed to initialize product runner"); 15 | ok($p->start(), 1, "Failed to start product"); 16 | 17 | # Callsigns which should be rejected on login 18 | my @reject_login = ( 19 | # static, built-in list 20 | "pass", 21 | # dynamic configurable rejections, with wildcards 22 | "LOGINA", 23 | "loginb", 24 | "0prrej", 25 | "mi0rej", 26 | "sufrej" 27 | ); 28 | 29 | my $fail = 0; 30 | 31 | # try to connect with each of the callsigns 32 | foreach my $s (@reject_login) { 33 | # connect to the server 34 | my $is = new Ham::APRS::IS("localhost:55152", $s); 35 | if (!$is) { 36 | warn "Failed to initialize Ham::APRS::IS: $s\n"; 37 | $fail++; 38 | } 39 | 40 | my $ret = $is->connect('retryuntil' => 8); 41 | if ($ret) { 42 | warn "Succeeded to connect to the server as '$s': " . $is->{'error'}; 43 | $fail++; 44 | } else { 45 | #warn "OK, rejected: " . $is->{'error'} . "\n"; 46 | } 47 | 48 | $is->disconnect(); 49 | } 50 | 51 | ok($fail, 0, "Server accepted logins which it should have rejected."); 52 | 53 | ok($p->stop(), 1, "Failed to stop product"); 54 | 55 | -------------------------------------------------------------------------------- /tests/t/12quirks-mode.t: -------------------------------------------------------------------------------- 1 | # 2 | # Some bad packets which should be allowed in quirks mode 3 | # (client has known bad-and-unmaintained software) 4 | # 5 | 6 | use Test; 7 | BEGIN { plan tests => 6 + 5 + 3 }; 8 | use runproduct; 9 | use istest; 10 | use Ham::APRS::IS; 11 | use Time::HiRes qw(sleep); 12 | 13 | my $p = new runproduct('basic'); 14 | 15 | ok(defined $p, 1, "Failed to initialize product runner"); 16 | ok($p->start(), 1, "Failed to start product"); 17 | 18 | $Ham::APRS::IS::aprs_appid = 'HR-IXPWINDi-123'; 19 | 20 | my $login = "N5CAL-1"; 21 | my $server_call = "TESTING"; 22 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login); 23 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 24 | 25 | my $i_rx = new Ham::APRS::IS("localhost:55152", "N5CAL-2"); 26 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 27 | 28 | my $ret; 29 | $ret = $i_tx->connect('retryuntil' => 8); 30 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 31 | 32 | $ret = $i_rx->connect('retryuntil' => 8); 33 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 34 | 35 | # do the actual tests 36 | my($tx, $rx); 37 | 38 | # spaces in front of srccall 39 | $rx = "SRC>DST,qAR,$login:>spaces in beginning"; 40 | $tx = " $rx"; 41 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 42 | 43 | # nulls in front of srccall 44 | $rx = "SRC>DST,qAR,$login:>nulls in beginning"; 45 | $tx = "\x00$rx"; 46 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 47 | 48 | # spaces in end of srccall 49 | my $tail = ">DST,qAR,$login:>spaces in end of srccall"; 50 | $tx = "SRC $tail"; 51 | $rx = "SRC$tail"; 52 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 53 | 54 | # spaces in path callsign 55 | $tail = ":>spaces in end of path element"; 56 | $tx = "SRC>DST,qAR,$login $tail"; 57 | $rx = "SRC>DST,qAR,$login$tail"; 58 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 59 | 60 | # should drop with bad srccall 61 | $tx = 'default setting is>DST,qAR,FOO:bad srccall'; 62 | istest::should_drop(\&ok, $i_tx, $i_rx, 63 | $tx, 64 | "SRC>LONG:dummy", 1); # will pass (helper packet) 65 | 66 | 67 | # disconnect 68 | 69 | $ret = $i_tx->disconnect(); 70 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 71 | 72 | $ret = $i_tx->disconnect(); 73 | ok($ret, 1, "Failed to disconnect from the server: " . $i_tx->{'error'}); 74 | 75 | # stop 76 | 77 | ok($p->stop(), 1, "Failed to stop product"); 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /tests/t/13thirdparty.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test 3rd-party packet header parsing and filtering 3 | # 4 | 5 | use Test; 6 | BEGIN { plan tests => 6 + 10 + 3 }; 7 | use runproduct; 8 | use istest; 9 | use Ham::APRS::IS; 10 | 11 | my $p = new runproduct('basic'); 12 | 13 | ok(defined $p, 1, "Failed to initialize product runner"); 14 | ok($p->start(), 1, "Failed to start product"); 15 | 16 | my $login = "N5CAL-1"; 17 | my $server_call = "TESTING"; 18 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login); 19 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 20 | 21 | # First filter is for uncompressed packet, second for compressed, 22 | # third for mic-e, fourth for prefix filter test. 23 | # The first and last one also test upper-case letters as filter keys. 24 | my $i_rx = new Ham::APRS::IS("localhost:55581", "N5CAL-2", 25 | 'filter' => 'r/60.228/24.8495/5 p/OG p/OI p/K b/BB7LZB'); 26 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 27 | 28 | my $ret; 29 | $ret = $i_tx->connect('retryuntil' => 8); 30 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 31 | 32 | $ret = $i_rx->connect('retryuntil' => 8); 33 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 34 | 35 | ############### tests ######################## 36 | 37 | # check that 3rd party packet matches a P filter 38 | $tx = $rx = "UU0AA>TEST,qAR,IGATE:}OG7LZB>DST,NET,GATE*:>should pass 3rd party inner prefix"; 39 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 40 | 41 | # check that 3rd party packet matches a B filter 42 | $tx = $rx = "UU0AA>TEST,qAR,IGATE:}BB7LZB>DST,NET,GATE*:>should pass 3rd party inner prefix with b filter"; 43 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 44 | 45 | # check that 3rd party packet matches a P filter 46 | $tx = $rx = "OI0AA>TEST,qAR,IGATE:}ZZ7LZB>DST,NET,GATE*:>should pass 3rd party outer prefix"; 47 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 48 | 49 | # check that 3rd party packet matches a coordinate filter 50 | $tx = $rx = "UU1AA>TEST,qAR,IGATE:}OF7LZB>DST,NET,GATE:!6013.69NR02450.97E&"; 51 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 52 | 53 | # check that a nested 3rd party packet matches a coordinate filter 54 | $tx = $rx = "UU1AA>TEST,qAR,IGATE:}OF7LZB>DST,NET,GATE:}OF7LZC>DST,NET2,GATE2:!6013.69NR02450.97E&"; 55 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 56 | 57 | # check that a really nested 3rd party packet matches a coordinate filter 58 | $tx = $rx = "UU1AA>TEST,qAR,IGATE:}OF7LZB>DST,NET,GATE:}OF7LZC>DST,NET2,GATE2:}OF7LZD>DST,NET2,GATE2:}OF7LZE>DST,NET2,GATE2:!6013.69NR02450.97E&"; 59 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 60 | 61 | # Invalid 3rd-party header, does not have 2 hops 62 | $tx = "KG4LAA>APWW08,TCPIP*,qAS,n3wax:}KB3ONM>APK102,KV3B-1,WIDE1,WIDE2,KV3B-1,WIDE1,KG4LAA*::N3HEV-9 :ack26"; 63 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $tx); 64 | 65 | # Invalid 3rd-party header, does not have 2 hops 66 | $tx = "KG4LAA>APWW08,TCPIP*,qAS,n3wax:}KB3ONM>APK102,KV3B-1::N3HEV-9 :ack26"; 67 | $helper = "K2SRC>APRS,qAR,$login:>should pass 2"; 68 | istest::should_drop(\&ok, $i_tx, $i_rx, $tx, $helper); 69 | 70 | # Invalid 3rd-party header, only has the } marker, but is passed still 71 | # (needs to have }...>...: to be treated as 3rd-party) 72 | $tx = "K3SRC>APWW08,TCPIP*,qAR,IGA:}blah blah"; 73 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $tx); 74 | 75 | # Valid third-party header with a used digi 76 | $tx = "K3SRC>APWW09,TCPIP*,qAR,IGA:}KB3ONM>APK102,KV3B-1,FOO,BAR*:should pass"; 77 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $tx); 78 | 79 | # disconnect ################################# 80 | 81 | $ret = $i_rx->disconnect(); 82 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 83 | 84 | $ret = $i_tx->disconnect(); 85 | ok($ret, 1, "Failed to disconnect from the server: " . $i_tx->{'error'}); 86 | 87 | # stop 88 | 89 | ok($p->stop(), 1, "Failed to stop product"); 90 | 91 | -------------------------------------------------------------------------------- /tests/t/22path-trace.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test path tracing (qAI) 3 | # 4 | 5 | use Test; 6 | BEGIN { plan tests => 7 + 2 + 3 }; 7 | use runproduct; 8 | use istest; 9 | use Ham::APRS::IS; 10 | 11 | ok(1); # If we made it this far, we're ok. 12 | 13 | my $p = new runproduct('basic'); 14 | 15 | ok(defined $p, 1, "Failed to initialize product runner"); 16 | ok($p->start(), 1, "Failed to start product"); 17 | 18 | $tx_call = "OHCALL-1"; 19 | $server_call = "TESTING"; 20 | $i_tx = new Ham::APRS::IS("localhost:55152", $tx_call); 21 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 22 | 23 | $i_rx = new Ham::APRS::IS("localhost:55152", "OHCALL-2"); 24 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 25 | 26 | # connect 27 | 28 | $ret = $i_tx->connect('retryuntil' => 8); 29 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 30 | $ret = $i_rx->connect('retryuntil' => 8); 31 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 32 | 33 | # 34 | # Test that qAI packets. 35 | # 36 | # If trace is on, the q construct is qAI, or the FROMCALL is on the server's trace list: 37 | # { 38 | # (1) If the packet is from a verified port where the login is not found after the q construct: 39 | # Append ,login 40 | # (2) else if the packet is from an outbound connection 41 | # Append ,IPADDR 42 | # 43 | # (3) Append ,SERVERLOGIN 44 | # } 45 | #} 46 | # 47 | 48 | # (1) 49 | my $position = "!6028.52N/02505.61E# Testing"; 50 | istest::txrx(\&ok, $i_tx, $i_rx, 51 | "$tx_call>DST,DIGI,qAI,BLAH:$position", 52 | "$tx_call>DST,DIGI,qAI,BLAH,$tx_call,$server_call:$position"); 53 | 54 | # (3) 55 | $position .= '.'; # make it unique to pass dupe check 56 | istest::txrx(\&ok, $i_tx, $i_rx, 57 | "$tx_call>DST,DIGI,qAI,$tx_call:$position", 58 | "$tx_call>DST,DIGI,qAI,$tx_call,$server_call:$position"); 59 | 60 | # disconnect 61 | 62 | $ret = $i_rx->disconnect(); 63 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 64 | $ret = $i_tx->disconnect(); 65 | ok($ret, 1, "Failed to disconnect from the server: " . $i_tx->{'error'}); 66 | 67 | # stop 68 | 69 | ok($p->stop(), 1, "Failed to stop product"); 70 | 71 | -------------------------------------------------------------------------------- /tests/t/22qconstr-uplink.t: -------------------------------------------------------------------------------- 1 | # 2 | # Uplink Q construct tests 3 | # 4 | 5 | use Test; 6 | BEGIN { plan tests => 2 + 6 + 2 + 3 + 2 }; 7 | use runproduct; 8 | use istest; 9 | use Ham::APRS::IS; 10 | use Ham::APRS::IS_Fake; 11 | ok(1); # If we made it this far, we're ok. 12 | 13 | my $upstream_call = 'FAKEUP'; 14 | 15 | my $iss1 = new Ham::APRS::IS_Fake('127.0.0.1:54153', $upstream_call); 16 | ok(defined $iss1, 1, "Test failed to initialize listening server socket (IPv4)"); 17 | $iss1->bind_and_listen(); 18 | 19 | my $p = new runproduct('uplinks'); 20 | 21 | ok(defined $p, 1, "Failed to initialize product runner"); 22 | ok($p->start(), 1, "Failed to start product"); 23 | 24 | my $login = "N5CAL-1"; 25 | my $server_call = "TESTING"; 26 | my $i_rx = new Ham::APRS::IS("127.0.0.1:55152", $login); 27 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 28 | 29 | my $is1 = $iss1->accept(); 30 | ok(defined $is1, (1), "Failed to accept connection 1 from server"); 31 | ok($iss1->process_login($is1), 'ok', "Failed to accept login 1 from server"); 32 | 33 | my $ret; 34 | $ret = $i_rx->connect('retryuntil' => 8); 35 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 36 | 37 | # do the actual tests 38 | 39 | # 40 | # If trace is on, the q construct is qAI, or the FROMCALL is on the server's trace list: 41 | # { 42 | # If the packet is from a verified port where the login is not found after the q construct: 43 | # (1) Append ,login 44 | # else if the packet is from an outbound connection 45 | # (2) Append ,IPADDR 46 | # 47 | # (3) Append ,SERVERLOGIN 48 | # } 49 | # 50 | 51 | #warn "doing test 1\n"; 52 | 53 | # (2): 54 | istest::txrx(\&ok, $is1, $i_rx, 55 | "SRC>DST,DIGI1,DIGI2*,qAI,FOOBA,BLAA:testing qAI (1)", 56 | "SRC>DST,DIGI1,DIGI2*,qAI,FOOBA,BLAA,$upstream_call,$server_call:testing qAI (1)"); 57 | 58 | # unbind the IPv4 server and create IPv6 server 59 | $iss1->unbind(); 60 | 61 | #warn "switching to ipv6\n"; 62 | 63 | my $iss6 = new Ham::APRS::IS_Fake('[::]:54153', $upstream_call); 64 | ok(defined $iss6, 1, "Test failed to initialize listening server socket on IPv6"); 65 | $iss6->bind_and_listen(); 66 | 67 | #warn "disconnecting uplink 1\n"; 68 | 69 | $is1->disconnect(); 70 | 71 | #warn "accepting ipv6 connect\n"; 72 | 73 | my $is6 = $iss6->accept(); 74 | ok(defined $is6, (1), "Failed to accept connection ipv6 from server"); 75 | ok($iss6->process_login($is6), 'ok', "Failed to accept login ipv6 from server"); 76 | 77 | # (2), ipv6: 78 | istest::txrx(\&ok, $is6, $i_rx, 79 | "SRC>DST,DIGI1,DIGI2*,qAI,FOOBAR,BLAA:testing qAI (ipv6)", 80 | "SRC>DST,DIGI1,DIGI2*,qAI,FOOBAR,BLAA,$upstream_call,$server_call:testing qAI (ipv6)"); 81 | 82 | # disconnect 83 | $ret = $i_rx->disconnect(); 84 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 85 | 86 | ok($p->stop(), 1, "Failed to stop product"); 87 | 88 | -------------------------------------------------------------------------------- /tests/t/23qconstr-long.t: -------------------------------------------------------------------------------- 1 | # 2 | # Uplink Q construct tests 3 | # 4 | 5 | use Test; 6 | 7 | use runproduct; 8 | use istest; 9 | use Ham::APRS::IS; 10 | use Ham::APRS::IS_Fake; 11 | 12 | my @packets; 13 | 14 | BEGIN { 15 | for (my $i = 1; $i < 500; $i++) { 16 | my $data = "packet $i"; 17 | my $elems = int($i/7); 18 | my $append = $i % 7; 19 | #warn "$data elems $elems append $append\n"; 20 | my @l; 21 | for (my $d = 0; $d < $elems; $d++) { 22 | push @l, sprintf("SRV%03d", $d); 23 | } 24 | if ($append) { 25 | push @l, "D" x $append; 26 | } elsif (@l) { 27 | $l[$#l] =~ s/(.)$/,Y/; 28 | } 29 | #warn join(',', @l) . "\n"; 30 | my $packet = "SRC>DST,qAI," . join(',', @l) . ":$data"; 31 | $packet =~ s/,,/,/g; 32 | #warn "$packet\n"; 33 | push @packets, $packet; 34 | } 35 | 36 | plan tests => 7 + ($#packets+1) + 5; 37 | }; 38 | 39 | ok(1); # If we made it this far, we're ok. 40 | 41 | my $iss6 = new Ham::APRS::IS_Fake('[::]:54153', 'FAKEIS'); 42 | ok(defined $iss6, 1, "Test failed to initialize listening server socket on IPv6"); 43 | $iss6->bind_and_listen(); 44 | 45 | my $p = new runproduct('uplinks'); 46 | 47 | ok(defined $p, 1, "Failed to initialize product runner"); 48 | ok($p->start(), 1, "Failed to start product"); 49 | 50 | my $login = "N5CAL-1"; 51 | my $server_call = "TESTING"; 52 | my $i_rx = new Ham::APRS::IS("localhost:55152", $login); 53 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 54 | 55 | my $is6 = $iss6->accept(); 56 | ok(defined $is6, (1), "Failed to accept connection ipv6 from server"); 57 | ok($iss6->process_login($is6), 'ok', "Failed to accept login ipv6 from server"); 58 | 59 | my $ret; 60 | $ret = $i_rx->connect('retryuntil' => 8); 61 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 62 | 63 | # do the actual tests 64 | 65 | my $maxlen = 509; 66 | $maxlen = 510 if (defined $ENV{'TEST_PRODUCT'} && $ENV{'TEST_PRODUCT'} =~ /^javap/); 67 | 68 | foreach my $packet (@packets) { 69 | my $expect = $packet; 70 | $expect =~ s/:/,FAKEIS,$server_call:/; 71 | #warn "tx: $packet\n"; 72 | if (length($expect) > $maxlen) { 73 | $is6->sendline($packet); 74 | ok(1); 75 | } else { 76 | istest::txrx(\&ok, $is6, $i_rx, 77 | $packet, 78 | $expect); 79 | } 80 | } 81 | 82 | my $read1; 83 | $read1 = $is6->getline_noncomment(1); 84 | ok($read1, undef, "Ouch, received data from read-only upstream connection ipv6"); 85 | $read1 = $i_rx->getline_noncomment(1); 86 | ok($read1, undef, "Ouch, received unexpected data from full stream"); 87 | 88 | $ret = $i_rx->disconnect(); 89 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 90 | 91 | ok($p->stop(), 1, "Failed to stop product"); 92 | 93 | -------------------------------------------------------------------------------- /tests/t/27digipath-long.t: -------------------------------------------------------------------------------- 1 | # 2 | # Uplink Q construct tests 3 | # 4 | 5 | use Test; 6 | 7 | use runproduct; 8 | use istest; 9 | use Ham::APRS::IS; 10 | use Ham::APRS::IS_Fake; 11 | 12 | my @packets; 13 | 14 | BEGIN { 15 | for (my $i = 0; $i < 500; $i++) { 16 | my $data = "packet $i"; 17 | my $elems = int($i/7); 18 | my $append = $i % 7; 19 | #warn "$data elems $elems append $append\n"; 20 | my @l; 21 | for (my $d = 0; $d < $elems; $d++) { 22 | push @l, sprintf("DIG%03d", $d); 23 | } 24 | if ($append) { 25 | push @l, "D" x $append; 26 | } elsif (@l) { 27 | $l[$#l] .= '*'; 28 | } 29 | #warn join(',', @l) . "\n"; 30 | my $packet = "SRC>DST," . join(',', @l) . ",qAI,IGATE:$data"; 31 | $packet =~ s/,,/,/; 32 | #warn "$packet\n"; 33 | push @packets, $packet; 34 | } 35 | 36 | plan tests => 8 + ($#packets+1) + 2 + 2; 37 | }; 38 | 39 | ok(1); # If we made it this far, we're ok. 40 | 41 | #my $iss1 = new Ham::APRS::IS_Fake('127.0.0.1:54153', 'FAKE4'); 42 | #ok(defined $iss1, 1, "Test failed to initialize listening server socket"); 43 | #$iss1->bind_and_listen(); 44 | 45 | my $iss6 = new Ham::APRS::IS_Fake('[::]:54153', 'FAKE6'); 46 | ok(defined $iss6, 1, "Test failed to initialize listening server socket on IPv6"); 47 | $iss6->bind_and_listen(); 48 | 49 | my $p = new runproduct('uplinks'); 50 | 51 | ok(defined $p, 1, "Failed to initialize product runner"); 52 | ok($p->start(), 1, "Failed to start product"); 53 | 54 | my $login = "N5CAL-1"; 55 | my $server_call = "TESTING"; 56 | my $i_rx = new Ham::APRS::IS("localhost:55152", $login); 57 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 58 | 59 | my $is6 = $iss6->accept(); 60 | ok(defined $is6, (1), "Failed to accept connection ipv6 from server"); 61 | ok($iss6->process_login($is6), 'ok', "Failed to accept login ipv6 from server"); 62 | 63 | my $ret; 64 | $ret = $i_rx->connect('retryuntil' => 8); 65 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 66 | 67 | # do the actual tests 68 | 69 | my $maxlen = 509; 70 | $maxlen = 510 if (defined $ENV{'TEST_PRODUCT'} && $ENV{'TEST_PRODUCT'} =~ /^javap/); 71 | 72 | # (1): 73 | foreach my $packet (@packets) { 74 | my $expect = $packet; 75 | $expect =~ s/IGATE:/IGATE,FAKE6,$server_call:/; 76 | if (length($expect) > $maxlen) { 77 | my $res = $is6->sendline($packet); 78 | if ($res) { 79 | ok(1); 80 | } else { 81 | ok(undef, 1, "Ouch, write to server failed"); 82 | } 83 | } else { 84 | istest::txrx(\&ok, $is6, $i_rx, 85 | $packet, 86 | $expect); 87 | } 88 | } 89 | 90 | my $read1; 91 | $read1 = $is6->getline_noncomment(1); 92 | ok($read1, undef, "Ouch, received data from read-only upstream connection ipv6"); 93 | #$read1 = $is1->getline_noncomment(1); 94 | #ok($read1, undef, "Ouch, received data from read-only upstream connection 1"); 95 | $read1 = $i_rx->getline_noncomment(1); 96 | ok($read1, undef, "Ouch, received unexpected data from full stream"); 97 | 98 | $ret = $i_rx->disconnect(); 99 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 100 | 101 | ok($p->stop(), 1, "Failed to stop product"); 102 | 103 | -------------------------------------------------------------------------------- /tests/t/31parser-nmea.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test NMEA packet parsing and filtering capability: 3 | # 4 | # verify that these packets pass based on a filter which matches 5 | # the positions in these packets 6 | # 7 | 8 | use Test; 9 | BEGIN { plan tests => 12 }; 10 | use runproduct; 11 | use istest; 12 | use Ham::APRS::IS; 13 | ok(1); # If we made it this far, we're ok. 14 | 15 | my $p = new runproduct('basic'); 16 | 17 | ok(defined $p, 1, "Failed to initialize product runner"); 18 | ok($p->start(), 1, "Failed to start product"); 19 | 20 | my $login = "N5CAL-1"; 21 | my $server_call = "TESTING"; 22 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login); 23 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 24 | 25 | my $i_rx = new Ham::APRS::IS("localhost:55581", "N5CAL-2", 26 | 'filter' => 'r/-38.5452/-58.7366/1 s/->' # GPRMC 27 | ); 28 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 29 | 30 | my $ret; 31 | $ret = $i_tx->connect('retryuntil' => 8); 32 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 33 | 34 | $ret = $i_rx->connect('retryuntil' => 8); 35 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 36 | 37 | # do the actual tests 38 | 39 | my($tx, $rx); 40 | 41 | # 8: should pass filter by position 42 | $tx = "OH1XYZ>GPSMW:\$GPRMC,184649,A,3832.7107,S,05844.1957,W,0.000,0.0,130909,4.5,W*62"; 43 | $rx = "OH1XYZ>GPSMW,qAS,$login:\$GPRMC,184649,A,3832.7107,S,05844.1957,W,0.000,0.0,130909,4.5,W*62"; 44 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 45 | 46 | # 9: should pass filter by car symbol 47 | $tx = "OH2XYZ>GPSMV:\$GPRMC,212052,A,4609.1157,N,12258.8145,W,0.168,13.4,130909,17.9,E*6B"; 48 | $rx = "OH2XYZ>GPSMV,qAS,$login:\$GPRMC,212052,A,4609.1157,N,12258.8145,W,0.168,13.4,130909,17.9,E*6B"; 49 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 50 | 51 | # 10: should drop, wrong position and symbol 52 | $tx = "OH3XYZ>GPSMW:\$GPRMC,212052,A,4609.1157,N,12258.8145,W,0.168,13.4,130909,17.9,E*6B"; 53 | my $dummy = "OH4XYZ>GPSMV:\$GPRMC,212052,A,4609.1157,N,12258.8145,W,0.168,13.4,130909,17.9,E*6B"; 54 | istest::should_drop(\&ok, $i_tx, $i_rx, 55 | $tx, # should drop 56 | $dummy, 1, 1); # will pass (helper packet) 57 | 58 | # 11: should drop, invalid char in coordinates 59 | $tx = "OH4XYZ>GPSMW:\$GPRMO,182051.\xf000,A,6039.8655,N,01708.3799,E,20.07,243.41,070313,,,A*5A"; 60 | $dummy = "OH5XYZ>GPSMV:\$GPRMC,212052,A,4609.1157,N,12258.8145,W,0.168,13.4,130909,17.9,E*6B"; 61 | istest::should_drop(\&ok, $i_tx, $i_rx, 62 | $tx, # should drop 63 | $dummy, 1, 1); # will pass (helper packet) 64 | 65 | # stop 66 | 67 | ok($p->stop(), 1, "Failed to stop product"); 68 | 69 | -------------------------------------------------------------------------------- /tests/t/32filter-negative.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test negative filters 3 | # 4 | 5 | use Test; 6 | BEGIN { plan tests => 10 }; 7 | use runproduct; 8 | use istest; 9 | use Ham::APRS::IS; 10 | 11 | my $p = new runproduct('basic'); 12 | 13 | ok(defined $p, 1, "Failed to initialize product runner"); 14 | ok($p->start(), 1, "Failed to start product"); 15 | 16 | my $login = "N5CAL-1"; 17 | my $server_call = "TESTING"; 18 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login); 19 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 20 | 21 | # allow range, then drop using a buddy filter 22 | my $i_rx = new Ham::APRS::IS("localhost:55581", "N5CAL-2", 23 | 'filter' => 'r/60.4752/25.0947/100 -b/D3NY'); 24 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 25 | 26 | my $ret; 27 | $ret = $i_tx->connect('retryuntil' => 8); 28 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 29 | 30 | $ret = $i_rx->connect('retryuntil' => 8); 31 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 32 | 33 | # do the actual tests 34 | 35 | my $srccall = "OH2RDP-1"; 36 | my $dstcall = "BEACON-15"; 37 | my($tx, $rx, $helper); 38 | 39 | # check that the r/ range filter passes packets within the range 40 | $tx = "$srccall>$dstcall,OH2RDG*,WIDE:!6028.51N/02505.68E# should pass"; 41 | $rx = "$srccall>$dstcall,OH2RDG*,WIDE,qAS,$login:!6028.51N/02505.68E# should pass"; 42 | istest::txrx(\&ok, $i_tx, $i_rx, $tx, $rx); 43 | 44 | # disconnect 45 | 46 | $ret = $i_rx->disconnect(); 47 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 48 | 49 | $ret = $i_tx->disconnect(); 50 | ok($ret, 1, "Failed to disconnect from the server: " . $i_tx->{'error'}); 51 | 52 | # stop 53 | 54 | ok($p->stop(), 1, "Failed to stop product"); 55 | 56 | -------------------------------------------------------------------------------- /tests/t/35filter-digi-entry-unproto.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test filters for digipeater, entrycall and unproto 3 | # 4 | 5 | use Test; 6 | BEGIN { plan tests => 6 + 4 + 2 + 2 + 1 }; 7 | use runproduct; 8 | use istest; 9 | use Ham::APRS::IS; 10 | use Time::HiRes qw(sleep); 11 | 12 | my $p = new runproduct('basic'); 13 | 14 | ok(defined $p, 1, "Failed to initialize product runner"); 15 | ok($p->start(), 1, "Failed to start product"); 16 | 17 | my $login = "N5CAL-1"; 18 | my $server_call = "TESTING"; 19 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login); 20 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 21 | 22 | # allow range, then drop using a buddy filter 23 | my $i_rx = new Ham::APRS::IS("localhost:55581", "N5CAL-2"); 24 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 25 | 26 | my $ret; 27 | $ret = $i_tx->connect('retryuntil' => 8); 28 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 29 | 30 | $ret = $i_rx->connect('retryuntil' => 8); 31 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 32 | 33 | my($tx, $rx, $helper); 34 | 35 | # filter for digi callsign 36 | $i_rx->sendline("#filter d/OH7RDA/OH2RDS/OH9*"); 37 | sleep(0.5); 38 | 39 | # The filter must only match when the path element is USED* 40 | $pass = "SRC1>APRS,OH7RDA*,qAR,$login:!6016.66NT02515.26E# pass digi1"; 41 | $drop = "SRC2>APRS,OH7RDA,qAR,$login:!6016.66NT02515.26E# pass digi1 unused"; 42 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 43 | 44 | # The * in a later digi also marks the previous ones as used. 45 | $pass = "SRC3>APRS,OH7RDA,OH8RDA*,qAR,$login:!6016.66NT02515.26E# pass digi2"; 46 | $drop = "SRC4>APRS,OH7RDA,OH8RDA,qAR,$login:!6016.66NT02515.26E# pass digi2 unused"; 47 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 48 | 49 | # do the same with a wildcarded filter entry 50 | $pass = "SRC5>APRS,OH9RDA*,qAR,$login:!6016.66NT02515.26E# pass digi1"; 51 | $drop = "SRC6>APRS,OH9RDA,qAR,$login:!6016.66NT02515.26E# pass digi1 unused"; 52 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 53 | 54 | $pass = "SRC7>APRS,OH9RDA,OH8RDA*,qAR,$login:!6016.66NT02515.26E# pass digi2"; 55 | $drop = "SRC8>APRS,OH9RDA,OH8RDA,qAR,$login:!6016.66NT02515.26E# pass digi2 unused"; 56 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 57 | 58 | # filter for q construct entrycall 59 | $i_rx->sendline("#filter e/$login/IGA*"); 60 | sleep(0.5); 61 | 62 | $pass = "ESRC1>APRS,qAR,$login:!6016.66NT02515.26E# pass entrycall"; 63 | $drop = "ESRC2>APRS,qAR,BAA:!6016.66NT02515.26E# drop entrycall"; 64 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 65 | 66 | # try with wildcard, and the matching call as non-first entry 67 | $pass = "ESRC3>APRS,qAR,IGATE1:!6016.66NT02515.26E# pass entrycall wildcard"; 68 | $drop = "ESRC4>APRS,qAR,FOO,IGATE2:!6016.66NT02515.26E# drop entrycall wildcard"; 69 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 70 | 71 | # filter for unproto address 72 | $i_rx->sendline("#filter u/ALTNET/APZ*"); 73 | sleep(0.5); 74 | 75 | $pass = "USRC1>ALTNET,qAR,$login:!6016.66NT02515.26E# pass unproto"; 76 | $drop = "USRC2>ALTBLA,qAR,$login:!6016.66NT02515.26E# drop unproto"; 77 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 78 | 79 | $pass = "USRC3>APZMDR,qAR,$login:!6016.66NT02515.26E# pass unproto wildcard"; 80 | $drop = "USRC4>APXMDR,qAR,$login:!6016.66NT02515.26E# drop unproto wildcard"; 81 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 82 | 83 | 84 | # stop 85 | 86 | ok($p->stop(), 1, "Failed to stop product"); 87 | 88 | -------------------------------------------------------------------------------- /tests/t/37filter-qconstr.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test filters for area (a/), my position range (m/), friend pos range (f/) 3 | # 4 | 5 | use Test; 6 | BEGIN { plan tests => 6 + 4 + 1 }; 7 | use runproduct; 8 | use istest; 9 | use Ham::APRS::IS; 10 | use Time::HiRes qw(sleep); 11 | 12 | my $p = new runproduct('basic'); 13 | 14 | ok(defined $p, 1, "Failed to initialize product runner"); 15 | ok($p->start(), 1, "Failed to start product"); 16 | 17 | my $login = "N5CAL-1"; 18 | my $rxlogin = "N5CAL-2"; 19 | my $server_call = "TESTING"; 20 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login); 21 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 22 | 23 | my $i_rx = new Ham::APRS::IS("localhost:55581", $rxlogin); 24 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 25 | 26 | my $ret; 27 | $ret = $i_tx->connect('retryuntil' => 8); 28 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 29 | 30 | $ret = $i_rx->connect('retryuntil' => 8); 31 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 32 | 33 | my($tx, $rx, $helper); 34 | 35 | ############################### 36 | 37 | # filter for Q construct 38 | $i_rx->sendline("#filter q/RUo"); 39 | sleep(0.5); 40 | 41 | $pass = "PU2TFG-15>APN390,qAR,$login:!2334.10S/04719.70W# pass qAR"; 42 | $drop = "$login>APN390:!2334.10N/04719.70W# drop qAC"; 43 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 44 | 45 | # this drop is outside documentation, but it is dropped 46 | $pass = "PU2TFG-1>APN390,qAU,$login:!2334.10S/04719.70W# pass qAU"; 47 | $drop = "$login>APN390,qAU,$login:!2334.10N/04719.70W# drop qAU)"; 48 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 49 | 50 | $pass = "PU2TFG-2>APN390,qAo,SRVR1:!2334.10S/04719.70W# pass qAo"; 51 | $drop = "DR0P>APN39X,qAr,CL1ENT:!2334.10N/04719.70W# drop qAr)"; 52 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 53 | 54 | $pass = "PU2TFG-2>APN390,qAo,SRVR1:!2334.10S/04719.70W# pass qAo"; 55 | $drop = "DR0P-2>APN39,qAO,CL1ENT:!2334.10N/04719.70W# drop qAr)"; 56 | istest::should_drop(\&ok, $i_tx, $i_rx, $drop, $pass); 57 | 58 | # stop 59 | 60 | ok($p->stop(), 1, "Failed to stop product"); 61 | 62 | -------------------------------------------------------------------------------- /tests/t/38filter-verylong.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test filters for parsing of greatly repeated filters (single filter gets extended) 3 | # TODO: run this threaded, with multiple copies, has a better chance of finding 4 | # a bug. 5 | # 6 | 7 | my $buddyrounds = 300; 8 | 9 | use Test; 10 | BEGIN { plan tests => 2 + 3*300 + 1 }; 11 | use runproduct; 12 | use istest; 13 | use Ham::APRS::IS; 14 | use Time::HiRes qw(sleep); 15 | 16 | my $p = new runproduct('basic'); 17 | 18 | ok(defined $p, 1, "Failed to initialize product runner"); 19 | ok($p->start(), 1, "Failed to start product"); 20 | 21 | my $login = "N5CAL-1"; 22 | my $server_call = "TESTING"; 23 | my $i_tx; 24 | 25 | for (my $i = 0; $i < $buddyrounds; $i++) { 26 | my @filters; 27 | for (my $b = 0; $b < $i; $b++) { 28 | push @filters, 'b/BUDDYYYY' . $b . '-42'; 29 | } 30 | $i_tx = new Ham::APRS::IS("localhost:55580", $login, 'filter' => join(' ', @filters), 'nopass' => 1); 31 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 32 | $ret = $i_tx->connect('retryuntil' => 8); 33 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 34 | $ret = $i_tx->disconnect(); 35 | ok($ret, 1, "Failed to disconnect from the server: " . $i_tx->{'error'}); 36 | } 37 | 38 | 39 | ok($p->stop(), 1, "Failed to stop product"); 40 | -------------------------------------------------------------------------------- /tests/t/50disc-blobs.t: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Test disconnection in the middle of transmitting a packet 4 | # 5 | 6 | use Test; 7 | BEGIN { plan tests => 12 }; 8 | use runproduct; 9 | use istest; 10 | use Ham::APRS::IS; 11 | 12 | my $p = new runproduct('basic'); 13 | 14 | ok(defined $p, 1, "Failed to initialize product runner"); 15 | ok($p->start(), 1, "Failed to start product"); 16 | 17 | my $login_tx = "N0GAT"; 18 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login_tx); 19 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 20 | 21 | my $login_rx = "N1GAT"; 22 | my $i_rx = new Ham::APRS::IS("localhost:55152", $login_rx); 23 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 24 | 25 | my $ret; 26 | $ret = $i_tx->connect('retryuntil' => 8); 27 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 28 | 29 | $ret = $i_rx->connect('retryuntil' => 8); 30 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 31 | 32 | # Send a packet halfway and disconnect 33 | my $tx = "M0SRC>APRS,OH2RDG*,WIDE,qAR,$login_tx:!6028.51N/02505.68E#this packet trunca"; 34 | $i_tx->sendline($tx, 1); 35 | sleep(1); 36 | $i_tx->disconnect(); 37 | 38 | my $r = $i_rx->getline_noncomment(2); 39 | ok($r, undef, "Got message without CRLF truncated due to disconnect"); 40 | 41 | # connect again 42 | 43 | $ret = $i_tx->connect('retryuntil' => 8); 44 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 45 | 46 | # 47 | ############################################ 48 | # Do a blob transmit test 49 | # 50 | 51 | my $blobsize = 32*1024; 52 | $tx = ''; 53 | my $txl = 0; 54 | my $txn = 0; 55 | my @l = (); 56 | 57 | while ($txl < $blobsize) { 58 | $s = "M" . ($txn + 10) . ">APRS,qAR,$login_tx:!6028.51N/02505.68E#should pass blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa END"; 59 | push @l, $s; 60 | $tx .= $s . "\r\n";; 61 | $txl += length($s); 62 | $txn++; 63 | } 64 | 65 | warn " blobsize $txl ($blobsize) with $txn packets\n"; 66 | 67 | my $sent = $i_tx->sendline($tx, 1); 68 | ok($sent, 1, "Failed to transmit blob of $txl bytes"); 69 | 70 | my $rxl = 0; 71 | my $rxn = 0; 72 | while (my $rx = $i_rx->getline_noncomment(1)) { 73 | if ($rx ne $l[$rxn]) { 74 | warn "got wrong packet: $rx\n"; 75 | } 76 | $rxn++; 77 | $rxl += length($rx); 78 | } 79 | 80 | ok($rxn, $txn, "Received wrong number of lines from blob"); 81 | ok($rxl, $txl, "Received wrong number of bytes from blob"); 82 | 83 | 84 | # stop 85 | 86 | ok($p->stop(), 1, "Failed to stop product"); 87 | 88 | -------------------------------------------------------------------------------- /tests/t/51load.t: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Test heavier packet load 4 | # 5 | 6 | use Test; 7 | BEGIN { plan tests => 10 }; 8 | use runproduct; 9 | use Ham::APRS::IS; 10 | use Time::HiRes qw( sleep time ); 11 | 12 | my $p = new runproduct('basic'); 13 | 14 | ok(defined $p, 1, "Failed to initialize product runner"); 15 | ok($p->start(), 1, "Failed to start product"); 16 | 17 | my $login_tx = "N0GAT"; 18 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login_tx); 19 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 20 | 21 | my $login_rx = "N1GAT"; 22 | my $i_rx = new Ham::APRS::IS("localhost:55152", $login_rx); 23 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 24 | 25 | $ret = $i_rx->connect('retryuntil' => 8); 26 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 27 | 28 | my $ret; 29 | $ret = $i_tx->connect('retryuntil' => 8); 30 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 31 | 32 | # let it get started 33 | sleep(0.5); 34 | 35 | ############################################ 36 | 37 | my $flush_interval = 500; 38 | $flush_interval = 300; 39 | my $bytelimit = 20*1024*1024; 40 | my $window = 64*1024; 41 | #my $window = 2*1024; 42 | my $outstanding = 0; 43 | my $txn = 0; 44 | my $rxn = 0; 45 | my $txl = 0; 46 | my $rxl = 0; 47 | my @l = (); 48 | my $txq = ''; 49 | my $txq_l = 0; 50 | 51 | my $start_t = time(); 52 | 53 | while ($txl < $bytelimit) { 54 | $s = "M" . ($txn % 10000 + 10) . ">APRS,qAR,$login_tx:!6028.51N/02505.68E# packet $txn blaa blaa blaa blaa END"; 55 | push @l, $s; 56 | $s .= "\r\n"; 57 | my $sl = length($s); 58 | $txl += $sl; 59 | $txq_l += $sl; 60 | $txq .= $s; 61 | $txn++; 62 | 63 | if ($txq_l >= $flush_interval) { 64 | $i_tx->sendline($txq, 1); 65 | $outstanding += $txq_l; 66 | $txq_l = 0; 67 | $txq = ''; 68 | } 69 | 70 | while (($outstanding > $window) && (my $rx = $i_rx->getline_noncomment(1))) { 71 | my $exp = shift @l; 72 | if ($exp ne $rx) { 73 | warn "Ouch, received wrong packet: $rx\nExpected: $exp"; 74 | } 75 | my $rx_l = length($rx) + 2; 76 | $outstanding -= $rx_l; 77 | $rxn++; 78 | $rxl += $rx_l; 79 | } 80 | } 81 | 82 | if ($txq_l > 0) { 83 | warn "flushing the rest\n"; 84 | $i_tx->sendline($txq, 1); 85 | $outstanding += $txq_l; 86 | $txq_l = 0; 87 | $txq = 0; 88 | } 89 | 90 | warn "reading the rest, have received $rxn packets, sent $txn\n"; 91 | while (($outstanding > 0) && (my $rx = $i_rx->getline_noncomment(0.5))) { 92 | my $exp = shift @l; 93 | if ($exp ne $rx) { 94 | warn "Ouch, received wrong packet: $rx\n"; 95 | } 96 | my $rx_l = length($rx) + 2; 97 | $outstanding -= $rx_l; 98 | $rxn++; 99 | $rxl += $rx_l; 100 | } 101 | warn "after reading the rest, have received $rxn packets, sent $txn, outstanding $outstanding bytes\n"; 102 | 103 | $end_t = time(); 104 | $dur_t = $end_t - $start_t; 105 | 106 | warn sprintf("took %.3f seconds, %.0f packets/sec\n", $dur_t, $rxn / $dur_t); 107 | 108 | ok($rxn, $txn, "Received wrong number of lines from blob"); 109 | ok($rxl, $txl, "Received wrong number of bytes from blob"); 110 | ok($outstanding, 0, "There are outstanding bytes in the server after timeout"); 111 | 112 | # stop 113 | 114 | ok($p->stop(), 1, "Failed to stop product"); 115 | 116 | -------------------------------------------------------------------------------- /tests/t/51uplink-reconnect.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test uplink reconnection after flooding the upstream obuf 3 | # 4 | 5 | use Test; 6 | 7 | my $rounds = 3; 8 | 9 | BEGIN { plan tests => 8 + 3*4 }; 10 | use runproduct; 11 | use istest; 12 | use Ham::APRS::IS; 13 | use Ham::APRS::IS_Fake; 14 | ok(1); # If we made it this far, we're ok. 15 | 16 | my $upstream_call = 'FAKEUP'; 17 | 18 | my $iss1 = new Ham::APRS::IS_Fake('127.0.0.1:10153', $upstream_call); 19 | ok(defined $iss1, 1, "Test failed to initialize listening server socket (IPv4)"); 20 | $iss1->bind_and_listen(); 21 | 22 | my $p = new runproduct('basic'); 23 | 24 | ok(defined $p, 1, "Failed to initialize product runner"); 25 | ok($p->start(), 1, "Failed to start product"); 26 | 27 | my $login = "N5CAL-1"; 28 | my $server_call = "TESTING"; 29 | my $i_tx = new Ham::APRS::IS("localhost:55152", $login); 30 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 31 | 32 | #warn "accepting\n"; 33 | 34 | my $ret; 35 | $ret = $i_tx->connect('retryuntil' => 8); 36 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 37 | 38 | my $prefix = 'FL'; 39 | for (my $round = 0; $round < $rounds; $round += 1) { 40 | #warn "round $round\n"; 41 | 42 | my $is1 = $iss1->accept(); 43 | ok(defined $is1, (1), "Round $round: Failed to accept connection 1 from server"); 44 | ok($iss1->process_login($is1), 'ok', "Round $round: Failed to accept login 1 from server"); 45 | 46 | my $obuf = ''; 47 | for (my $txn = 0; $txn < 10000; $txn++) { 48 | $obuf .= $prefix . ($txn % 10000 + 10) . ">APRS,qAR,OH9XYZ-5:!6028.51N/02505.68E# round $round packet $txn blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa blaa END\r\n"; 49 | my $len = length($obuf); 50 | if ($len > 32768) { 51 | #warn "writing $len\n"; 52 | $i_tx->sendline($obuf, 1); 53 | $obuf = ''; 54 | } 55 | } 56 | if ($obuf) { 57 | $i_tx->sendline($obuf, 1); 58 | $obuf = ''; 59 | } 60 | 61 | my $is2 = $iss1->accept(); 62 | ok(defined $is2, (1), "Round $round: Failed to accept reconnection from server"); 63 | ok($iss1->process_login($is2), 'ok', "Round $round: Failed to accept reconnection login from server"); 64 | 65 | $is1->disconnect(); 66 | $is1 = $is1; 67 | } 68 | 69 | # disconnect 70 | $ret = $i_tx->disconnect(); 71 | ok($ret, 1, "Failed to disconnect from the server: " . $i_tx->{'error'}); 72 | 73 | ok($p->stop(), 1, "Failed to stop product"); 74 | 75 | -------------------------------------------------------------------------------- /tests/t/60udp-client.t: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Test UDP client functions 4 | # 5 | 6 | use Test; 7 | BEGIN { plan tests => 6 + 1 + 3 }; 8 | use runproduct; 9 | use istest; 10 | use Ham::APRS::IS; 11 | use Time::HiRes qw(sleep); 12 | 13 | my $p = new runproduct('basic'); 14 | 15 | ok(defined $p, 1, "Failed to initialize product runner"); 16 | ok($p->start(), 1, "Failed to start product"); 17 | 18 | my $login = "N5CAL-1"; 19 | my $server_call = "TESTING"; 20 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login); 21 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 22 | 23 | # an udp client 24 | my $i_rx = new Ham::APRS::IS("localhost:55152", "N5CAL-2", 'udp' => 51342); 25 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 26 | 27 | my $ret; 28 | $ret = $i_tx->connect('retryuntil' => 8); 29 | ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); 30 | 31 | $ret = $i_rx->connect('retryuntil' => 8); 32 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 33 | 34 | # test ########################### 35 | 36 | my $data = "udp packet content"; 37 | 38 | istest::txrx(\&ok, $i_tx, $i_rx, 39 | "SRC>DST,qAR,$login:$data", 40 | "SRC>DST,qAR,$login:$data"); 41 | 42 | 43 | # disconnect #################### 44 | 45 | $ret = $i_rx->disconnect(); 46 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 47 | 48 | $ret = $i_tx->disconnect(); 49 | ok($ret, 1, "Failed to disconnect from the server: " . $i_tx->{'error'}); 50 | 51 | # stop 52 | 53 | ok($p->stop(), 1, "Failed to stop product"); 54 | 55 | -------------------------------------------------------------------------------- /tests/t/61udp-peer.t: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Test UDP core peers. While at it, check that the 4 | # basic loop prevention rules work. 5 | # 6 | # 1) Traffic from upstreams goes to clients. 7 | # 2) Traffic from core peers goes to clients. 8 | # 3) Traffic does not pass between peers and upstreams. 9 | # 4) Traffic from clients goes to core peers and upstreams. 10 | # 11 | # The testing order is selected so that the last packet proves 12 | # the previous ones were not transitted to the wrong sockets. 13 | # 14 | 15 | use Test; 16 | BEGIN { plan tests => 9 + 5 + 2 }; 17 | use runproduct; 18 | use istest; 19 | use Ham::APRS::IS; 20 | use Ham::APRS::IS_Fake; 21 | use Ham::APRS::IS_Fake_UDP; 22 | use Time::HiRes qw(sleep); 23 | 24 | my $p = new runproduct('basic'); 25 | 26 | # UDP peer socket 27 | my $udp = new Ham::APRS::IS_Fake_UDP('127.0.0.1:16405', 'N0UDP'); 28 | ok(defined $udp, (1), "Failed to set up UDP server socket"); 29 | ok($udp->bind_and_listen(), 1, "Failed to bind UDP server socket"); 30 | $udp->set_destination('127.0.0.1:16404'); 31 | 32 | # TCP server socket 33 | my $upstream_call = 'FAKEUP'; 34 | my $iss1 = new Ham::APRS::IS_Fake('127.0.0.1:10153', $upstream_call); 35 | ok(defined $iss1, 1, "Test failed to initialize listening server socket (IPv4)"); 36 | $iss1->bind_and_listen(); 37 | 38 | # Start software 39 | ok(defined $p, 1, "Failed to initialize product runner"); 40 | ok($p->start(), 1, "Failed to start product"); 41 | 42 | # Set up client and connect 43 | my $login = "N5CAL-1"; 44 | my $server_call = "TESTING"; 45 | my $client = new Ham::APRS::IS("localhost:55152", $login); 46 | ok(defined $client, 1, "Failed to initialize Ham::APRS::IS"); 47 | 48 | my $ret; 49 | $ret = $client->connect('retryuntil' => 8); 50 | ok($ret, 1, "Failed to connect to the server: " . $client->{'error'}); 51 | 52 | # Accept connection from server 53 | my $is1 = $iss1->accept(); 54 | ok(defined $is1, (1), "Failed to accept connection 1 from server"); 55 | ok($iss1->process_login($is1), 'ok', "Failed to accept login 1 from server"); 56 | 57 | # test ########################### 58 | 59 | my $s; 60 | 61 | # 1) from upstream to client 62 | $s = "SRC>DST,qAR,IGATE:upstream to client"; 63 | istest::txrx(\&ok, $is1, $client, $s, $s); 64 | 65 | # 2) from core peer to client, with trace 66 | $s = "SRC>DST,qAI,IGATE,SRV1:core peer to client, 1"; 67 | my $rx = "SRC>DST,qAI,IGATE,SRV1,TESTING:core peer to client, 1"; 68 | istest::txrx(\&ok, $udp, $client, $s, $rx); 69 | 70 | # 3) from core peer to client 71 | $s = "SRC>DST,qAR,IGATE:core peer to client, 2"; 72 | istest::txrx(\&ok, $udp, $client, $s, $s); 73 | 74 | # 4) from client to peers and upstreams 75 | $s = "SRC>DST,qAR,IGATE:from client"; 76 | istest::txrx(\&ok, $client, $is1, $s, $s); 77 | 78 | my $r = $udp->getline(); 79 | ok($r, $s, "Failed to pass packet from client to UDP peer"); 80 | 81 | # disconnect #################### 82 | 83 | $ret = $client->disconnect(); 84 | ok($ret, 1, "Failed to disconnect from the server: " . $client->{'error'}); 85 | 86 | # stop 87 | 88 | ok($p->stop(), 1, "Failed to stop product"); 89 | 90 | -------------------------------------------------------------------------------- /tests/t/62http-client.t: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Test HTTP services 4 | # 5 | 6 | use Test; 7 | BEGIN { plan tests => 4 + 2 + 2 }; 8 | use runproduct; 9 | use istest; 10 | use Ham::APRS::IS; 11 | use LWP; 12 | use LWP::UserAgent; 13 | use HTTP::Request::Common; 14 | 15 | my $p = new runproduct('basic'); 16 | 17 | ok(defined $p, 1, "Failed to initialize product runner"); 18 | ok($p->start(), 1, "Failed to start product"); 19 | 20 | my $login = "N5CAL-1"; 21 | my $i_rx = new Ham::APRS::IS("localhost:55152", $login); 22 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 23 | 24 | $ret = $i_rx->connect('retryuntil' => 8); 25 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 26 | 27 | # set up http client ############ 28 | 29 | my $ua = LWP::UserAgent->new; 30 | 31 | $ua->agent( 32 | agent => "httpaprstester/1.0", 33 | timeout => 10, 34 | max_redirect => 0, 35 | ); 36 | 37 | # test ########################### 38 | 39 | my $data = "TEST>HTAPRS,TCPIP*:>http packet content"; 40 | my $out = "TEST>HTAPRS,TCPIP*,qAC,TESTING:>http packet content"; 41 | my $post = "user TEST pass 29939 vers httpaprstester 1.0\r\n" 42 | . "$data\r\n"; 43 | my $url = "http://127.0.0.1:55080/"; 44 | #$url = "http://he.fi/"; 45 | my $req = HTTP::Request::Common::POST($url); 46 | #$req->header('Accept', 'text/plain'); 47 | $req->header('Accept-type', 'text/plain'); 48 | $req->header('Content-type', 'application/octet-stream'); 49 | $req->header('Content-Length', length($post)); 50 | $req->content($post); 51 | 52 | my $res = $ua->simple_request($req); 53 | 54 | ok($res->code, 200, "HTTP POST packet upload returned wrong response code, message: " . $res->message); 55 | 56 | #istest::txrx(\&ok, $i_tx, $i_rx, 57 | # "SRC>DST,qAR,$login:$data", 58 | # "SRC>DST,qAR,$login:$data"); 59 | 60 | my $l = $i_rx->getline_noncomment(); 61 | ok($l, $out, "Got wrong line through HTTP uploading"); 62 | 63 | # disconnect #################### 64 | 65 | $ret = $i_rx->disconnect(); 66 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 67 | 68 | # stop 69 | 70 | ok($p->stop(), 1, "Failed to stop product"); 71 | 72 | -------------------------------------------------------------------------------- /tests/t/63udp-submit.t: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Test UDP submit functions 4 | # 5 | 6 | use Test; 7 | BEGIN { plan tests => 5 + 2 + 2 }; 8 | use runproduct; 9 | use istest; 10 | use Ham::APRS::IS; 11 | use IO::Socket::INET; 12 | use Time::HiRes qw(sleep); 13 | 14 | my $p = new runproduct('basic'); 15 | 16 | ok(defined $p, 1, "Failed to initialize product runner"); 17 | ok($p->start(), 1, "Failed to start product"); 18 | 19 | my $login = "OH0CAL-3"; 20 | my $server_call = "TESTING"; 21 | 22 | # full feed client 23 | my $i_rx = new Ham::APRS::IS("localhost:55152", "OH0CAL-2"); 24 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 25 | 26 | $ret = $i_rx->connect('retryuntil' => 8); 27 | ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); 28 | 29 | # udp setup 30 | $usock = IO::Socket::INET->new(Proto => 'udp', PeerPort => 55080, PeerAddr => "127.0.0.1"); 31 | ok(defined $usock, (1), "Failed to set up an UDP client socket"); 32 | 33 | # test ########################### 34 | 35 | my $data = "TEST>UDAPRS,TCPIP*:>udp packet content"; 36 | my $out = "TEST>UDAPRS,TCPIP*,qAU,TESTING:>udp packet content"; 37 | my $post = "user TEST pass 29939 vers udpaprstester 1.0\r\n" 38 | . "$data"; 39 | 40 | $usock->send($post); 41 | 42 | my $l = $i_rx->getline_noncomment(); 43 | ok($l, $out, "Got wrong line when sent using UDP"); 44 | 45 | # test rejections 46 | 47 | my $fail = 0; 48 | my(@udp_rej) = ( 49 | [ 'UDROP', '', "UDROP>UDAPRS:>udp packet rejected, no passcode" ], 50 | [ 'UDROP', 123, "UDROP>UDAPRS:>udp packet rejected, bad passcode" ], 51 | [ 'UDROPASDSJSKD', 13293, "UDROPASDSJSKD>UDAPRS:>udp packet rejected, too long login callsign" ], 52 | [ 'UDRO/OH', 29929, "UDROP>UDAPRS:>udp packet rejected, invalid login callsign" ], 53 | ); 54 | 55 | for my $rej (@udp_rej) { 56 | my($src, $passcode, $rejdata) = @{ $rej }; 57 | my $rej_post = "user $src pass $passcode vers udpaprstester 1.0\r\n" 58 | . "$rejdata"; 59 | 60 | $usock->send($rej_post); 61 | } 62 | 63 | # another success after the failures 64 | 65 | $data = "TEST>UDAPRS:>udp packet content 2"; 66 | $out = "TEST>UDAPRS,TCPIP*,qAU,TESTING:>udp packet content 2"; 67 | $post = "user TEST pass 29939 vers udpaprstester 1.0\r\n" 68 | . "$data\r\n"; 69 | 70 | $usock->send($post); 71 | 72 | $l = $i_rx->getline_noncomment(); 73 | ok($l, $out, "Got wrong line when sent using UDP"); 74 | 75 | 76 | # disconnect #################### 77 | 78 | $ret = $i_rx->disconnect(); 79 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 80 | 81 | # stop 82 | 83 | ok($p->stop(), 1, "Failed to stop product"); 84 | 85 | -------------------------------------------------------------------------------- /tests/t/65dupeclient.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test client receiving dupes 3 | # 4 | 5 | use Test; 6 | BEGIN { plan tests => 2 + 3*2 + 3 + 3 + 1 }; 7 | use runproduct; 8 | use istest; 9 | use Ham::APRS::IS; 10 | 11 | # initialize and start the product up 12 | my $p = new runproduct('basic'); 13 | 14 | ok(defined $p, 1, "Failed to initialize product runner"); 15 | ok($p->start(), 1, "Failed to start product"); 16 | 17 | # create two connections to the server: 18 | # - i_tx for transmitting packets 19 | # - i_rx for receiving packets 20 | # - i_dup for receiving dupes 21 | 22 | my $login = "N5CAL-1"; 23 | my $server_call = "TESTING"; 24 | my $i_tx = new Ham::APRS::IS("localhost:55580", $login); 25 | ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); 26 | 27 | my $i_rx = new Ham::APRS::IS("localhost:55152", "N5CAL-2"); 28 | ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); 29 | 30 | my $i_dup = new Ham::APRS::IS("localhost:55153", "DUPS"); 31 | ok(defined $i_dup, 1, "Failed to initialize Ham::APRS::IS"); 32 | 33 | my $ret; 34 | $ret = $i_tx->connect('retryuntil' => 8); 35 | ok($ret, 1, "Failed to connect to the server filter port: " . $i_tx->{'error'}); 36 | $ret = $i_rx->connect('retryuntil' => 8); 37 | ok($ret, 1, "Failed to connect to the server full feed: " . $i_rx->{'error'}); 38 | $ret = $i_dup->connect('retryuntil' => 8); 39 | ok($ret, 1, "Failed to connect to the server dupe port: " . $i_dup->{'error'}); 40 | 41 | # do the actual tests 42 | 43 | my $data = 'foo'; 44 | 45 | # Send an unique packet 46 | # 47 | # istest::txrx( $okref, $transmit_connection, $receive_connection, 48 | # $transmitted_line, $expected_line_on_rx ); 49 | # 50 | my $l = "SRC>DST,qAR,$login:$data"; 51 | istest::txrx(\&ok, $i_tx, $i_rx, $l, $l); 52 | 53 | # Send again, and check that it only comes out on the dupe socket 54 | istest::should_drop(\&ok, $i_tx, $i_rx, 55 | $l, # should drop 56 | "SRC>DST:dummy", 1); # will pass (helper packet) 57 | 58 | my $d = $i_dup->getline_noncomment(); 59 | ok($d, "dup\t$l", "Got wrong line on dupe socket"); 60 | 61 | # disconnect 62 | 63 | $ret = $i_rx->disconnect(); 64 | ok($ret, 1, "Failed to disconnect from the server: " . $i_rx->{'error'}); 65 | $ret = $i_tx->disconnect(); 66 | ok($ret, 1, "Failed to disconnect from the server: " . $i_tx->{'error'}); 67 | $ret = $i_dup->disconnect(); 68 | ok($ret, 1, "Failed to disconnect from the server: " . $i_dup->{'error'}); 69 | 70 | # stop 71 | 72 | ok($p->stop(), 1, "Failed to stop product"); 73 | 74 | -------------------------------------------------------------------------------- /tests/t/66http-status.t: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Test HTTP status service, only on aprsc 4 | # 5 | 6 | use Test; 7 | 8 | BEGIN { 9 | plan tests => (!defined $ENV{'TEST_PRODUCT'} || $ENV{'TEST_PRODUCT'} =~ /aprsc/) ? 2 + 16 + 1 : 0; 10 | }; 11 | 12 | if (defined $ENV{'TEST_PRODUCT'} && $ENV{'TEST_PRODUCT'} !~ /aprsc/) { 13 | exit(0); 14 | } 15 | 16 | use runproduct; 17 | use LWP; 18 | use LWP::UserAgent; 19 | use HTTP::Request::Common; 20 | use JSON::XS; 21 | 22 | # set up the JSON module 23 | my $json = new JSON::XS; 24 | 25 | if (!$json) { 26 | die "JSON loading failed"; 27 | } 28 | 29 | $json->latin1(0); 30 | $json->ascii(1); 31 | $json->utf8(0); 32 | 33 | my $p = new runproduct('basic'); 34 | 35 | ok(defined $p, 1, "Failed to initialize product runner"); 36 | ok($p->start(), 1, "Failed to start product"); 37 | 38 | # set up http client ############ 39 | 40 | my $ua = LWP::UserAgent->new; 41 | 42 | $ua->agent( 43 | agent => "httpaprstester/1.0", 44 | timeout => 10, 45 | max_redirect => 0, 46 | ); 47 | 48 | # test ########################### 49 | 50 | my($req, $res, $j); 51 | 52 | # transfer-encodings that the client cant decode (compression) 53 | my $can_accept = HTTP::Message::decodable; 54 | 55 | $req = HTTP::Request::Common::GET("http://127.0.0.1:55501/"); 56 | $res = $ua->simple_request($req); 57 | ok($res->code, 200, "HTTP GET of status server front page returned wrong response code, message: " . $res->message); 58 | 59 | $req->header('Accept-Encoding', $can_accept); 60 | $res = $ua->simple_request($req); 61 | ok($res->code, 200, "HTTP GET (compressed) of status server front page returned wrong response code, message: " . $res->message); 62 | 63 | 64 | $req = HTTP::Request::Common::GET("http://127.0.0.1:55501/status.json"); 65 | $res = $ua->simple_request($req); 66 | ok($res->code, 200, "HTTP GET of status server status.json returned wrong response code, message: " . $res->message); 67 | $j = $json->decode($res->decoded_content(charset => 'none')); 68 | ok(defined $j, 1, "JSON decoding of status.json failed"); 69 | ok(defined $j->{'server'}, 1, "status.json does not define 'server'"); 70 | 71 | $req->header('Accept-Encoding', $can_accept); 72 | $res = $ua->simple_request($req); 73 | ok($res->code, 200, "HTTP GET (compressed) of status server status.json returned wrong response code, message: " . $res->message); 74 | $j = $json->decode($res->decoded_content(charset => 'none')); 75 | ok(defined $j, 1, "JSON decoding of (compressed) status.json failed"); 76 | ok(defined $j->{'server'}, 1, "status.json (compressed) does not define 'server'"); 77 | 78 | 79 | $req = HTTP::Request::Common::GET("http://127.0.0.1:55501/counterdata?totals.tcp_bytes_rx"); 80 | $res = $ua->simple_request($req); 81 | ok($res->code, 200, "HTTP GET of status server /counterdata?totals.tcp_bytes_rx returned wrong response code, message: " . $res->message); 82 | $j = $json->decode($res->decoded_content(charset => 'none')); 83 | ok(defined $j, 1, "JSON decoding of /counterdata?totals.tcp_bytes_rx failed"); 84 | ok(defined $j->{'interval'}, 1, "counterdata json does not define 'interval'"); 85 | ok(defined $j->{'values'}, 1, "counterdata json does not define 'values'"); 86 | 87 | $req->header('Accept-Encoding', $can_accept); 88 | $res = $ua->simple_request($req); 89 | ok($res->code, 200, "HTTP GET (compressed) of status server /counterdata?totals.tcp_bytes_rx returned wrong response code, message: " . $res->message); 90 | $j = $json->decode($res->decoded_content(charset => 'none')); 91 | ok(defined $j, 1, "JSON decoding of (compressed) /counterdata?totals.tcp_bytes_rx failed"); 92 | ok(defined $j->{'interval'}, 1, "counterdata json (compressed) does not define 'interval'"); 93 | ok(defined $j->{'values'}, 1, "counterdata json (compressed) does not define 'values'"); 94 | 95 | 96 | # stop 97 | 98 | ok($p->stop(), 1, "Failed to stop product"); 99 | 100 | -------------------------------------------------------------------------------- /tests/tls-openssl.conf: -------------------------------------------------------------------------------- 1 | 2 | HOME = . 3 | # RANDFILE = .rnd 4 | 5 | 6 | # Extra OBJECT IDENTIFIER info: 7 | oid_section = new_oids 8 | 9 | [ new_oids ] 10 | # Amateur radio callsign OID used by LotW 11 | callSign = 1.3.6.1.4.1.12348.1.1 12 | 13 | [ ca ] 14 | default_ca = testca 15 | 16 | [ testca ] 17 | 18 | dir = ./tls-testca # Where everything is kept 19 | certs = $dir/certs # Where the issued certs are kept 20 | crl_dir = $dir/crl # Where the issued crl are kept 21 | database = $dir/index.txt # database index file. 22 | unique_subject = no # Set to 'no' to allow creation of 23 | # several ctificates with same subject. 24 | new_certs_dir = $dir/newcerts # default place for new certs. 25 | 26 | certificate = $dir/cacert.pem # The CA certificate 27 | serial = $dir/serial # The current serial number 28 | private_key = $dir/private/cakey.pem # The private key 29 | 30 | x509_extensions = aprsc_cert 31 | default_md = sha256 32 | 33 | # Comment out the following two lines for the "traditional" 34 | # (and highly broken) format. 35 | name_opt = ca_default # Subject Name options 36 | cert_opt = ca_default # Certificate field options 37 | 38 | 39 | policy = policy_client 40 | 41 | [ policy_client ] 42 | callSign = optional 43 | organizationName = optional 44 | organizationalUnitName = optional 45 | commonName = optional 46 | emailAddress = optional 47 | 48 | [ req ] 49 | distinguished_name = req_distinguished_name 50 | default_bits = 2048 51 | 52 | 53 | [ req_distinguished_name ] 54 | callSign = Callsign 55 | callSign_max = 12 56 | 57 | commonName = Common Name (e.g. server FQDN or YOUR name) 58 | commonName_max = 64 59 | 60 | emailAddress = Email Address 61 | emailAddress_max = 64 62 | 63 | [ aprsc_cert ] 64 | basicConstraints=CA:FALSE 65 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 66 | subjectKeyIdentifier=hash 67 | authorityKeyIdentifier=keyid:always,issuer 68 | extendedKeyUsage = clientAuth,serverAuth 69 | 70 | 71 | -------------------------------------------------------------------------------- /tools/Markdown.license: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004, John Gruber 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | * Neither the name "Markdown" nor the names of its contributors may 17 | be used to endorse or promote products derived from this software 18 | without specific prior written permission. 19 | 20 | This software is provided by the copyright holders and contributors "as 21 | is" and any express or implied warranties, including, but not limited 22 | to, the implied warranties of merchantability and fitness for a 23 | particular purpose are disclaimed. In no event shall the copyright owner 24 | or contributors be liable for any direct, indirect, incidental, special, 25 | exemplary, or consequential damages (including, but not limited to, 26 | procurement of substitute goods or services; loss of use, data, or 27 | profits; or business interruption) however caused and on any theory of 28 | liability, whether in contract, strict liability, or tort (including 29 | negligence or otherwise) arising in any way out of the use of this 30 | software, even if advised of the possibility of such damage. 31 | -------------------------------------------------------------------------------- /tools/aprs-is-multirx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | $VERSION = 'APRS-IS-MULTIRX 1.0'; 4 | 5 | use POSIX; 6 | use IO::Multiplex; 7 | use Ham::APRS::IS; 8 | 9 | select STDOUT; $| = 1; 10 | 11 | my $quit = 0; 12 | my $APRSIS; 13 | 14 | my $mycall = $ARGV[0] . '-'; 15 | my $server = $ARGV[1]; 16 | #my $filter = 'p/OH2R'; 17 | #my $filter = 'p/OH'; 18 | my $filter = 'p/OH2R -p/OH2 p/OH '; 19 | $filter = 'p/OH/OI/OJ/OF'; 20 | 21 | my @APRS = (); 22 | 23 | my $n = $ARGV[2]; 24 | 25 | my $MUX = new IO::Multiplex; 26 | 27 | warn "Connecting to $server as $mycall with $n sockets\n"; 28 | 29 | my $call_n = 0; 30 | 31 | sub connect_new() 32 | { 33 | my $call = sprintf("%s%d",$mycall,$call_n); 34 | $call_n++; 35 | $APRSIS = Ham::APRS::IS->new($server, $call); 36 | 37 | if (!defined($APRSIS)) { 38 | printf "Failed to init Ham::APRS::IS socket!\n"; 39 | exit 4; 40 | } 41 | 42 | if (!$APRSIS->connect()) { 43 | printf "Failed to connect\n"; 44 | exit 4; 45 | } 46 | 47 | $MUX->add( $APRSIS->{'sock'} ); 48 | } 49 | 50 | foreach my $i (1 .. $n) { 51 | connect_new(); 52 | } 53 | $MUX->set_callback_object(__PACKAGE__); 54 | $MUX->loop(); 55 | 56 | # 57 | #my $now = time; 58 | #my $last = $now + 60*60; 59 | #local $line; 60 | # 61 | #while (1) { 62 | # $now = time; 63 | # foreach my $A (@APRS) { 64 | # $line = $A->getline; 65 | # } 66 | #} 67 | exit 0; 68 | 69 | 70 | sub mux_input { 71 | my $package = shift; 72 | my $mux = shift; 73 | my $fh = shift; 74 | my $data = shift; 75 | 76 | $$data = ''; 77 | } 78 | 79 | my $eofs = 0; 80 | 81 | sub mux_eof { 82 | my $package = shift; 83 | my $mux = shift; 84 | my $fh = shift; 85 | $eofs++; 86 | warn "eof $eofs\n"; 87 | 88 | $MUX->close($fh); 89 | 90 | connect_new(); 91 | } 92 | 93 | -------------------------------------------------------------------------------- /tools/aprsis-http-post: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use LWP; 4 | use LWP::UserAgent; 5 | use HTTP::Request::Common; 6 | 7 | my $ua = LWP::UserAgent->new; 8 | 9 | $ua->agent( 10 | agent => "httpaprstester/1.0", 11 | timeout => 10, 12 | max_redirect => 0, 13 | ); 14 | 15 | # test ########################### 16 | 17 | my $data = "TEST>HTAPRS,TCPIP*:>http packet content"; 18 | my $out = "TEST>HTAPRS,TCPIP*,qAC,TESTING:>http packet content"; 19 | my $post = "user TEST pass 29939 vers httpaprstester 1.0\r\n" 20 | . "$data\r\n"; 21 | my $url = "http://127.0.0.1:55080/"; 22 | my $req = HTTP::Request::Common::POST($url); 23 | $req->header('Accept-Type', 'text/plain'); # wat? 24 | #$req->header('Accept', 'text/plain'); # this is what HTTP uses, really 25 | $req->header('Content-Type', 'application/octet-stream'); 26 | $req->header('Content-Length', length($post)); 27 | $req->content($post); 28 | 29 | my $res = $ua->simple_request($req); 30 | 31 | print $res->code . " " . $res->message . "\n\n"; 32 | print $res->content . "\n"; 33 | 34 | --------------------------------------------------------------------------------