direwolf/kissnet.c

672 lines
17 KiB
C
Raw Permalink Normal View History

Version 1.0 - Initial commit Changes to be committed: new file: .gitattributes new file: .gitignore new file: APRStt-Implementation-Notes.pdf new file: CHANGES.txt new file: LICENSE-dire-wolf.txt new file: LICENSE-other.txt new file: Makefile.linux new file: Makefile.win new file: Quick-Start-Guide-Windows.pdf new file: Raspberry-Pi-APRS.pdf new file: User-Guide.pdf new file: aclients.c new file: aprs_tt.c new file: aprs_tt.h new file: atest.c new file: audio.c new file: audio.h new file: audio_win.c new file: ax25_pad.c new file: ax25_pad.h new file: beacon.c new file: beacon.h new file: config.c new file: config.h new file: decode_aprs.c new file: decode_aprs.h new file: dedupe.c new file: dedupe.h new file: demod.c new file: demod.h new file: demod_9600.c new file: demod_9600.h new file: demod_afsk.c new file: demod_afsk.h new file: digipeater.c new file: digipeater.h new file: direwolf.c new file: direwolf.conf new file: direwolf.desktop new file: direwolf.h new file: dsp.c new file: dsp.h new file: dtmf.c new file: dtmf.h new file: dw-icon.ico new file: dw-icon.png new file: dw-icon.rc new file: dw-start.sh new file: dwgps.c new file: dwgps.h new file: encode_aprs.c new file: encode_aprs.h new file: fcs_calc.c new file: fcs_calc.h new file: fsk_demod_agc.h new file: fsk_demod_state.h new file: fsk_filters.h new file: fsk_gen_filter.h new file: gen_packets.c new file: gen_tone.c new file: gen_tone.h new file: hdlc_rec.c new file: hdlc_rec.h new file: hdlc_rec2.c new file: hdlc_rec2.h new file: hdlc_send.c new file: hdlc_send.h new file: igate.c new file: igate.h new file: kiss.c new file: kiss.h new file: kiss_frame.c new file: kiss_frame.h new file: kissnet.c new file: kissnet.h new file: latlong.c new file: latlong.h new file: ll2utm.c new file: misc/README-dire-wolf.txt new file: misc/strcasestr.c new file: misc/strsep.c new file: misc/strtok_r.c new file: morse.c new file: multi_modem.c new file: multi_modem.h new file: ptt.c new file: ptt.h new file: pttest.c new file: rdq.c new file: rdq.h new file: redecode.c new file: redecode.h new file: regex/COPYING new file: regex/INSTALL new file: regex/LICENSES new file: regex/NEWS new file: regex/README new file: regex/README-dire-wolf.txt new file: regex/re_comp.h new file: regex/regcomp.c new file: regex/regex.c new file: regex/regex.h new file: regex/regex_internal.c new file: regex/regex_internal.h new file: regex/regexec.c new file: rrbb.c new file: rrbb.h new file: server.c new file: server.h new file: symbols-new.txt new file: symbols.c new file: symbols.h new file: symbolsX.txt new file: textcolor.c new file: textcolor.h new file: tocalls.txt new file: tq.c new file: tq.h new file: tt_text.c new file: tt_text.h new file: tt_user.c new file: tt_user.h new file: tune.h new file: udp_test.c new file: utm/LatLong-UTMconversion.c new file: utm/LatLong-UTMconversion.h new file: utm/README.txt new file: utm/SwissGrid.cpp new file: utm/UTMConversions.cpp new file: utm/constants.h new file: utm2ll.c new file: version.h new file: xmit.c new file: xmit.h
2015-07-27 00:35:07 +00:00
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011-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: kissnet.c
*
* Purpose: Provide service to other applications via KISS protocol via TCP socket.
*
* Input:
*
* Outputs:
*
* Description: This provides a TCP socket for communication with a client application.
*
* It implements the KISS TNS protocol as described in:
* http://www.ka9q.net/papers/kiss.html
*
* Briefly, a frame is composed of
*
* * FEND (0xC0)
* * Contents - with special escape sequences so a 0xc0
* byte in the data is not taken as end of frame.
* as part of the data.
* * FEND
*
* The first byte of the frame contains:
*
* * port number in upper nybble.
* * command in lower nybble.
*
*
* Commands from application recognized:
*
* 0 Data Frame AX.25 frame in raw format.
*
* 1 TXDELAY See explanation in xmit.c.
*
* 2 Persistence " "
*
* 3 SlotTime " "
*
* 4 TXtail " "
* Spec says it is obsolete but Xastir
* sends it and we respect it.
*
* 5 FullDuplex Ignored. Always full duplex.
*
* 6 SetHardware TNC specific. Ignored.
*
* FF Return Exit KISS mode. Ignored.
*
*
* Messages sent to client application:
*
* 0 Data Frame Received AX.25 frame in raw format.
*
*
*
*
* References: Getting Started with Winsock
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb530742(v=vs.85).aspx
*
* Future: Originally we had:
* KISS over serial port.
* AGW over socket.
* This is the two of them munged together and we end up with duplicate code.
* It would have been better to separate out the transport and application layers.
* Maybe someday.
*
*---------------------------------------------------------------*/
/*
* Native Windows: Use the Winsock interface.
* Linux: Use the BSD socket interface.
* Cygwin: Can use either one.
*/
#if __WIN32__
#include <winsock2.h>
#define _WIN32_WINNT 0x0501
#include <ws2tcpip.h>
#else
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "direwolf.h"
#include "tq.h"
#include "ax25_pad.h"
#include "textcolor.h"
#include "audio.h"
#include "kissnet.h"
#include "kiss_frame.h"
#include "xmit.h"
static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
static int client_sock; /* File descriptor for socket for */
/* communication with client application. */
/* Set to -1 if not connected. */
/* (Don't use SOCKET type because it is unsigned.) */
static int num_channels; /* Number of radio ports. */
static void * connect_listen_thread (void *arg);
static void * kissnet_listen_thread (void *arg);
static int kiss_debug = 0; /* Print information flowing from and to client. */
void kiss_net_set_debug (int n)
{
kiss_debug = n;
}
/*-------------------------------------------------------------------
*
* Name: kissnet_init
*
* Purpose: Set up a server to listen for connection requests from
* an application such as Xastir or APRSIS32.
*
* Inputs: mc->kiss_port - TCP port for server.
* Main program has default of 8000 but allows
* an alternative to be specified on the command line
*
* Outputs:
*
* Description: This starts two threads:
* * to listen for a connection from client app.
* * to listen for commands from client app.
* so the main application doesn't block while we wait for these.
*
*--------------------------------------------------------------------*/
void kissnet_init (struct misc_config_s *mc)
{
#if __WIN32__
HANDLE connect_listen_th;
HANDLE cmd_listen_th;
#else
pthread_t connect_listen_tid;
pthread_t cmd_listen_tid;
#endif
int e;
int kiss_port = mc->kiss_port;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("kissnet_init ( %d )\n", kiss_port);
#endif
memset (&kf, 0, sizeof(kf));
client_sock = -1;
num_channels = mc->num_channels;
/*
* This waits for a client to connect and sets client_sock.
*/
#if __WIN32__
connect_listen_th = _beginthreadex (NULL, 0, connect_listen_thread, (void *)kiss_port, 0, NULL);
if (connect_listen_th == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create KISS socket connect listening thread\n");
return;
}
#else
e = pthread_create (&connect_listen_tid, NULL, connect_listen_thread, (void *)(long)kiss_port);
if (e != 0) {
text_color_set(DW_COLOR_ERROR);
perror("Could not create KISS socket connect listening thread");
return;
}
#endif
/*
* This reads messages from client when client_sock is valid.
*/
#if __WIN32__
cmd_listen_th = _beginthreadex (NULL, 0, kissnet_listen_thread, NULL, 0, NULL);
if (cmd_listen_th == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create KISS socket command listening thread\n");
return;
}
#else
e = pthread_create (&cmd_listen_tid, NULL, kissnet_listen_thread, NULL);
if (e != 0) {
text_color_set(DW_COLOR_ERROR);
perror("Could not create KISS socket command listening thread");
return;
}
#endif
}
/*-------------------------------------------------------------------
*
* Name: connect_listen_thread
*
* Purpose: Wait for a connection request from an application.
*
* Inputs: arg - TCP port for server.
* Main program has default of 8001 but allows
* an alternative to be specified on the command line
*
* Outputs: client_sock - File descriptor for communicating with client app.
*
* Description: Wait for connection request from client and establish
* communication.
* Note that the client can go away and come back again and
* re-establish communication without restarting this application.
*
*--------------------------------------------------------------------*/
static void * connect_listen_thread (void *arg)
{
#if __WIN32__
struct addrinfo hints;
struct addrinfo *ai = NULL;
int err;
char kiss_port_str[12];
SOCKET listen_sock;
WSADATA wsadata;
sprintf (kiss_port_str, "%d", (int)(long)arg);
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("DEBUG: kissnet port = %d = '%s'\n", (int)(long)arg, kiss_port_str);
#endif
err = WSAStartup (MAKEWORD(2,2), &wsadata);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf("WSAStartup failed: %d\n", err);
return (NULL);
}
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
//sleep (1);
return (NULL);
}
memset (&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
err = getaddrinfo(NULL, kiss_port_str, &hints, &ai);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf("getaddrinfo failed: %d\n", err);
//sleep (1);
WSACleanup();
return (NULL);
}
listen_sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (listen_sock == INVALID_SOCKET) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("connect_listen_thread: Socket creation failed, err=%d", WSAGetLastError());
return (NULL);
}
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf("Binding to port %s ... \n", kiss_port_str);
#endif
err = bind( listen_sock, ai->ai_addr, (int)ai->ai_addrlen);
if (err == SOCKET_ERROR) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(ai);
closesocket(listen_sock);
WSACleanup();
return (NULL);
}
freeaddrinfo(ai);
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf("opened KISS socket as fd (%d) on port (%s) for stream i/o\n", listen_sock, kiss_port_str );
#endif
while (1) {
while (client_sock > 0) {
SLEEP_SEC(1); /* Already connected. Try again later. */
}
#define QUEUE_SIZE 5
if(listen(listen_sock,QUEUE_SIZE) == SOCKET_ERROR)
{
text_color_set(DW_COLOR_ERROR);
dw_printf("Listen failed with error: %d\n", WSAGetLastError());
return (NULL);
}
text_color_set(DW_COLOR_INFO);
dw_printf("Ready to accept KISS client application on port %s ...\n", kiss_port_str);
client_sock = accept(listen_sock, NULL, NULL);
if (client_sock == -1) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Accept failed with error: %d\n", WSAGetLastError());
closesocket(listen_sock);
WSACleanup();
return (NULL);
}
text_color_set(DW_COLOR_INFO);
dw_printf("\nConnected to KISS client application ...\n\n");
}
#else
struct sockaddr_in sockaddr; /* Internet socket address stuct */
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
int kiss_port = (int)(long)arg;
int listen_sock;
listen_sock= socket(AF_INET,SOCK_STREAM,0);
if (listen_sock == -1) {
text_color_set(DW_COLOR_ERROR);
perror ("connect_listen_thread: Socket creation failed");
return (NULL);
}
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(kiss_port);
sockaddr.sin_family = AF_INET;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf("Binding to port %d ... \n", kiss_port);
#endif
if (bind(listen_sock,(struct sockaddr*)&sockaddr,sizeof(sockaddr)) == -1) {
text_color_set(DW_COLOR_ERROR);
perror ("connect_listen_thread: Bind failed");
return (NULL);
}
getsockname( listen_sock, (struct sockaddr *)(&sockaddr), &sockaddr_size);
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf("opened KISS socket as fd (%d) on port (%d) for stream i/o\n", listen_sock, ntohs(sockaddr.sin_port) );
#endif
while (1) {
while (client_sock > 0) {
SLEEP_SEC(1); /* Already connected. Try again later. */
}
#define QUEUE_SIZE 5
if(listen(listen_sock,QUEUE_SIZE) == -1)
{
text_color_set(DW_COLOR_ERROR);
perror ("connect_listen_thread: Listen failed");
return (NULL);
}
text_color_set(DW_COLOR_INFO);
dw_printf("Ready to accept KISS client application on port %d ...\n", kiss_port);
client_sock = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
text_color_set(DW_COLOR_INFO);
dw_printf("\nConnected to KISS client application ...\n\n");
}
#endif
}
/*-------------------------------------------------------------------
*
* Name: kissnet_send_rec_packet
*
* Purpose: Send a received packet to the client app.
*
* Inputs: chan - Channel number where packet was received.
* 0 = first, 1 = second if any.
*
* fbuf - Address of raw received frame buffer
* or a text string.
*
* flen - Number of bytes for AX.25 frame.
* or -1 for a text string.
*
*
* Description: Send message to client if connected.
* Disconnect from client, and notify user, if any error.
*
*--------------------------------------------------------------------*/
void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen)
{
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN];
int kiss_len;
int j;
int err;
if (client_sock == -1) {
return;
}
if (flen < 0) {
flen = strlen((char*)fbuf);
if (kiss_debug) {
kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
}
strcpy ((char *)kiss_buff, (char *)fbuf);
kiss_len = strlen((char *)kiss_buff);
}
else {
kiss_len = 0;
kiss_buff[kiss_len++] = FEND;
kiss_buff[kiss_len++] = chan << 4;
for (j=0; j<flen; j++) {
if (fbuf[j] == FEND) {
kiss_buff[kiss_len++] = FESC;
kiss_buff[kiss_len++] = TFEND;
}
else if (fbuf[j] == FESC) {
kiss_buff[kiss_len++] = FESC;
kiss_buff[kiss_len++] = TFESC;
}
else {
kiss_buff[kiss_len++] = fbuf[j];
}
assert (kiss_len < sizeof (kiss_buff));
}
kiss_buff[kiss_len++] = FEND;
/* Bug: This has the escapes but not the surrounding FENDs. */
if (kiss_debug) {
kiss_debug_print (TO_CLIENT, NULL, kiss_buff+1, kiss_len-2);
}
}
#if __WIN32__
err = send (client_sock, (char*)kiss_buff, kiss_len, 0);
if (err == SOCKET_ERROR)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError %d sending message to KISS client application. Closing connection.\n\n", WSAGetLastError());
closesocket (client_sock);
client_sock = -1;
WSACleanup();
}
#else
err = write (client_sock, kiss_buff, kiss_len);
if (err <= 0)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError sending message to KISS client application. Closing connection.\n\n");
close (client_sock);
client_sock = -1;
}
#endif
} /* end kissnet_send_rec_packet */
/*-------------------------------------------------------------------
*
* Name: read_from_socket
*
* Purpose: Read from socket until we have desired number of bytes.
*
* Inputs: fd - file descriptor.
* ptr - address where data should be placed.
* len - desired number of bytes.
*
* Description: Just a wrapper for the "read" system call but it should
* never return fewer than the desired number of bytes.
*
* Not really needed for KISS because we are dealing with
* a stream of bytes rather than message blocks.
*
*--------------------------------------------------------------------*/
static int read_from_socket (int fd, char *ptr, int len)
{
int got_bytes = 0;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("read_from_socket (%d, %p, %d)\n", fd, ptr, len);
#endif
while (got_bytes < len) {
int n;
#if __WIN32__
//TODO: any flags for send/recv?
n = recv (fd, ptr + got_bytes, len - got_bytes, 0);
#else
n = read (fd, ptr + got_bytes, len - got_bytes);
#endif
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("read_from_socket: n = %d\n", n);
#endif
if (n <= 0) {
return (n);
}
got_bytes += n;
}
assert (got_bytes >= 0 && got_bytes <= len);
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("read_from_socket: return %d\n", got_bytes);
#endif
return (got_bytes);
}
/*-------------------------------------------------------------------
*
* Name: kissnet_listen_thread
*
* Purpose: Wait for KISS messages from an application.
*
* Inputs: arg - Not used.
*
* Outputs: client_sock - File descriptor for communicating with client app.
*
* Description: Process messages from the client application.
* Note that the client can go away and come back again and
* re-establish communication without restarting this application.
*
*--------------------------------------------------------------------*/
/* Return one byte (value 0 - 255) */
static int kiss_get (void)
{
unsigned char ch;
int n;
while (1) {
while (client_sock <= 0) {
SLEEP_SEC(1); /* Not connected. Try again later. */
}
/* Just get one byte at a time. */
n = read_from_socket (client_sock, (char *)(&ch), 1);
if (n == 1) {
#if DEBUG9
dw_printf (log_fp, "%02x %c %c", ch,
isprint(ch) ? ch : '.' ,
(isupper(ch>>1) || isdigit(ch>>1) || (ch>>1) == ' ') ? (ch>>1) : '.');
if (ch == FEND) fprintf (log_fp, " FEND");
if (ch == FESC) fprintf (log_fp, " FESC");
if (ch == TFEND) fprintf (log_fp, " TFEND");
if (ch == TFESC) fprintf (log_fp, " TFESC");
if (ch == '\r') fprintf (log_fp, " CR");
if (ch == '\n') fprintf (log_fp, " LF");
fprintf (log_fp, "\n");
if (ch == FEND) fflush (log_fp);
#endif
return(ch);
}
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError reading KISS byte from clent application. Closing connection.\n\n");
#if __WIN32__
closesocket (client_sock);
#else
close (client_sock);
#endif
client_sock = -1;
}
}
static void * kissnet_listen_thread (void *arg)
{
unsigned char ch;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("kissnet_listen_thread ( socket = %d )\n", client_sock);
#endif
while (1) {
ch = kiss_get();
if (kiss_frame (&kf, ch, kiss_debug, kissnet_send_rec_packet)) {
kiss_process_msg (&kf, kiss_debug);
}
} /* while (1) */
return (NULL); /* to suppress compiler warning. */
} /* end kissnet_listen_thread */
/* end kissnet.c */