mirror of https://github.com/wb2osz/direwolf.git
826 lines
19 KiB
C
826 lines
19 KiB
C
|
//
|
||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||
|
//
|
||
|
// Copyright (C) 2013 John Langner, WB2OSZ
|
||
|
//
|
||
|
// This program is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU General Public License as published by
|
||
|
// the Free Software Foundation, either version 2 of the License, or
|
||
|
// (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU General Public License
|
||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
//
|
||
|
|
||
|
|
||
|
/*------------------------------------------------------------------
|
||
|
*
|
||
|
* Module: aclients.c
|
||
|
*
|
||
|
* Purpose: Multiple concurrent APRS clients for comparing
|
||
|
* TNC demodulator performance.
|
||
|
*
|
||
|
* Description: Establish connection with multiple servers and
|
||
|
* compare results side by side.
|
||
|
*
|
||
|
* Usage: aclients 8000=AGWPE 8002=DireWolf COM1=D710A
|
||
|
*
|
||
|
* This will connect to multiple physical or virtual
|
||
|
* TNCs, read packets from them, and display results.
|
||
|
*
|
||
|
*---------------------------------------------------------------*/
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Native Windows: Use the Winsock interface.
|
||
|
* Linux: Use the BSD socket interface.
|
||
|
* Cygwin: Can use either one.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#if __WIN32__
|
||
|
|
||
|
#include <winsock2.h>
|
||
|
// default is 0x0400
|
||
|
#undef _WIN32_WINNT
|
||
|
#define _WIN32_WINNT 0x0501 /* Minimum OS version is XP. */
|
||
|
#include <ws2tcpip.h>
|
||
|
#else
|
||
|
//#define __USE_XOPEN2KXSI 1
|
||
|
//#define __USE_XOPEN 1
|
||
|
#include <stdlib.h>
|
||
|
#include <netdb.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <netinet/tcp.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <termios.h>
|
||
|
#include <sys/errno.h>
|
||
|
#endif
|
||
|
|
||
|
#include <unistd.h>
|
||
|
#include <stdio.h>
|
||
|
#include <assert.h>
|
||
|
#include <ctype.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
|
||
|
#include "direwolf.h"
|
||
|
#include "ax25_pad.h"
|
||
|
#include "textcolor.h"
|
||
|
#include "version.h"
|
||
|
|
||
|
|
||
|
struct agwpe_s {
|
||
|
short portx; /* 0 for first, 1 for second, etc. */
|
||
|
short port_hi_reserved;
|
||
|
short kind_lo; /* message type */
|
||
|
short kind_hi;
|
||
|
char call_from[10];
|
||
|
char call_to[10];
|
||
|
int data_len; /* Number of data bytes following. */
|
||
|
int user_reserved;
|
||
|
};
|
||
|
|
||
|
|
||
|
#if __WIN32__
|
||
|
static unsigned __stdcall client_thread_net (void *arg);
|
||
|
static unsigned __stdcall client_thread_serial (void *arg);
|
||
|
#else
|
||
|
static void * client_thread_net (void *arg);
|
||
|
static void * client_thread_serial (void *arg);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Convert Internet address to text.
|
||
|
* Can't use InetNtop because it is supported only on Windows Vista and later.
|
||
|
*/
|
||
|
|
||
|
static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t StringBufSize)
|
||
|
{
|
||
|
struct sockaddr_in *sa4;
|
||
|
struct sockaddr_in6 *sa6;
|
||
|
|
||
|
switch (Family) {
|
||
|
case AF_INET:
|
||
|
sa4 = (struct sockaddr_in *)pAddr;
|
||
|
#if __WIN32__
|
||
|
sprintf (pStringBuf, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
|
||
|
sa4->sin_addr.S_un.S_un_b.s_b2,
|
||
|
sa4->sin_addr.S_un.S_un_b.s_b3,
|
||
|
sa4->sin_addr.S_un.S_un_b.s_b4);
|
||
|
#else
|
||
|
inet_ntop (AF_INET, &(sa4->sin_addr), pStringBuf, StringBufSize);
|
||
|
#endif
|
||
|
break;
|
||
|
case AF_INET6:
|
||
|
sa6 = (struct sockaddr_in6 *)pAddr;
|
||
|
#if __WIN32__
|
||
|
sprintf (pStringBuf, "%x:%x:%x:%x:%x:%x:%x:%x",
|
||
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]),
|
||
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
|
||
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]),
|
||
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[3]),
|
||
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[4]),
|
||
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[5]),
|
||
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[6]),
|
||
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[7]));
|
||
|
#else
|
||
|
inet_ntop (AF_INET6, &(sa6->sin6_addr), pStringBuf, StringBufSize);
|
||
|
#endif
|
||
|
break;
|
||
|
default:
|
||
|
sprintf (pStringBuf, "Invalid address family!");
|
||
|
}
|
||
|
assert (strlen(pStringBuf) < StringBufSize);
|
||
|
return pStringBuf;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*------------------------------------------------------------------
|
||
|
*
|
||
|
* Name: main
|
||
|
*
|
||
|
* Purpose: Start up multiple client threads listening to different
|
||
|
* TNCs. Print packets. Tally up statistics.
|
||
|
*
|
||
|
* Usage: aclients 8000=AGWPE 8002=DireWolf COM1=D710A
|
||
|
*
|
||
|
* Each command line argument is TCP port number or a
|
||
|
* serial port name. Follow by = and a text description
|
||
|
* of what is connected.
|
||
|
*
|
||
|
* For now, everything is assumed to be on localhost.
|
||
|
* Maybe someday we might recognize host:port=description.
|
||
|
*
|
||
|
*---------------------------------------------------------------*/
|
||
|
|
||
|
#define MAX_CLIENTS 6
|
||
|
|
||
|
/* Obtained from the command line. */
|
||
|
|
||
|
static int num_clients;
|
||
|
|
||
|
static char hostname[MAX_CLIENTS][50];
|
||
|
static char port[MAX_CLIENTS][30];
|
||
|
static char description[MAX_CLIENTS][50];
|
||
|
|
||
|
#if __WIN32__
|
||
|
static HANDLE client_th[MAX_CLIENTS];
|
||
|
#else
|
||
|
static pthread_t client_tid[MAX_CLIENTS];
|
||
|
#endif
|
||
|
|
||
|
#define LINE_WIDTH 120
|
||
|
static int column_width;
|
||
|
static char packets[LINE_WIDTH+4];
|
||
|
static int packet_count[MAX_CLIENTS];
|
||
|
|
||
|
|
||
|
//#define PRINT_MINUTES 2
|
||
|
|
||
|
#define PRINT_MINUTES 30
|
||
|
|
||
|
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
int j;
|
||
|
time_t start_time, now, next_print_time;
|
||
|
|
||
|
#if __WIN32__
|
||
|
#else
|
||
|
int e;
|
||
|
|
||
|
setlinebuf (stdout);
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Extract command line args.
|
||
|
*/
|
||
|
num_clients = argc - 1;
|
||
|
|
||
|
if (num_clients < 1 || num_clients > MAX_CLIENTS) {
|
||
|
printf ("Specify up to %d TNCs on the command line.\n", MAX_CLIENTS);
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
column_width = LINE_WIDTH / num_clients;
|
||
|
|
||
|
for (j=0; j<num_clients; j++) {
|
||
|
char stemp[100];
|
||
|
char *p;
|
||
|
|
||
|
strcpy (stemp, argv[j+1]);
|
||
|
p = strtok (stemp, "=");
|
||
|
if (p == NULL) {
|
||
|
printf ("Internal error 1\n");
|
||
|
exit (1);
|
||
|
}
|
||
|
strcpy (hostname[j], "localhost");
|
||
|
strcpy (port[j], p);
|
||
|
p = strtok (NULL, "=");
|
||
|
if (p == NULL) {
|
||
|
printf ("Missing description after %s\n", port[j]);
|
||
|
exit (1);
|
||
|
}
|
||
|
strcpy (description[j], p);
|
||
|
}
|
||
|
|
||
|
//printf ("_WIN32_WINNT = %04x\n", _WIN32_WINNT);
|
||
|
//for (j=0; j<num_clients; j++) {
|
||
|
// printf ("%s,%s,%s\n", hostname[j], port[j], description[j]);
|
||
|
//}
|
||
|
|
||
|
memset (packets, ' ', (size_t)LINE_WIDTH);
|
||
|
packets[LINE_WIDTH] = '\0';
|
||
|
|
||
|
for (j=0; j<num_clients; j++) {
|
||
|
packet_count[j] = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
for (j=0; j<num_clients; j++) {
|
||
|
#if __WIN32__
|
||
|
if (isdigit(port[j][0])) {
|
||
|
client_th[j] = (HANDLE)_beginthreadex (NULL, 0, client_thread_net, (void *)j, 0, NULL);
|
||
|
}
|
||
|
else {
|
||
|
client_th[j] = (HANDLE)_beginthreadex (NULL, 0, client_thread_serial, (void *)j, 0, NULL);
|
||
|
}
|
||
|
if (client_th[j] == NULL) {
|
||
|
printf ("Internal error: Could not create client thread %d.\n", j);
|
||
|
exit (1);
|
||
|
}
|
||
|
#else
|
||
|
if (isdigit(port[j][0])) {
|
||
|
e = pthread_create (&client_tid[j], NULL, client_thread_net, (void *)(long)j);
|
||
|
}
|
||
|
else {
|
||
|
e = pthread_create (&client_tid[j], NULL, client_thread_serial, (void *)(long)j);
|
||
|
}
|
||
|
if (e != 0) {
|
||
|
perror("Internal error: Could not create client thread.");
|
||
|
exit (1);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
start_time = time(NULL);
|
||
|
next_print_time = start_time + (PRINT_MINUTES) * 60;
|
||
|
|
||
|
/*
|
||
|
* Print results from clients.
|
||
|
*/
|
||
|
while (1) {
|
||
|
int k;
|
||
|
int something;
|
||
|
|
||
|
SLEEP_MS(100);
|
||
|
|
||
|
something = 0;
|
||
|
for (k=0; k<LINE_WIDTH && ! something; k++) {
|
||
|
if (packets[k] != ' ') {
|
||
|
something = 1;
|
||
|
}
|
||
|
}
|
||
|
if (something) {
|
||
|
/* time for others to catch up. */
|
||
|
SLEEP_MS(200);
|
||
|
|
||
|
printf ("%s\n", packets);
|
||
|
memset (packets, ' ', (size_t)LINE_WIDTH);
|
||
|
}
|
||
|
|
||
|
now = time(NULL);
|
||
|
if (now >= next_print_time) {
|
||
|
next_print_time = now + (PRINT_MINUTES) * 60;
|
||
|
|
||
|
printf ("\nTotals after %d minutes", (int)((now - start_time) / 60));
|
||
|
|
||
|
for (j=0; j<num_clients; j++) {
|
||
|
printf (", %s %d", description[j], packet_count[j]);
|
||
|
}
|
||
|
printf ("\n\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
return 0; // unreachable
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*-------------------------------------------------------------------
|
||
|
*
|
||
|
* Name: client_thread_net
|
||
|
*
|
||
|
* Purpose: Establish connection with a TNC via network.
|
||
|
*
|
||
|
* Inputs: arg - My instance index, 0 thru MAX_CLIENTS-1.
|
||
|
*
|
||
|
* Outputs: packets - Received packets are put in the corresponding column.
|
||
|
*
|
||
|
*--------------------------------------------------------------------*/
|
||
|
|
||
|
#define MAX_HOSTS 30
|
||
|
|
||
|
#if __WIN32__
|
||
|
static unsigned __stdcall client_thread_net (void *arg)
|
||
|
#else
|
||
|
static void * client_thread_net (void *arg)
|
||
|
#endif
|
||
|
{
|
||
|
int my_index;
|
||
|
struct addrinfo hints;
|
||
|
struct addrinfo *ai_head = NULL;
|
||
|
struct addrinfo *ai;
|
||
|
struct addrinfo *hosts[MAX_HOSTS];
|
||
|
int num_hosts, n;
|
||
|
int err;
|
||
|
char ipaddr_str[46]; /* text form of IP address */
|
||
|
#if __WIN32__
|
||
|
WSADATA wsadata;
|
||
|
#endif
|
||
|
/*
|
||
|
* File descriptor for socket to server.
|
||
|
* Set to -1 if not connected.
|
||
|
* (Don't use SOCKET type because it is unsigned.)
|
||
|
*/
|
||
|
int server_sock = -1;
|
||
|
struct agwpe_s mon_cmd;
|
||
|
char data[1024];
|
||
|
int use_chan = -1;
|
||
|
|
||
|
|
||
|
my_index = (int)(long)arg;
|
||
|
|
||
|
#if DEBUGx
|
||
|
printf ("DEBUG: client_thread_net %d start, port = '%s'\n", my_index, port[my_index]);
|
||
|
#endif
|
||
|
|
||
|
#if __WIN32__
|
||
|
err = WSAStartup (MAKEWORD(2,2), &wsadata);
|
||
|
if (err != 0) {
|
||
|
printf("WSAStartup failed: %d\n", err);
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
|
||
|
printf("Could not find a usable version of Winsock.dll\n");
|
||
|
WSACleanup();
|
||
|
//sleep (1);
|
||
|
return (0);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
memset (&hints, 0, sizeof(hints));
|
||
|
|
||
|
hints.ai_family = AF_UNSPEC; /* Allow either IPv4 or IPv6. */
|
||
|
// hints.ai_family = AF_INET; /* IPv4 only. */
|
||
|
hints.ai_socktype = SOCK_STREAM;
|
||
|
hints.ai_protocol = IPPROTO_TCP;
|
||
|
|
||
|
/*
|
||
|
* Connect to TNC server.
|
||
|
*/
|
||
|
|
||
|
ai_head = NULL;
|
||
|
err = getaddrinfo(hostname[my_index], port[my_index], &hints, &ai_head);
|
||
|
if (err != 0) {
|
||
|
#if __WIN32__
|
||
|
printf ("Can't get address for server %s, err=%d\n",
|
||
|
hostname[my_index], WSAGetLastError());
|
||
|
#else
|
||
|
printf ("Can't get address for server %s, %s\n",
|
||
|
hostname[my_index], gai_strerror(err));
|
||
|
#endif
|
||
|
freeaddrinfo(ai_head);
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
#if DEBUG_DNS
|
||
|
printf ("getaddrinfo returns:\n");
|
||
|
#endif
|
||
|
num_hosts = 0;
|
||
|
for (ai = ai_head; ai != NULL; ai = ai->ai_next) {
|
||
|
#if DEBUG_DNS
|
||
|
ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
|
||
|
printf (" %s\n", ipaddr_str);
|
||
|
#endif
|
||
|
hosts[num_hosts] = ai;
|
||
|
if (num_hosts < MAX_HOSTS) num_hosts++;
|
||
|
}
|
||
|
|
||
|
#if DEBUG_DNS
|
||
|
printf ("addresses for hostname:\n");
|
||
|
for (n=0; n<num_hosts; n++) {
|
||
|
ia_to_text (hosts[n]->ai_family, hosts[n]->ai_addr, ipaddr_str, sizeof(ipaddr_str));
|
||
|
printf (" %s\n", ipaddr_str);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Try each address until we find one that is successful.
|
||
|
|
||
|
for (n=0; n<num_hosts; n++) {
|
||
|
int is;
|
||
|
|
||
|
ai = hosts[n];
|
||
|
|
||
|
ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
|
||
|
is = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||
|
#if __WIN32__
|
||
|
if (is == INVALID_SOCKET) {
|
||
|
printf ("Socket creation failed, err=%d", WSAGetLastError());
|
||
|
WSACleanup();
|
||
|
is = -1;
|
||
|
continue;
|
||
|
}
|
||
|
#else
|
||
|
if (err != 0) {
|
||
|
printf ("Socket creation failed, err=%s", gai_strerror(err));
|
||
|
(void) close (is);
|
||
|
is = -1;
|
||
|
continue;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifndef DEBUG_DNS
|
||
|
err = connect(is, ai->ai_addr, (int)ai->ai_addrlen);
|
||
|
#if __WIN32__
|
||
|
if (err == SOCKET_ERROR) {
|
||
|
#if DEBUGx
|
||
|
printf("Connect to %s on %s (%s), port %s failed.\n",
|
||
|
description[my_index], hostname[my_index], ipaddr_str, port[my_index]);
|
||
|
#endif
|
||
|
closesocket (is);
|
||
|
is = -1;
|
||
|
continue;
|
||
|
}
|
||
|
#else
|
||
|
if (err != 0) {
|
||
|
#if DEBUGx
|
||
|
printf("Connect to %s on %s (%s), port %s failed.\n",
|
||
|
description[my_index], hostname[my_index], ipaddr_str, port[my_index]);
|
||
|
#endif
|
||
|
(void) close (is);
|
||
|
is = -1;
|
||
|
continue;
|
||
|
}
|
||
|
int flag = 1;
|
||
|
err = setsockopt (is, IPPROTO_TCP, TCP_NODELAY, (void*)(long)(&flag), (socklen_t)sizeof(flag));
|
||
|
if (err < 0) {
|
||
|
printf("setsockopt TCP_NODELAY failed.\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* Success. */
|
||
|
|
||
|
printf("Client %d now connected to %s on %s (%s), port %s\n",
|
||
|
my_index, description[my_index], hostname[my_index], ipaddr_str, port[my_index] );
|
||
|
|
||
|
server_sock = is;
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
freeaddrinfo(ai_head);
|
||
|
|
||
|
if (server_sock == -1) {
|
||
|
|
||
|
printf("Client %d unable to connect to %s on %s (%s), port %s\n",
|
||
|
my_index, description[my_index], hostname[my_index], ipaddr_str, port[my_index] );
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Send command to toggle reception of frames in raw format.
|
||
|
*
|
||
|
* Note: Monitor format is only for UI frames.
|
||
|
* It also discards the via path.
|
||
|
*/
|
||
|
|
||
|
memset (&mon_cmd, 0, sizeof(mon_cmd));
|
||
|
|
||
|
mon_cmd.kind_lo = 'k';
|
||
|
|
||
|
#if __WIN32__
|
||
|
send (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0);
|
||
|
#else
|
||
|
(void)write (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Print all of the monitored packets.
|
||
|
*/
|
||
|
|
||
|
while (1) {
|
||
|
int n;
|
||
|
|
||
|
#if __WIN32__
|
||
|
n = recv (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0);
|
||
|
#else
|
||
|
n = read (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||
|
#endif
|
||
|
|
||
|
if (n != sizeof(mon_cmd)) {
|
||
|
printf ("Read error, client %d received %d command bytes.\n", my_index, n);
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
#if DEBUGx
|
||
|
printf ("client %d received '%c' data, data_len = %d\n",
|
||
|
my_index, mon_cmd.kind_lo, mon_cmd.data_len);
|
||
|
#endif
|
||
|
assert (mon_cmd.data_len >= 0 && mon_cmd.data_len < sizeof(data));
|
||
|
|
||
|
if (mon_cmd.data_len > 0) {
|
||
|
#if __WIN32__
|
||
|
n = recv (server_sock, data, mon_cmd.data_len, 0);
|
||
|
#else
|
||
|
n = read (server_sock, data, mon_cmd.data_len);
|
||
|
#endif
|
||
|
|
||
|
if (n != mon_cmd.data_len) {
|
||
|
printf ("Read error, client %d received %d data bytes.\n", my_index, n);
|
||
|
exit (1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Print it and add to counter.
|
||
|
* The AGWPE score was coming out double the proper value because
|
||
|
* we were getting the same thing from ports 2 and 3.
|
||
|
* 'use_chan' is the first channel we hear from.
|
||
|
* Listen only to that one.
|
||
|
*/
|
||
|
|
||
|
if (mon_cmd.kind_lo == 'K' && (use_chan == -1 || use_chan == mon_cmd.portx)) {
|
||
|
packet_t pp;
|
||
|
char *pinfo;
|
||
|
int info_len;
|
||
|
char result[400];
|
||
|
char *p;
|
||
|
int col, len;
|
||
|
|
||
|
//printf ("server %d, portx = %d\n", my_index, mon_cmd.portx);
|
||
|
|
||
|
use_chan == mon_cmd.portx;
|
||
|
pp = ax25_from_frame ((unsigned char *)(data+1), mon_cmd.data_len-1, -1);
|
||
|
ax25_format_addrs (pp, result);
|
||
|
info_len = ax25_get_info (pp, (unsigned char **)(&pinfo));
|
||
|
pinfo[info_len] = '\0';
|
||
|
strcat (result, pinfo);
|
||
|
for (p=result; *p!='\0'; p++) {
|
||
|
if (! isprint(*p)) *p = ' ';
|
||
|
}
|
||
|
#if DEBUGx
|
||
|
printf ("[%d] %s\n", my_index, result);
|
||
|
#endif
|
||
|
col = column_width * my_index;
|
||
|
len = strlen(result);
|
||
|
#define MARGIN 3
|
||
|
if (len > column_width - 3) {
|
||
|
len = column_width - 3;
|
||
|
}
|
||
|
if (packets[col] == ' ') {
|
||
|
memcpy (packets+col, result, (size_t)len);
|
||
|
}
|
||
|
else {
|
||
|
memcpy (packets+col, "OVERRUN! ", (size_t)10);
|
||
|
}
|
||
|
|
||
|
ax25_delete (pp);
|
||
|
packet_count[my_index]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} /* end client_thread_net */
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*-------------------------------------------------------------------
|
||
|
*
|
||
|
* Name: client_thread_serial
|
||
|
*
|
||
|
* Purpose: Establish connection with a TNC via serial port.
|
||
|
*
|
||
|
* Inputs: arg - My instance index, 0 thru MAX_CLIENTS-1.
|
||
|
*
|
||
|
* Outputs: packets - Received packets are put in the corresponding column.
|
||
|
*
|
||
|
*--------------------------------------------------------------------*/
|
||
|
|
||
|
#if __WIN32__
|
||
|
typedef HANDLE MYFDTYPE;
|
||
|
#define MYFDERROR INVALID_HANDLE_VALUE
|
||
|
#else
|
||
|
typedef int MYFDTYPE;
|
||
|
#define MYFDERROR (-1)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if __WIN32__
|
||
|
static unsigned __stdcall client_thread_serial (void *arg)
|
||
|
#else
|
||
|
static void * client_thread_serial (void *arg)
|
||
|
#endif
|
||
|
{
|
||
|
int my_index = (int)(long)arg;
|
||
|
|
||
|
#if __WIN32__
|
||
|
|
||
|
MYFDTYPE fd;
|
||
|
DCB dcb;
|
||
|
int ok;
|
||
|
|
||
|
|
||
|
fd = CreateFile(port[my_index], GENERIC_READ | GENERIC_WRITE,
|
||
|
0, NULL, OPEN_EXISTING, 0, NULL);
|
||
|
|
||
|
if (fd == MYFDERROR) {
|
||
|
printf("Client %d unable to connect to %s on %s.\n",
|
||
|
my_index, description[my_index], port[my_index] );
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
/* Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363201(v=vs.85).aspx */
|
||
|
|
||
|
memset (&dcb, 0, sizeof(dcb));
|
||
|
dcb.DCBlength = sizeof(DCB);
|
||
|
|
||
|
ok = GetCommState (fd, &dcb);
|
||
|
if (! ok) {
|
||
|
printf ("GetCommState failed.\n");
|
||
|
}
|
||
|
|
||
|
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
|
||
|
|
||
|
dcb.BaudRate = 9600;
|
||
|
dcb.fBinary = 1;
|
||
|
dcb.fParity = 0;
|
||
|
dcb.fOutxCtsFlow = 0;
|
||
|
dcb.fOutxDsrFlow = 0;
|
||
|
dcb.fDtrControl = 0;
|
||
|
dcb.fDsrSensitivity = 0;
|
||
|
dcb.fOutX = 0;
|
||
|
dcb.fInX = 0;
|
||
|
dcb.fErrorChar = 0;
|
||
|
dcb.fNull = 0; /* Don't drop nul characters! */
|
||
|
dcb.fRtsControl = 0;
|
||
|
dcb.ByteSize = 8;
|
||
|
dcb.Parity = NOPARITY;
|
||
|
dcb.StopBits = ONESTOPBIT;
|
||
|
|
||
|
ok = SetCommState (fd, &dcb);
|
||
|
if (! ok) {
|
||
|
printf ("SetCommState failed.\n");
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
/* Linux version. */
|
||
|
|
||
|
int fd;
|
||
|
struct termios ts;
|
||
|
int e;
|
||
|
|
||
|
fd = open (port[my_index], O_RDWR);
|
||
|
|
||
|
if (fd == MYFDERROR) {
|
||
|
printf("Client %d unable to connect to %s on %s.\n",
|
||
|
my_index, description[my_index], port[my_index] );
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
e = tcgetattr (fd, &ts);
|
||
|
if (e != 0) { perror ("nm tcgetattr"); }
|
||
|
|
||
|
cfmakeraw (&ts);
|
||
|
|
||
|
// TODO: speed?
|
||
|
ts.c_cc[VMIN] = 1; /* wait for at least one character */
|
||
|
ts.c_cc[VTIME] = 0; /* no fancy timing. */
|
||
|
|
||
|
e = tcsetattr (fd, TCSANOW, &ts);
|
||
|
if (e != 0) { perror ("nm tcsetattr"); }
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/* Success. */
|
||
|
|
||
|
printf("Client %d now connected to %s on %s\n",
|
||
|
my_index, description[my_index], port[my_index] );
|
||
|
|
||
|
/*
|
||
|
* Assume we are already in monitor mode.
|
||
|
*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Print all of the monitored packets.
|
||
|
*/
|
||
|
|
||
|
while (1) {
|
||
|
unsigned char ch;
|
||
|
char result[500];
|
||
|
int col, len;
|
||
|
int done;
|
||
|
char *p;
|
||
|
|
||
|
len = 0;
|
||
|
done = 0;
|
||
|
|
||
|
while ( ! done) {
|
||
|
|
||
|
#if __WIN32__
|
||
|
DWORD n;
|
||
|
|
||
|
if (! ReadFile (fd, &ch, 1, &n, NULL)) {
|
||
|
printf ("Read error on %s.\n", description[my_index]);
|
||
|
CloseHandle (fd);
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
int n;
|
||
|
|
||
|
if ( ( n = read(fd, & ch, 1)) < 0) {
|
||
|
printf ("Read error on %s.\n", description[my_index]);
|
||
|
close (fd);
|
||
|
exit (1);
|
||
|
}
|
||
|
#endif
|
||
|
if (n == 1) {
|
||
|
|
||
|
/*
|
||
|
* Try to build one line for each packet.
|
||
|
* The KPC3+ breaks a packet into two lines like this:
|
||
|
*
|
||
|
* KB1ZXL-1>T2QY5P,W1MHL*,WIDE2-1: <<UI>>:
|
||
|
* `c0+!h4>/]"4a}146.520MHz Listening, V-Alert & WLNK-1=
|
||
|
*
|
||
|
* N8VIM>BEACON,W1XM,WB2OSZ-1,WIDE2*: <UI>:
|
||
|
* !4240.85N/07133.99W_PHG72604/ Pepperell, MA. WX. 442.9+ PL100
|
||
|
*
|
||
|
* Don't know why some are <<UI>> and some <UI>.
|
||
|
*
|
||
|
* Anyhow, ignore the return character if preceded by >:
|
||
|
*/
|
||
|
if (ch == '\r') {
|
||
|
if (len >= 10 && result[len-2] == '>' && result[len-1] == ':') {
|
||
|
continue;
|
||
|
}
|
||
|
done = 1;
|
||
|
continue;
|
||
|
}
|
||
|
if (ch == '\n') continue;
|
||
|
result[len++] = ch;
|
||
|
}
|
||
|
}
|
||
|
result[len] = '\0';
|
||
|
|
||
|
/*
|
||
|
* Print it and add to counter.
|
||
|
*/
|
||
|
if (len > 0) {
|
||
|
/* Blank any unprintable characters. */
|
||
|
for (p=result; *p!='\0'; p++) {
|
||
|
if (! isprint(*p)) *p = ' ';
|
||
|
}
|
||
|
#if DEBUGx
|
||
|
printf ("[%d] %s\n", my_index, result);
|
||
|
#endif
|
||
|
col = column_width * my_index;
|
||
|
if (len > column_width - 3) {
|
||
|
len = column_width - 3;
|
||
|
}
|
||
|
if (packets[col] == ' ') {
|
||
|
memcpy (packets+col, result, (size_t)len);
|
||
|
}
|
||
|
else {
|
||
|
memcpy (packets+col, "OVERRUN! ", (size_t)10);
|
||
|
}
|
||
|
packet_count[my_index]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} /* end client_thread_serial */
|
||
|
|
||
|
/* end aclients.c */
|