mirror of https://github.com/wb2osz/direwolf.git
New kissutil application.
This commit is contained in:
parent
0dbb376129
commit
678b09df3f
|
@ -4,8 +4,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc kissutil
|
||||||
APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc
|
|
||||||
|
|
||||||
all : $(APPS) direwolf.desktop direwolf.conf
|
all : $(APPS) direwolf.desktop direwolf.conf
|
||||||
@echo " "
|
@echo " "
|
||||||
|
@ -255,18 +254,18 @@ endif
|
||||||
# but the initial test doesn't look good. About 2.3 times as slow.
|
# but the initial test doesn't look good. About 2.3 times as slow.
|
||||||
|
|
||||||
# If you want to use OSS (for FreeBSD, OpenBSD) instead of
|
# If you want to use OSS (for FreeBSD, OpenBSD) instead of
|
||||||
# ALSA (for Linux), comment out (or remove) the two lines below.
|
# ALSA (for Linux), comment out (or remove) the line below.
|
||||||
|
|
||||||
# TODO: Can we automate this somehow?
|
# TODO: Can we automate this somehow?
|
||||||
|
|
||||||
CFLAGS += -DUSE_ALSA
|
alsa = 1
|
||||||
LDFLAGS += -lasound
|
|
||||||
|
|
||||||
ifeq ($(wildcard /usr/include/pthread.h),)
|
ifeq ($(wildcard /usr/include/pthread.h),)
|
||||||
$(error /usr/include/pthread.h does not exist. Install it with "sudo apt-get install libc6-dev" or "sudo yum install libc6-dev" )
|
$(error /usr/include/pthread.h does not exist. Install it with "sudo apt-get install libc6-dev" or "sudo yum install libc6-dev" )
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_ALSA),)
|
ifneq ($(alsa),)
|
||||||
|
CFLAGS += -DUSE_ALSA
|
||||||
|
LDFLAGS += -lasound
|
||||||
ifeq ($(wildcard /usr/include/alsa/asoundlib.h),)
|
ifeq ($(wildcard /usr/include/alsa/asoundlib.h),)
|
||||||
$(error /usr/include/alsa/asoundlib.h does not exist. Install it with "sudo apt-get install libasound2-dev" or "sudo yum install libasound2-dev" )
|
$(error /usr/include/alsa/asoundlib.h does not exist. Install it with "sudo apt-get install libasound2-dev" or "sudo yum install libasound2-dev" )
|
||||||
endif
|
endif
|
||||||
|
@ -377,9 +376,9 @@ tocalls-symbols :
|
||||||
|
|
||||||
# Separate application to decode raw data.
|
# Separate application to decode raw data.
|
||||||
|
|
||||||
# First two use .c rather than .o because they depend on DECAMAIN definition.
|
# First three use .c rather than .o because they depend on DECAMAIN definition.
|
||||||
|
|
||||||
decode_aprs : decode_aprs.c kiss_frame.c dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o misc.a
|
decode_aprs : decode_aprs.c kiss_frame.c ax25_pad.c dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o symbols.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o misc.a
|
||||||
$(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
@ -429,6 +428,13 @@ aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.o misc.a
|
||||||
$(CC) $(CFLAGS) -g -o $@ $^
|
$(CC) $(CFLAGS) -g -o $@ $^
|
||||||
|
|
||||||
|
|
||||||
|
# Talk to a KISS TNC.
|
||||||
|
# Note: kiss_frame.c has conditional compilation on KISSUTIL.
|
||||||
|
|
||||||
|
kissutil : kissutil.c kiss_frame.c ax25_pad.o fcs_calc.o textcolor.o serial_port.o dtime_now.o sock.o misc.a
|
||||||
|
$(CC) $(CFLAGS) -g -DKISSUTIL -o $@ $^
|
||||||
|
|
||||||
|
|
||||||
# Touch Tone to Speech sample application.
|
# Touch Tone to Speech sample application.
|
||||||
|
|
||||||
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a
|
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a
|
||||||
|
@ -562,6 +568,7 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
||||||
$(INSTALL) gen_packets $(INSTALLDIR)/bin
|
$(INSTALL) gen_packets $(INSTALLDIR)/bin
|
||||||
$(INSTALL) atest $(INSTALLDIR)/bin
|
$(INSTALL) atest $(INSTALLDIR)/bin
|
||||||
$(INSTALL) ttcalc $(INSTALLDIR)/bin
|
$(INSTALL) ttcalc $(INSTALLDIR)/bin
|
||||||
|
$(INSTALL) kissutil $(INSTALLDIR)/bin
|
||||||
$(INSTALL) dwespeak.sh $(INSTALLDIR)/bin
|
$(INSTALL) dwespeak.sh $(INSTALLDIR)/bin
|
||||||
#
|
#
|
||||||
# Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory.
|
# Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory.
|
||||||
|
|
17
Makefile.win
17
Makefile.win
|
@ -16,7 +16,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc tnctest
|
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc tnctest kissutil
|
||||||
|
|
||||||
|
|
||||||
# People say we need -mthreads option for threads to work properly.
|
# People say we need -mthreads option for threads to work properly.
|
||||||
|
@ -30,6 +30,8 @@ CFLAGS := -Ofast -march=pentium3 -msse -Iregex -Iutm -Igeotranz -mthreads -DUSE_
|
||||||
AR := ar
|
AR := ar
|
||||||
|
|
||||||
CFLAGS += -g
|
CFLAGS += -g
|
||||||
|
# TEMP EXPERIMENT - DO NOT RELEASE
|
||||||
|
#CFLAGS += -fsanitize=undefined
|
||||||
|
|
||||||
# For version 1.4, we upgrade from gcc 4.6.2 to 4.9.3.
|
# For version 1.4, we upgrade from gcc 4.6.2 to 4.9.3.
|
||||||
|
|
||||||
|
@ -159,9 +161,9 @@ tocalls-symbols :
|
||||||
|
|
||||||
# Separate application to decode raw data.
|
# Separate application to decode raw data.
|
||||||
|
|
||||||
# First two use .c rather than .o because they depend on DECAMAIN definition.
|
# First three use .c rather than .o because they depend on DECAMAIN definition.
|
||||||
|
|
||||||
decode_aprs : decode_aprs.c kiss_frame.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o regex.a misc.a geotranz.a
|
decode_aprs : decode_aprs.c kiss_frame.c ax25_pad.c dwgpsnmea.o dwgps.o serial_port.o symbols.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o regex.a misc.a geotranz.a
|
||||||
$(CC) $(CFLAGS) -DDECAMAIN -o decode_aprs $^
|
$(CC) $(CFLAGS) -DDECAMAIN -o decode_aprs $^
|
||||||
|
|
||||||
|
|
||||||
|
@ -556,6 +558,14 @@ aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
|
||||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||||
|
|
||||||
|
|
||||||
|
# Talk to a KISS TNC.
|
||||||
|
|
||||||
|
# Note: kiss_frame.c has conditional compilation on KISSUTIL.
|
||||||
|
|
||||||
|
kissutil : kissutil.c kiss_frame.c ax25_pad.o fcs_calc.o textcolor.o serial_port.o sock.o dtime_now.o misc.a regex.a
|
||||||
|
$(CC) $(CFLAGS) -DKISSUTIL -o $@ $^ -lwinmm -lws2_32
|
||||||
|
|
||||||
|
|
||||||
# Touch Tone to Speech sample application.
|
# Touch Tone to Speech sample application.
|
||||||
|
|
||||||
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a
|
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a
|
||||||
|
@ -641,6 +651,7 @@ dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2l
|
||||||
gen_packets.exe \
|
gen_packets.exe \
|
||||||
atest.exe \
|
atest.exe \
|
||||||
ttcalc.exe \
|
ttcalc.exe \
|
||||||
|
kissutil.exe \
|
||||||
dwespeak.bat \
|
dwespeak.bat \
|
||||||
telemetry-toolkit/*
|
telemetry-toolkit/*
|
||||||
rm README-doc.md
|
rm README-doc.md
|
||||||
|
|
209
kiss_frame.c
209
kiss_frame.c
|
@ -27,6 +27,9 @@
|
||||||
*
|
*
|
||||||
* Description: The KISS TNS protocol is described in http://www.ka9q.net/papers/kiss.html
|
* Description: The KISS TNS protocol is described in http://www.ka9q.net/papers/kiss.html
|
||||||
*
|
*
|
||||||
|
* ( An extended form, to handle multiple TNCs on a single serial port.
|
||||||
|
* Not applicable for our situation. http://he.fi/pub/oh7lzb/bpq/multi-kiss.pdf )
|
||||||
|
*
|
||||||
* Briefly, a frame is composed of
|
* Briefly, a frame is composed of
|
||||||
*
|
*
|
||||||
* * FEND (0xC0)
|
* * FEND (0xC0)
|
||||||
|
@ -58,6 +61,9 @@
|
||||||
* _5 FullDuplex Ignored.
|
* _5 FullDuplex Ignored.
|
||||||
*
|
*
|
||||||
* _6 SetHardware TNC specific.
|
* _6 SetHardware TNC specific.
|
||||||
|
*
|
||||||
|
* _C XKISS extension - not supported.
|
||||||
|
* _E XKISS extention - not supported.
|
||||||
*
|
*
|
||||||
* FF Return Exit KISS mode. Ignored.
|
* FF Return Exit KISS mode. Ignored.
|
||||||
*
|
*
|
||||||
|
@ -86,9 +92,32 @@
|
||||||
/* In server.c. Should probably move to some misc. function file. */
|
/* In server.c. Should probably move to some misc. function file. */
|
||||||
void hex_dump (unsigned char *p, int len);
|
void hex_dump (unsigned char *p, int len);
|
||||||
|
|
||||||
|
#ifdef KISSUTIL
|
||||||
|
void hex_dump (unsigned char *p, int len)
|
||||||
|
{
|
||||||
|
int n, i, offset;
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
while (len > 0) {
|
||||||
|
n = len < 16 ? len : 16;
|
||||||
|
printf (" %03x: ", offset);
|
||||||
|
for (i=0; i<n; i++) {
|
||||||
|
printf (" %02x", p[i]);
|
||||||
|
}
|
||||||
|
for (i=n; i<16; i++) {
|
||||||
|
printf (" ");
|
||||||
|
}
|
||||||
|
printf (" ");
|
||||||
|
for (i=0; i<n; i++) {
|
||||||
|
printf ("%c", isprint(p[i]) ? p[i] : '.');
|
||||||
|
}
|
||||||
|
printf ("\n");
|
||||||
|
p += 16;
|
||||||
|
offset += 16;
|
||||||
|
len -= 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if KISSTEST
|
#if KISSTEST
|
||||||
|
|
||||||
|
@ -102,11 +131,17 @@ void text_color_set (dw_color_t c)
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#ifndef DECAMAIN
|
#ifndef DECAMAIN
|
||||||
static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug);
|
#ifndef KISSUTIL
|
||||||
|
static void kiss_set_hardware (int chan, char *command, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int));
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if KISSUTIL
|
||||||
|
#define text_color_set(x) ;
|
||||||
|
#define dw_printf printf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/*-------------------------------------------------------------------
|
||||||
|
@ -332,7 +367,7 @@ int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,unsigned char*,int,int))
|
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
|
||||||
{
|
{
|
||||||
|
|
||||||
//dw_printf ("kiss_frame ( %c %02x ) \n", ch, ch);
|
//dw_printf ("kiss_frame ( %c %02x ) \n", ch, ch);
|
||||||
|
@ -370,14 +405,17 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, v
|
||||||
kf->noise[kf->noise_len] = '\0';
|
kf->noise[kf->noise_len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef KISSUTIL
|
||||||
/* Try to appease client app by sending something back. */
|
/* Try to appease client app by sending something back. */
|
||||||
if (strcasecmp("restart\r", (char*)(kf->noise)) == 0 ||
|
if (strcasecmp("restart\r", (char*)(kf->noise)) == 0 ||
|
||||||
strcasecmp("reset\r", (char*)(kf->noise)) == 0) {
|
strcasecmp("reset\r", (char*)(kf->noise)) == 0) {
|
||||||
(*sendfun) (0, (unsigned char *)"\xc0\xc0", -1, client);
|
// first 2 parameters don't matter when length is -1 indicating text.
|
||||||
|
(*sendfun) (0, 0, (unsigned char *)"\xc0\xc0", -1, client);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
(*sendfun) (0, (unsigned char *)"\r\ncmd:", -1, client);
|
(*sendfun) (0, 0, (unsigned char *)"\r\ncmd:", -1, client);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
kf->noise_len = 0;
|
kf->noise_len = 0;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -421,7 +459,7 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, v
|
||||||
hex_dump (unwrapped+1, ulen-1);
|
hex_dump (unwrapped+1, ulen-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
kiss_process_msg (unwrapped, ulen, debug);
|
kiss_process_msg (unwrapped, ulen, debug, client, sendfun);
|
||||||
|
|
||||||
kf->state = KS_SEARCHING;
|
kf->state = KS_SEARCHING;
|
||||||
return;
|
return;
|
||||||
|
@ -456,11 +494,22 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, v
|
||||||
*
|
*
|
||||||
* kiss_len - Number of bytes including the command.
|
* kiss_len - Number of bytes including the command.
|
||||||
*
|
*
|
||||||
* debug - Debug option is selected.
|
* debug - Debug option is selected.
|
||||||
|
*
|
||||||
|
* client - Client app number for TCP KISS.
|
||||||
|
* Ignored for pseudo termal and serial port.
|
||||||
|
*
|
||||||
|
* sendfun - Function to send something to the client application.
|
||||||
|
* "Set Hardware" can send a response.
|
||||||
*
|
*
|
||||||
*-----------------------------------------------------------------*/
|
*-----------------------------------------------------------------*/
|
||||||
|
|
||||||
static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
#ifndef KISSUTIL // All these ifdefs in here are a sign that this should be refactored.
|
||||||
|
// Should split this into multiple files.
|
||||||
|
// Some functions are only for the TNC end.
|
||||||
|
// Other functions are suitble for both TNC and client app.
|
||||||
|
|
||||||
|
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
|
||||||
{
|
{
|
||||||
int port;
|
int port;
|
||||||
int cmd;
|
int cmd;
|
||||||
|
@ -472,10 +521,19 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
||||||
|
|
||||||
switch (cmd)
|
switch (cmd)
|
||||||
{
|
{
|
||||||
case 0: /* Data Frame */
|
case KISS_CMD_DATA_FRAME: /* 0 = Data Frame */
|
||||||
|
|
||||||
/* Special hack - Discard apparently bad data from Linux AX25. */
|
/* Special hack - Discard apparently bad data from Linux AX25. */
|
||||||
|
|
||||||
|
/* Note July 2017: There is a variant of of KISS, called SMACK, that assumes */
|
||||||
|
/* a TNC can never have more than 8 ports. http://symek.de/g/smack.html */
|
||||||
|
/* It uses the MSB to indicate that a checksum is added. I wonder if this */
|
||||||
|
/* is why we sometimes hear about a request to transmit on channel 8. */
|
||||||
|
/* Should we have a message that asks the user if SMACK is being used, */
|
||||||
|
/* and if so, turn it off in the application configuration? */
|
||||||
|
/* Our current default is a maximum of 6 channels but it is easily */
|
||||||
|
/* increased by changing one number and recompiling. */
|
||||||
|
|
||||||
if ((port == 2 || port == 8) &&
|
if ((port == 2 || port == 8) &&
|
||||||
kiss_msg[1] == 'Q' << 1 &&
|
kiss_msg[1] == 'Q' << 1 &&
|
||||||
kiss_msg[2] == 'S' << 1 &&
|
kiss_msg[2] == 'S' << 1 &&
|
||||||
|
@ -525,50 +583,69 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: /* TXDELAY */
|
case KISS_CMD_TXDELAY: /* 1 = TXDELAY */
|
||||||
|
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
dw_printf ("KISS protocol set TXDELAY = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
|
dw_printf ("KISS protocol set TXDELAY = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
|
||||||
|
if (kiss_msg[1] < 4 || kiss_msg[1] > 100) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Are you sure you want such an extreme value for TXDELAY?\n");
|
||||||
|
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
|
||||||
|
}
|
||||||
xmit_set_txdelay (port, kiss_msg[1]);
|
xmit_set_txdelay (port, kiss_msg[1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: /* Persistence */
|
case KISS_CMD_PERSISTENCE: /* 2 = Persistence */
|
||||||
|
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
dw_printf ("KISS protocol set Persistence = %d, port %d\n", kiss_msg[1], port);
|
dw_printf ("KISS protocol set Persistence = %d, port %d\n", kiss_msg[1], port);
|
||||||
|
if (kiss_msg[1] < 5 || kiss_msg[1] > 250) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Are you sure you want such an extreme value for PERSIST?\n");
|
||||||
|
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
|
||||||
|
}
|
||||||
xmit_set_persist (port, kiss_msg[1]);
|
xmit_set_persist (port, kiss_msg[1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: /* SlotTime */
|
case KISS_CMD_SLOTTIME: /* 3 = SlotTime */
|
||||||
|
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
dw_printf ("KISS protocol set SlotTime = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
|
dw_printf ("KISS protocol set SlotTime = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
|
||||||
|
if (kiss_msg[1] < 2 || kiss_msg[1] > 50) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Are you sure you want such an extreme value for SLOTTIME?\n");
|
||||||
|
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
|
||||||
|
}
|
||||||
xmit_set_slottime (port, kiss_msg[1]);
|
xmit_set_slottime (port, kiss_msg[1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: /* TXtail */
|
case KISS_CMD_TXTAIL: /* 4 = TXtail */
|
||||||
|
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
dw_printf ("KISS protocol set TXtail = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
|
dw_printf ("KISS protocol set TXtail = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
|
||||||
|
if (kiss_msg[1] < 2) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Setting TXTAIL so low is asking for trouble. You probably don't want to do this.\n");
|
||||||
|
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
|
||||||
|
}
|
||||||
xmit_set_txtail (port, kiss_msg[1]);
|
xmit_set_txtail (port, kiss_msg[1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5: /* FullDuplex */
|
case KISS_CMD_FULLDUPLEX: /* 5 = FullDuplex */
|
||||||
|
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
dw_printf ("KISS protocol set FullDuplex = %d, port %d - Ignored\n", kiss_msg[1], port);
|
dw_printf ("KISS protocol set FullDuplex = %d, port %d - Ignored\n", kiss_msg[1], port);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6: /* TNC specific */
|
case KISS_CMD_SET_HARDWARE: /* 6 = TNC specific */
|
||||||
|
|
||||||
|
kiss_msg[kiss_len] = '\0';
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
dw_printf ("KISS protocol set hardware - Ignored.\n");
|
dw_printf ("KISS protocol set hardware \"%s\", port %d\n", (char*)(kiss_msg+1), port);
|
||||||
|
kiss_set_hardware (port, (char*)(kiss_msg+1), debug, client, sendfun);
|
||||||
// TODO: kiss_set_hardware (...)
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 15: /* End KISS mode, port should be 15. */
|
case KISS_CMD_END_KISS: /* 15 = End KISS mode, port should be 15. */
|
||||||
/* Ignore it. */
|
/* Ignore it. */
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
dw_printf ("KISS protocol end KISS mode - Ignored.\n");
|
dw_printf ("KISS protocol end KISS mode - Ignored.\n");
|
||||||
|
@ -584,11 +661,19 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
||||||
dw_printf ("Use \"-d kn\" option on direwolf command line to observe\n");
|
dw_printf ("Use \"-d kn\" option on direwolf command line to observe\n");
|
||||||
dw_printf ("all communication with the client application.\n");
|
dw_printf ("all communication with the client application.\n");
|
||||||
|
|
||||||
|
if (cmd == XKISS_CMD_DATA || cmd == XKISS_CMD_POLL) {
|
||||||
|
dw_printf ("\n");
|
||||||
|
dw_printf ("It looks like you are trying to use the \"XKISS\" protocol which is not supported.\n");
|
||||||
|
dw_printf ("Change your application settings to use standard \"KISS\" rather than some other variant.\n");
|
||||||
|
dw_printf ("\n");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* end kiss_process_msg */
|
} /* end kiss_process_msg */
|
||||||
|
|
||||||
|
#endif // ifndef KISSUTIL
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/*-------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
|
@ -596,7 +681,25 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
||||||
*
|
*
|
||||||
* Purpose: Process the "set hardware" command.
|
* Purpose: Process the "set hardware" command.
|
||||||
*
|
*
|
||||||
* Inputs:
|
* Inputs: chan - channel, 0 - 15.
|
||||||
|
*
|
||||||
|
* command - All but the first byte. e.g. "TXBUF:99"
|
||||||
|
* Case sensitive.
|
||||||
|
* Will be modified so be sure caller doesn't care.
|
||||||
|
*
|
||||||
|
* debug - debug level.
|
||||||
|
*
|
||||||
|
* client - Client app number for TCP KISS.
|
||||||
|
* Ignored for pseudo terminal and serial port.
|
||||||
|
*
|
||||||
|
* sendfun - Function to send something to the client application.
|
||||||
|
*
|
||||||
|
* This is the tricky part. We can have any combination of
|
||||||
|
* serial port, pseudo terminal, and multiple TCP clients.
|
||||||
|
* We need to send the response to same place where query came
|
||||||
|
* from. The function is different for each class of device
|
||||||
|
* and we need a client number for the TCP case because we
|
||||||
|
* can have multiple TCP KISS clients at the same time.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Description: This is new in version 1.5. "Set hardware" was previously ignored.
|
* Description: This is new in version 1.5. "Set hardware" was previously ignored.
|
||||||
|
@ -635,10 +738,61 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
||||||
* completely different. It might even be possible to recognize both.
|
* completely different. It might even be possible to recognize both.
|
||||||
* This might allow leveraging of other existing applications.
|
* This might allow leveraging of other existing applications.
|
||||||
*
|
*
|
||||||
|
* Let's start with the easy to understand human readable format.
|
||||||
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
// static void kiss_set_hardware (...)
|
#ifndef KISSUTIL
|
||||||
|
|
||||||
|
static void kiss_set_hardware (int chan, char *command, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
|
||||||
|
{
|
||||||
|
char *param;
|
||||||
|
char response[100];
|
||||||
|
|
||||||
|
param = strchr (command, ':');
|
||||||
|
if (param != NULL) {
|
||||||
|
*param = '\0';
|
||||||
|
param++;
|
||||||
|
|
||||||
|
if (strcmp(command, "TXBUF") == 0) { /* Number of frames in transmit queue. */
|
||||||
|
|
||||||
|
if (strlen(param) > 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("KISS Set Hardware TXBUF: did not expect a parameter.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// See what we have in the transmit queue for specified channel.
|
||||||
|
// fldigi uses bytes but frames seems to make more sense in this situation.
|
||||||
|
// Do we add one if PTT is on? That information doesn't seem to be easily available.
|
||||||
|
|
||||||
|
// TODO: FIXME: not implemented yet.
|
||||||
|
|
||||||
|
// n = tq_count (chan, TQ_PRIO_0_HI) + tq_count (chan, TQ_PRIO_1_LO);
|
||||||
|
|
||||||
|
snprintf (response, sizeof(response), "TXBUF:whatever");
|
||||||
|
|
||||||
|
(*sendfun) (chan, KISS_CMD_SET_HARDWARE, (unsigned char *)response, strlen(response), client);
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "TNC") == 0) { /* Identify software version. */
|
||||||
|
; // TODO...
|
||||||
|
}
|
||||||
|
else if (strcmp(command, "TRXS") == 0) {
|
||||||
|
; // TODO... BUSY
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("KISS Set Hardware invalid command.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("KISS Set Hardware expected the form COMMAND:[parameter[,parameter...]]\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
} /* end kiss_set_hardware */
|
||||||
|
|
||||||
|
#endif // ifndef KISSUTIL
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/*-------------------------------------------------------------------
|
||||||
|
@ -657,6 +811,7 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
||||||
|
|
||||||
void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len)
|
void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len)
|
||||||
{
|
{
|
||||||
|
#ifndef KISSUTIL
|
||||||
const char *direction [2] = { "from", "to" };
|
const char *direction [2] = { "from", "to" };
|
||||||
const char *prefix [2] = { "<<<", ">>>" };
|
const char *prefix [2] = { "<<<", ">>>" };
|
||||||
const char *function[16] = {
|
const char *function[16] = {
|
||||||
|
@ -664,11 +819,14 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int
|
||||||
"TXtail", "FullDuplex", "SetHardware", "Invalid 7",
|
"TXtail", "FullDuplex", "SetHardware", "Invalid 7",
|
||||||
"Invalid 8", "Invalid 9", "Invalid 10", "Invalid 11",
|
"Invalid 8", "Invalid 9", "Invalid 10", "Invalid 11",
|
||||||
"Invalid 12", "Invalid 13", "Invalid 14", "Return" };
|
"Invalid 12", "Invalid 13", "Invalid 14", "Return" };
|
||||||
|
#endif
|
||||||
|
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("\n");
|
|
||||||
|
|
||||||
|
#ifdef KISSUTIL
|
||||||
|
dw_printf ("From KISS TNC:\n");
|
||||||
|
#else
|
||||||
|
dw_printf ("\n");
|
||||||
if (special == NULL) {
|
if (special == NULL) {
|
||||||
unsigned char *p; /* to skip over FEND if present. */
|
unsigned char *p; /* to skip over FEND if present. */
|
||||||
|
|
||||||
|
@ -684,6 +842,7 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int
|
||||||
prefix[(int)fromto], special, direction[(int)fromto],
|
prefix[(int)fromto], special, direction[(int)fromto],
|
||||||
msg_len);
|
msg_len);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
hex_dump (pmsg, msg_len);
|
hex_dump (pmsg, msg_len);
|
||||||
|
|
||||||
} /* end kiss_debug_print */
|
} /* end kiss_debug_print */
|
||||||
|
|
29
kiss_frame.h
29
kiss_frame.h
|
@ -4,6 +4,25 @@
|
||||||
#include "audio.h" /* for struct audio_s */
|
#include "audio.h" /* for struct audio_s */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The first byte of a KISS frame has:
|
||||||
|
* channel in upper nybble.
|
||||||
|
* command in lower nybble.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define KISS_CMD_DATA_FRAME 0
|
||||||
|
#define KISS_CMD_TXDELAY 1
|
||||||
|
#define KISS_CMD_PERSISTENCE 2
|
||||||
|
#define KISS_CMD_SLOTTIME 3
|
||||||
|
#define KISS_CMD_TXTAIL 4
|
||||||
|
#define KISS_CMD_FULLDUPLEX 5
|
||||||
|
#define KISS_CMD_SET_HARDWARE 6
|
||||||
|
#define XKISS_CMD_DATA 12 // Not supported. http://he.fi/pub/oh7lzb/bpq/multi-kiss.pdf
|
||||||
|
#define XKISS_CMD_POLL 14 // Not supported.
|
||||||
|
#define KISS_CMD_END_KISS 15
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Special characters used by SLIP protocol.
|
* Special characters used by SLIP protocol.
|
||||||
*/
|
*/
|
||||||
|
@ -14,6 +33,7 @@
|
||||||
#define TFESC 0xDD
|
#define TFESC 0xDD
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum kiss_state_e {
|
enum kiss_state_e {
|
||||||
KS_SEARCHING = 0, /* Looking for FEND to start KISS frame. */
|
KS_SEARCHING = 0, /* Looking for FEND to start KISS frame. */
|
||||||
/* Must be 0 so we can simply zero whole structure to initialize. */
|
/* Must be 0 so we can simply zero whole structure to initialize. */
|
||||||
|
@ -41,17 +61,20 @@ typedef struct kiss_frame_s {
|
||||||
} kiss_frame_t;
|
} kiss_frame_t;
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef KISSUTIL
|
||||||
void kiss_frame_init (struct audio_s *pa);
|
void kiss_frame_init (struct audio_s *pa);
|
||||||
|
#endif
|
||||||
|
|
||||||
int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out);
|
int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out);
|
||||||
|
|
||||||
int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out);
|
int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out);
|
||||||
|
|
||||||
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,unsigned char*,int,int));
|
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int));
|
||||||
|
|
||||||
|
|
||||||
typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t;
|
typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t;
|
||||||
|
|
||||||
|
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int));
|
||||||
|
|
||||||
void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len);
|
void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len);
|
||||||
|
|
||||||
/* end kiss_frame.h */
|
/* end kiss_frame.h */
|
||||||
|
|
|
@ -0,0 +1,866 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 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: kissutil.c
|
||||||
|
*
|
||||||
|
* Purpose: Utility for talking to a KISS TNC.
|
||||||
|
*
|
||||||
|
* Description: Convert between KISS format and usual text representation.
|
||||||
|
* This might also serve as the starting point for an application
|
||||||
|
* that uses a KISS TNC.
|
||||||
|
* The TNC can be attached by TCP or a serial port.
|
||||||
|
*
|
||||||
|
* Usage: kissutil [ options ]
|
||||||
|
*
|
||||||
|
* Default is to connect to localhost:8001.
|
||||||
|
* See the "usage" functions at the bottom for details.
|
||||||
|
*
|
||||||
|
* FIXME: This is a rough prototype that needs more work.
|
||||||
|
* TODO: Prepare the "man" page.
|
||||||
|
* TODO: Add to User Guide.
|
||||||
|
*
|
||||||
|
*---------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
//#include "ax25_pad.h"
|
||||||
|
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "serial_port.h"
|
||||||
|
#include "kiss_frame.h"
|
||||||
|
#include "sock.h"
|
||||||
|
#include "dtime_now.h"
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: define in one place, use everywhere.
|
||||||
|
#if __WIN32__
|
||||||
|
#define THREAD_F unsigned __stdcall
|
||||||
|
#else
|
||||||
|
#define THREAD_F void *
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
#define DIR_CHAR "\\"
|
||||||
|
#else
|
||||||
|
#define DIR_CHAR "/"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static THREAD_F tnc_listen_net (void *arg);
|
||||||
|
static THREAD_F tnc_listen_serial (void *arg);
|
||||||
|
|
||||||
|
static void send_to_kiss_tnc (int chan, int cmd, char *data, int dlen);
|
||||||
|
static void hex_dump (unsigned char *p, int len);
|
||||||
|
|
||||||
|
|
||||||
|
// Why didn't I use send/recv for Linux?
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
#define SOCK_SEND(s,data,size) send(s,data,size,0)
|
||||||
|
#define SOCK_RECV(s,data,size) recv(s,data,size,0)
|
||||||
|
#else
|
||||||
|
#define SOCK_SEND(s,data,size) write(s,data,size)
|
||||||
|
#define SOCK_RECV(s,data,size) read(s,data,size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void usage(void);
|
||||||
|
static void usage2(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Obtained from the command line. */
|
||||||
|
|
||||||
|
static char hostname[50] = "localhost"; /* -h option. */
|
||||||
|
/* DNS host name or IPv4 address. */
|
||||||
|
/* Some of the code is there for IPv6 but */
|
||||||
|
/* it needs more work. */
|
||||||
|
/* Defaults to "localhost" if not specified. */
|
||||||
|
|
||||||
|
static char port[30] = "8001"; /* -p option. */
|
||||||
|
/* If it begins with a digit, it is considered */
|
||||||
|
/* a TCP port number at the hostname. */
|
||||||
|
/* Otherwise, we treat it as a serial port name. */
|
||||||
|
|
||||||
|
static int using_tcp = 1; /* Are we using TCP or serial port for TNC? */
|
||||||
|
/* Use corresponding one of the next two. */
|
||||||
|
/* This is derived from the first character of port. */
|
||||||
|
|
||||||
|
static int server_sock = -1; /* File descriptor for socket interface. */
|
||||||
|
/* Set to -1 if not used. */
|
||||||
|
/* (Don't use SOCKET type because it is unsigned.) */
|
||||||
|
|
||||||
|
static MYFDTYPE serial_fd = (MYFDTYPE)(-1); /* Serial port handle. */
|
||||||
|
|
||||||
|
static int serial_speed = 9600; /* -s option. */
|
||||||
|
/* Serial port speed, bps. */
|
||||||
|
|
||||||
|
static int verbose = 0; /* -v option. */
|
||||||
|
/* Display the KISS protocol in hexadecimal for troubleshooting. */
|
||||||
|
|
||||||
|
static char transmit_queue[120] = ""; /* -t option */
|
||||||
|
/* When specified, files are read from this directory */
|
||||||
|
/* rather than using stdin. Each file is one or more */
|
||||||
|
/* lines in the standard monitoring format. */
|
||||||
|
|
||||||
|
static char receive_queue[120] = ""; /* -r option */
|
||||||
|
/* When specified, each received frame is stored as a file */
|
||||||
|
/* with a unique name here. */
|
||||||
|
/* Directory must already exist; we won't create it. */
|
||||||
|
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
#define THREAD_F unsigned __stdcall
|
||||||
|
#else
|
||||||
|
#define THREAD_F void *
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
static HANDLE tnc_th;
|
||||||
|
#else
|
||||||
|
static pthread_t tnc_tid;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void process_input (char *stuff);
|
||||||
|
|
||||||
|
/* Trim any CR, LF from the end of line. */
|
||||||
|
|
||||||
|
static void trim (char *stuff)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
p = stuff + strlen(stuff) - 1;
|
||||||
|
while (strlen(stuff) > 0 && (*p == '\r' || *p == '\n')) {
|
||||||
|
*p = '\0';
|
||||||
|
p--;
|
||||||
|
}
|
||||||
|
} /* end trim */
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: main
|
||||||
|
*
|
||||||
|
* Purpose: Attach to KISS TNC and exchange information.
|
||||||
|
*
|
||||||
|
* Usage: See "usage" functions at end.
|
||||||
|
*
|
||||||
|
*---------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
text_color_init (0); // Turn off text color.
|
||||||
|
// It could interfere with trying to pipe stdout to some other application.
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
|
||||||
|
#else
|
||||||
|
int e;
|
||||||
|
setlinebuf (stdout); // TODO: What is the Windows equivalent?
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract command line args.
|
||||||
|
*/
|
||||||
|
while (1) {
|
||||||
|
int option_index = 0;
|
||||||
|
int c;
|
||||||
|
static struct option long_options[] = {
|
||||||
|
//{"future1", 1, 0, 0},
|
||||||
|
//{"future2", 0, 0, 0},
|
||||||
|
//{"future3", 1, 0, 'c'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ':' following option character means arg is required. */
|
||||||
|
|
||||||
|
c = getopt_long(argc, argv, "h:p:s:vt:r:",
|
||||||
|
long_options, &option_index);
|
||||||
|
if (c == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
|
||||||
|
case 'h': /* -h for hostname. */
|
||||||
|
strlcpy (hostname, optarg, sizeof(hostname));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p': /* -p for port, either TCP or serial device. */
|
||||||
|
strlcpy (port, optarg, sizeof(port));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's': /* -s for serial port speed. */
|
||||||
|
serial_speed = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'v': /* -v for verbose. */
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't': /* -t for transmit queue directory. */
|
||||||
|
strlcpy (transmit_queue, optarg, sizeof(transmit_queue));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r': /* -t for receive queue directory. */
|
||||||
|
strlcpy (receive_queue, optarg, sizeof(receive_queue));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
/* Unknown option message was already printed. */
|
||||||
|
usage ();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Should not be here. */
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf("?? getopt returned character code 0%o ??\n", c);
|
||||||
|
usage ();
|
||||||
|
}
|
||||||
|
} /* end while(1) for options */
|
||||||
|
|
||||||
|
if (optind < argc) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Warning: Unused command line arguments are ignored.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If receive queue directory was specified, make sure that it exists.
|
||||||
|
*/
|
||||||
|
if (strlen(receive_queue) > 0) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If port begins with digit, consider it to be TCP. */
|
||||||
|
/* Otherwise, treat as serial port name. */
|
||||||
|
|
||||||
|
using_tcp = isdigit(port[0]);
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
if (using_tcp) {
|
||||||
|
tnc_th = (HANDLE)_beginthreadex (NULL, 0, tnc_listen_net, (void *)99, 0, NULL);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tnc_th = (HANDLE)_beginthreadex (NULL, 0, tnc_listen_serial, (void *)99, 0, NULL);
|
||||||
|
}
|
||||||
|
if (tnc_th == NULL) {
|
||||||
|
printf ("Internal error: Could not create TNC listen thread.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (using_tcp) {
|
||||||
|
e = pthread_create (&tnc_tid, NULL, tnc_listen_net, (void *)(long)99);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
e = pthread_create (&tnc_tid, NULL, tnc_listen_serial, (void *)(long)99);
|
||||||
|
}
|
||||||
|
if (e != 0) {
|
||||||
|
perror("Internal error: Could not create TNC listen thread.");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process keyboard or other input source.
|
||||||
|
*/
|
||||||
|
char stuff[1000];
|
||||||
|
|
||||||
|
if (strlen(transmit_queue) > 0) {
|
||||||
|
/*
|
||||||
|
* Process and delete all files in specified directory.
|
||||||
|
* When done, sleep for a second and try again.
|
||||||
|
* This doesn't take them in any particular order.
|
||||||
|
* A future enhancement might sort by name or timestamp.
|
||||||
|
*/
|
||||||
|
while (1) {
|
||||||
|
DIR *dp;
|
||||||
|
struct dirent *ep;
|
||||||
|
|
||||||
|
//text_color_set(DW_COLOR_DEBUG);
|
||||||
|
//dw_printf("Get directory listing...\n");
|
||||||
|
|
||||||
|
dp = opendir (transmit_queue);
|
||||||
|
if (dp != NULL) {
|
||||||
|
while ((ep = readdir(dp)) != NULL) {
|
||||||
|
char path [300];
|
||||||
|
FILE *fp;
|
||||||
|
if (ep->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("Processing %s for transmit...\n", ep->d_name);
|
||||||
|
strlcpy (path, transmit_queue, sizeof(path));
|
||||||
|
strlcat (path, DIR_CHAR, sizeof(path));
|
||||||
|
strlcat (path, ep->d_name, sizeof(path));
|
||||||
|
fp = fopen (path, "r");
|
||||||
|
if (fp != NULL) {
|
||||||
|
while (fgets(stuff, sizeof(stuff), fp) != NULL) {
|
||||||
|
trim (stuff);
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("%s\n", stuff);
|
||||||
|
// TODO: Don't delete file if errors encountered?
|
||||||
|
process_input (stuff);
|
||||||
|
}
|
||||||
|
fclose (fp);
|
||||||
|
unlink (path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf("Can't open for read: %s\n", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir (dp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf("Can't access transmit queue directory %s. Quitting.\n", transmit_queue);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
SLEEP_SEC (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
* Using stdin.
|
||||||
|
*/
|
||||||
|
while (fgets(stuff, sizeof(stuff), stdin) != NULL) {
|
||||||
|
process_input (stuff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end main */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: process_input
|
||||||
|
*
|
||||||
|
* Purpose: Process frames/commands from user, either interactively or from files.
|
||||||
|
*
|
||||||
|
* Inputs: stuff - A frame is in usual format like SOURCE>DEST,DIGI:whatever.
|
||||||
|
* Commands begin with lower case letter.
|
||||||
|
* Note that it can be modified by this function.
|
||||||
|
*
|
||||||
|
* Later Enhancement: Return success/fail status. The transmit queue processing might want
|
||||||
|
* to preserve files that were not processed as expected.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void process_input (char *stuff)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
int chan = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove any end of line character(s).
|
||||||
|
*/
|
||||||
|
trim (stuff);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Optional prefix, like "[9]" to specify channel. TODO FIXME
|
||||||
|
*/
|
||||||
|
p = stuff;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it starts with upper case letter or digit, assume it is an AX.25 frame in monitor format.
|
||||||
|
* Lower case is a command (e.g. Persistence or set Hardware).
|
||||||
|
* Anything else, print explanation of what is expected.
|
||||||
|
*/
|
||||||
|
if (isupper(*p) || isdigit(*p)) {
|
||||||
|
|
||||||
|
// Parse the "TNC2 monitor format" and convert to AX.25 frame.
|
||||||
|
|
||||||
|
unsigned char frame_data[AX25_MAX_PACKET_LEN];
|
||||||
|
packet_t pp = ax25_from_text (p, 1);
|
||||||
|
if (pp != NULL) {
|
||||||
|
int frame_len = ax25_pack (pp, frame_data);
|
||||||
|
send_to_kiss_tnc (chan, KISS_CMD_DATA_FRAME, (char*)frame_data, frame_len);
|
||||||
|
ax25_delete (pp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("ERROR! Could not convert to AX.25 frame: %s\n", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (islower(*p)) {
|
||||||
|
char value;
|
||||||
|
|
||||||
|
switch (*p) {
|
||||||
|
case 'd': // txDelay, 10ms units
|
||||||
|
// TODO: should check for range 0 - 255.
|
||||||
|
value = atoi(p+1);
|
||||||
|
send_to_kiss_tnc (chan, KISS_CMD_TXDELAY, &value, 1);
|
||||||
|
break;
|
||||||
|
case 'p': // Persistence
|
||||||
|
value = atoi(p+1);
|
||||||
|
send_to_kiss_tnc (chan, KISS_CMD_PERSISTENCE, &value, 1);
|
||||||
|
break;
|
||||||
|
case 's': // Slot time, 10ms units
|
||||||
|
value = atoi(p+1);
|
||||||
|
send_to_kiss_tnc (chan, KISS_CMD_SLOTTIME, &value, 1);
|
||||||
|
break;
|
||||||
|
case 't': // txTelay, 10ms units
|
||||||
|
value = atoi(p+1);
|
||||||
|
send_to_kiss_tnc (chan, KISS_CMD_TXTAIL, &value, 1);
|
||||||
|
break;
|
||||||
|
case 'f': // Full duplex
|
||||||
|
value = atoi(p+1);
|
||||||
|
send_to_kiss_tnc (chan, KISS_CMD_FULLDUPLEX, &value, 1);
|
||||||
|
break;
|
||||||
|
case 'h': // set Hardware
|
||||||
|
p++;
|
||||||
|
while (*p != '\0' && isspace(*p)) { p++; }
|
||||||
|
send_to_kiss_tnc (chan, KISS_CMD_SET_HARDWARE, p, strlen(p));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Invalid command. Must be one of d p s t f h.\n");
|
||||||
|
usage2 ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
usage2 ();
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end process_input */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: send_to_kiss_tnc
|
||||||
|
*
|
||||||
|
* Purpose: Encapsulate the data/command, into a KISS frame, and send to the TNC.
|
||||||
|
*
|
||||||
|
* Inputs: chan - channel number.
|
||||||
|
*
|
||||||
|
* cmd - KISS_CMD_DATA_FRAME, KISS_CMD_SET_HARDWARE, etc.
|
||||||
|
*
|
||||||
|
* data - Information for KISS frame.
|
||||||
|
*
|
||||||
|
* dlen - Number of bytes in data.
|
||||||
|
*
|
||||||
|
* Description: Encapsulate as KISS frame and send to TNC.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void send_to_kiss_tnc (int chan, int cmd, char *data, int dlen)
|
||||||
|
{
|
||||||
|
unsigned char temp[1000];
|
||||||
|
unsigned char kissed[2000];
|
||||||
|
int klen;
|
||||||
|
|
||||||
|
if (chan < 0 || chan > 15) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("ERROR - Invalid channel %d - must be in range 0 to 15.\n", chan);
|
||||||
|
chan = 0;
|
||||||
|
}
|
||||||
|
if (cmd < 0 || cmd > 15) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("ERROR - Invalid command %d - must be in range 0 to 15.\n", cmd);
|
||||||
|
cmd = 0;
|
||||||
|
}
|
||||||
|
if (dlen < 0 || dlen > (int)(sizeof(temp)-1)) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("ERROR - Invalid data length %d - must be in range 0 to %d.\n", dlen, sizeof(temp)-1);
|
||||||
|
dlen = sizeof(temp)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp[0] = (chan << 4) | cmd;
|
||||||
|
memcpy (temp+1, data, dlen);
|
||||||
|
|
||||||
|
klen = kiss_encapsulate(temp, dlen+1, kissed);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("Sending to KISS TNC:\n");
|
||||||
|
hex_dump (kissed, klen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Should check for non -1 server_sock or serial_fd.
|
||||||
|
// Might need to delay when not using interactive input.
|
||||||
|
|
||||||
|
if (using_tcp) {
|
||||||
|
SOCK_SEND(server_sock, (char*)kissed, klen);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
serial_port_write (serial_fd, (char*)kissed, klen);
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end send_to_kiss_tnc */
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: tnc_listen_net
|
||||||
|
*
|
||||||
|
* Purpose: Connect to KISS TNC via TCP port.
|
||||||
|
* Print everything it sends to us.
|
||||||
|
*
|
||||||
|
* Inputs: arg - Currently not used.
|
||||||
|
*
|
||||||
|
* Global In: host
|
||||||
|
* port
|
||||||
|
*
|
||||||
|
* Global Out: server_sock - Needed to send to the TNC.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
//#define MAX_HOSTS 30
|
||||||
|
|
||||||
|
static THREAD_F tnc_listen_net (void *arg)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
char ipaddr_str[SOCK_IPADDR_LEN]; // Text form of IP address.
|
||||||
|
char data[4096];
|
||||||
|
int allow_ipv6 = 0; // Maybe someday.
|
||||||
|
int debug = 0;
|
||||||
|
int client = 0; // Not used in this situation.
|
||||||
|
kiss_frame_t kstate;
|
||||||
|
|
||||||
|
memset (&kstate, 0, sizeof(kstate));
|
||||||
|
|
||||||
|
err = sock_init ();
|
||||||
|
if (err < 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Network interface failure. Can't go on.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connect to network KISS TNC.
|
||||||
|
*/
|
||||||
|
server_sock = sock_connect (hostname, port, "TCP KISS TNC", allow_ipv6, debug, ipaddr_str);
|
||||||
|
|
||||||
|
if (server_sock == -1) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
// Should have been a message already. What else is there to say?
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print what we get from TNC.
|
||||||
|
*/
|
||||||
|
int len;
|
||||||
|
|
||||||
|
while ((len = SOCK_RECV (server_sock, (char*)(data), sizeof(data))) > 0) {
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < len; j++) {
|
||||||
|
|
||||||
|
// Feed in one byte at a time.
|
||||||
|
// kiss_process_msg is called when a complete frame has been accumulated.
|
||||||
|
|
||||||
|
// When verbose is specified, we get debug output like this:
|
||||||
|
//
|
||||||
|
// <<< Data frame from KISS client application, port 0, total length = 46
|
||||||
|
// 000: c0 00 82 a0 88 ae 62 6a e0 ae 84 64 9e a6 b4 ff ......bj...d....
|
||||||
|
// ...
|
||||||
|
// It says "from KISS client application" because it was written
|
||||||
|
// on the assumption it was being used in only one direction.
|
||||||
|
// Not worried enough about it to do anything at this time.
|
||||||
|
|
||||||
|
kiss_rec_byte (&kstate, data[j], verbose, client, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Read error from TCP KISS TNC. Terminating.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
|
||||||
|
// For the IGate we would loop around and try to reconnect but
|
||||||
|
// it doesn't seem appropriate here. Just quit.
|
||||||
|
|
||||||
|
} /* end tnc_listen_net */
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: tnc_listen_serial
|
||||||
|
*
|
||||||
|
* Purpose: Connect to KISS TNC via serial port.
|
||||||
|
* Print everything it sends to us.
|
||||||
|
*
|
||||||
|
* Inputs: arg - Currently not used.
|
||||||
|
*
|
||||||
|
* Global In: port
|
||||||
|
* serial_speed
|
||||||
|
*
|
||||||
|
* Global Out: serial_fd - Need for sending to the TNC.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static THREAD_F tnc_listen_serial (void *arg)
|
||||||
|
{
|
||||||
|
int client = 0;
|
||||||
|
kiss_frame_t kstate;
|
||||||
|
|
||||||
|
memset (&kstate, 0, sizeof(kstate));
|
||||||
|
|
||||||
|
serial_fd = serial_port_open (port, serial_speed);
|
||||||
|
|
||||||
|
if (serial_fd == MYFDERROR) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf("Unable to connect to KISS TNC serial port %s.\n", port);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read and print.
|
||||||
|
*/
|
||||||
|
while (1) {
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
ch = serial_port_get1(serial_fd);
|
||||||
|
|
||||||
|
if (ch < 0) {
|
||||||
|
dw_printf("Read error from serial port KISS TNC.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed in one byte at a time.
|
||||||
|
// kiss_process_msg is called when a complete frame has been accumulated.
|
||||||
|
|
||||||
|
kiss_rec_byte (&kstate, ch, verbose, client, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end tnc_listen_serial */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: kiss_process_msg
|
||||||
|
*
|
||||||
|
* Purpose: Process a frame from the KISS TNC.
|
||||||
|
* This is called when a complete frame has been accumulated.
|
||||||
|
* In this case, we simply print it.
|
||||||
|
*
|
||||||
|
* Inputs: kiss_msg - Kiss frame with FEND and escapes removed.
|
||||||
|
* The first byte contains channel and command.
|
||||||
|
*
|
||||||
|
* kiss_len - Number of bytes including the command.
|
||||||
|
*
|
||||||
|
* debug - Debug option is selected.
|
||||||
|
*
|
||||||
|
* client - Not used in this case.
|
||||||
|
*
|
||||||
|
* sendfun - Not used in this case.
|
||||||
|
*
|
||||||
|
*-----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
|
||||||
|
{
|
||||||
|
int chan;
|
||||||
|
int cmd;
|
||||||
|
packet_t pp;
|
||||||
|
alevel_t alevel;
|
||||||
|
|
||||||
|
chan = (kiss_msg[0] >> 4) & 0xf;
|
||||||
|
cmd = kiss_msg[0] & 0xf;
|
||||||
|
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case KISS_CMD_DATA_FRAME: /* 0 = Data Frame */
|
||||||
|
|
||||||
|
memset (&alevel, 0, sizeof(alevel));
|
||||||
|
pp = ax25_from_frame (kiss_msg+1, kiss_len-1, alevel);
|
||||||
|
if (pp == NULL) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
printf ("ERROR - Invalid KISS data frame from TNC.\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char addrs[500];
|
||||||
|
unsigned char *pinfo;
|
||||||
|
int info_len;
|
||||||
|
|
||||||
|
ax25_format_addrs (pp, addrs);
|
||||||
|
|
||||||
|
info_len = ax25_get_info (pp, &pinfo);
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_REC);
|
||||||
|
|
||||||
|
if (chan != 0) {
|
||||||
|
dw_printf ("[%d] ", chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
dw_printf ("%s", addrs); /* stations followed by : */
|
||||||
|
|
||||||
|
// Safe print will replace any unprintable characters with
|
||||||
|
// hexadecimal representation.
|
||||||
|
|
||||||
|
ax25_safe_print ((char *)pinfo, info_len, 0);
|
||||||
|
dw_printf ("\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add to receive queue directory if specified.
|
||||||
|
* File name will be based on time to 0.01 sec resolution. ??? TBD
|
||||||
|
*/
|
||||||
|
if (strlen(receive_queue) > 0) {
|
||||||
|
char fname [30];
|
||||||
|
char path [300];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
snprintf (fname, sizeof(fname), "%.0f", dtime_now() * 100.);
|
||||||
|
|
||||||
|
strlcpy (path, receive_queue, sizeof(path));
|
||||||
|
strlcat (path, DIR_CHAR, sizeof(path));
|
||||||
|
strlcat (path, fname, sizeof(path));
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("save received frame to %s\n", path);
|
||||||
|
fp = fopen (path, "w");
|
||||||
|
if (fp != NULL) {
|
||||||
|
fprintf (fp, "%s%s\n", addrs, pinfo);
|
||||||
|
fclose (fp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Unable to open for write: %s\n", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ax25_delete (pp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KISS_CMD_SET_HARDWARE: /* 6 = TNC specific */
|
||||||
|
|
||||||
|
kiss_msg[kiss_len] = '\0';
|
||||||
|
text_color_set(DW_COLOR_REC);
|
||||||
|
// TODO: Just precede by "h " for in/out symmetry?
|
||||||
|
printf ("KISS protocol set hardware \"%s\", channel %d\n", (char*)(kiss_msg+1), chan);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KISS_CMD_TXDELAY: /* 1 = TXDELAY */
|
||||||
|
case KISS_CMD_PERSISTENCE: /* 2 = Persistence */
|
||||||
|
case KISS_CMD_SLOTTIME: /* 3 = SlotTime */
|
||||||
|
case KISS_CMD_TXTAIL: /* 4 = TXtail */
|
||||||
|
case KISS_CMD_FULLDUPLEX: /* 5 = FullDuplex */
|
||||||
|
case KISS_CMD_END_KISS: /* 15 = End KISS mode, port should be 15. */
|
||||||
|
default:
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
printf ("Unexpected KISS command %d, channel %d\n", cmd, chan);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end kiss_process_msg */
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: We have multiple copies of this. Move to some misc file.
|
||||||
|
|
||||||
|
void hex_dump (unsigned char *p, int len)
|
||||||
|
{
|
||||||
|
int n, i, offset;
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
while (len > 0) {
|
||||||
|
n = len < 16 ? len : 16;
|
||||||
|
printf (" %03x: ", offset);
|
||||||
|
for (i=0; i<n; i++) {
|
||||||
|
printf (" %02x", p[i]);
|
||||||
|
}
|
||||||
|
for (i=n; i<16; i++) {
|
||||||
|
printf (" ");
|
||||||
|
}
|
||||||
|
printf (" ");
|
||||||
|
for (i=0; i<n; i++) {
|
||||||
|
printf ("%c", isprint(p[i]) ? p[i] : '.');
|
||||||
|
}
|
||||||
|
printf ("\n");
|
||||||
|
p += 16;
|
||||||
|
offset += 16;
|
||||||
|
len -= 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\n");
|
||||||
|
dw_printf ("kissutil - Utility for testing a KISS TNC.\n");
|
||||||
|
dw_printf ("\n");
|
||||||
|
dw_printf ("Convert between KISS format and usual text representation.\n");
|
||||||
|
dw_printf ("The TNC can be attached by TCP or a serial port.\n");
|
||||||
|
dw_printf ("\n");
|
||||||
|
dw_printf ("Usage: kissutil [ options ]\n");
|
||||||
|
dw_printf ("\n");
|
||||||
|
dw_printf (" -h hostname of TCP KISS TNC, default localhost.\n");
|
||||||
|
dw_printf (" -p port, default 8001.\n");
|
||||||
|
dw_printf (" If it does not start with a digit, it is\n");
|
||||||
|
dw_printf (" a serial port. e.g. /dev/ttyAMA0 or COM3.\n");
|
||||||
|
dw_printf (" -s Serial port speed, default 9600.\n");
|
||||||
|
dw_printf (" -v Verbose. Show the KISS frame contents.\n");
|
||||||
|
dw_printf (" -t Transmit queue directory. Processs and delete files here.\n");
|
||||||
|
dw_printf (" -r Receive queue directory. Store received frames here.\n");
|
||||||
|
usage2();
|
||||||
|
exit (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage2 (void)
|
||||||
|
{
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\n");
|
||||||
|
dw_printf ("Input, starting with upper case letter or digit, is assumed\n");
|
||||||
|
dw_printf ("to be an AX.25 frame in the usual TNC2 monitoring format.\n");
|
||||||
|
dw_printf ("\n");
|
||||||
|
dw_printf ("Input, starting with a lower case letter is a commmand.\n");
|
||||||
|
dw_printf ("Whitespace, as shown in examples, is optional.\n");
|
||||||
|
dw_printf ("\n");
|
||||||
|
dw_printf (" letter meaning example\n");
|
||||||
|
dw_printf (" ------ ------- -------\n");
|
||||||
|
dw_printf (" d txDelay, 10ms units d 30\n");
|
||||||
|
dw_printf (" p Persistence p 63\n");
|
||||||
|
dw_printf (" s Slot time, 10ms units s 10\n");
|
||||||
|
dw_printf (" t txTail, 10ms units t 5\n");
|
||||||
|
dw_printf (" f Full duplex f 0\n");
|
||||||
|
dw_printf (" h set Hardware h T.B.D.\n");
|
||||||
|
dw_printf ("\n");
|
||||||
|
dw_printf (" Lines may be preceded by the form \"[9]\" to indicate a\n");
|
||||||
|
dw_printf (" channel other than the default 0.\n");
|
||||||
|
dw_printf ("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end kissutil.c */
|
|
@ -0,0 +1,438 @@
|
||||||
|
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 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: sock.c
|
||||||
|
*
|
||||||
|
* Purpose: Functions for TCP sockets.
|
||||||
|
*
|
||||||
|
* Description: These are used for connecting between different applications,
|
||||||
|
* possibly on different hosts.
|
||||||
|
*
|
||||||
|
* New in version 1.5:
|
||||||
|
* Duplicate code already exists in multiple places and I was about
|
||||||
|
* to add another one. Instead, we will gather the common code here
|
||||||
|
* instead of having yet another copy.
|
||||||
|
*
|
||||||
|
*---------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.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 <time.h>
|
||||||
|
|
||||||
|
#include "textcolor.h"
|
||||||
|
#include "sock.h"
|
||||||
|
|
||||||
|
static void shuffle (struct addrinfo *host[], int nhosts);
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: sock_init
|
||||||
|
*
|
||||||
|
* Purpose: Preparation before using socket interface.
|
||||||
|
*
|
||||||
|
* Inputs: none
|
||||||
|
*
|
||||||
|
* Returns: 0 for success, -1 for error.
|
||||||
|
*
|
||||||
|
* Errors: Message is printed. I've never seen it fail.
|
||||||
|
*
|
||||||
|
* Description: Doesn't do anything for Linux.
|
||||||
|
*
|
||||||
|
* TODO: Use this instead of own copy in aclients.c
|
||||||
|
* TODO: Use this instead of own copy in appserver.c
|
||||||
|
* TODO: Use this instead of own copy in audio_win.c
|
||||||
|
* TODO: Use this instead of own copy in igate.c
|
||||||
|
* TODO: Use this instead of own copy in kissnet.c
|
||||||
|
* TODO: Use this instead of own copy in kissutil.c
|
||||||
|
* TODO: Use this instead of own copy in server.c
|
||||||
|
* TODO: Use this instead of own copy in tnctest.c
|
||||||
|
* TODO: Use this instead of own copy in ttcalc.c
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int sock_init(void)
|
||||||
|
{
|
||||||
|
#if __WIN32__
|
||||||
|
WSADATA wsadata;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = WSAStartup (MAKEWORD(2,2), &wsadata);
|
||||||
|
if (err != 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf("WSAStartup failed, error: %d\n", err);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
} /* end sock_init */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: sock_connect
|
||||||
|
*
|
||||||
|
* Purpose: Connect to given host / port.
|
||||||
|
*
|
||||||
|
* Inputs: hostname - Host name or IP address.
|
||||||
|
*
|
||||||
|
* port - TCP port as text string.
|
||||||
|
*
|
||||||
|
* description - Description of the remote server to be used in error message.
|
||||||
|
* e.g. "APRS-IS (Igate) Server" or "TCP KISS TNC".
|
||||||
|
*
|
||||||
|
* allow_ipv6 - True to allow IPv6. Otherwise only IPv4.
|
||||||
|
*
|
||||||
|
* debug - Print debugging information.
|
||||||
|
*
|
||||||
|
* Outputs: ipaddr_str - The IP address, in text form, is placed here in case
|
||||||
|
* the caller wants it. Should be SOCK_IPADDR_LEN bytes.
|
||||||
|
*
|
||||||
|
* Returns: Socket Handle / file descriptor or -1 for error.
|
||||||
|
*
|
||||||
|
* Errors: (1) Can't find address for given host name.
|
||||||
|
*
|
||||||
|
* Print error and return -1.
|
||||||
|
*
|
||||||
|
* (2) Can't connect to one of the address(es).
|
||||||
|
*
|
||||||
|
* Silently try the next one.
|
||||||
|
*
|
||||||
|
* (3) Can't connect to any of the address(es).
|
||||||
|
*
|
||||||
|
* Nothing is printed for success. The caller might do that
|
||||||
|
* to provide confirmation on what is happening.
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int sock_connect (char *hostname, char *port, char *description, int allow_ipv6, int debug, char ipaddr_str[SOCK_IPADDR_LEN])
|
||||||
|
{
|
||||||
|
#define MAX_HOSTS 50
|
||||||
|
|
||||||
|
struct addrinfo hints;
|
||||||
|
struct addrinfo *ai_head = NULL;
|
||||||
|
struct addrinfo *ai;
|
||||||
|
struct addrinfo *hosts[MAX_HOSTS];
|
||||||
|
int num_hosts, n;
|
||||||
|
int err;
|
||||||
|
int server_sock = -1;
|
||||||
|
|
||||||
|
strlcpy (ipaddr_str, "???", SOCK_IPADDR_LEN);
|
||||||
|
memset (&hints, 0, sizeof(hints));
|
||||||
|
|
||||||
|
hints.ai_family = AF_UNSPEC; /* Allow either IPv4 or IPv6. */
|
||||||
|
if ( ! allow_ipv6) {
|
||||||
|
hints.ai_family = AF_INET; /* IPv4 only. */
|
||||||
|
}
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_protocol = IPPROTO_TCP;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, we need to look up the DNS name to get IP address.
|
||||||
|
* It is possible to have multiple addresses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ai_head = NULL;
|
||||||
|
err = getaddrinfo(hostname, port, &hints, &ai_head);
|
||||||
|
if (err != 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
#if __WIN32__
|
||||||
|
dw_printf ("Can't get address for %s, %s, err=%d\n",
|
||||||
|
description, hostname, WSAGetLastError());
|
||||||
|
#else
|
||||||
|
dw_printf ("Can't get address for %s, %s, %s\n",
|
||||||
|
description, hostname, gai_strerror(err));
|
||||||
|
#endif
|
||||||
|
freeaddrinfo(ai_head);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("getaddrinfo returns:\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
num_hosts = 0;
|
||||||
|
for (ai = ai_head; ai != NULL; ai = ai->ai_next) {
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
sock_ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, SOCK_IPADDR_LEN);
|
||||||
|
dw_printf (" %s\n", ipaddr_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts[num_hosts] = ai;
|
||||||
|
if (num_hosts < MAX_HOSTS) num_hosts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffle (hosts, num_hosts);
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("addresses for hostname:\n");
|
||||||
|
for (n=0; n<num_hosts; n++) {
|
||||||
|
sock_ia_to_text (hosts[n]->ai_family, hosts[n]->ai_addr, ipaddr_str, SOCK_IPADDR_LEN);
|
||||||
|
dw_printf (" %s\n", ipaddr_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try each address until we find one that is successful.
|
||||||
|
*/
|
||||||
|
for (n = 0; n < num_hosts; n++) {
|
||||||
|
#if __WIN32__
|
||||||
|
SOCKET is;
|
||||||
|
#else
|
||||||
|
int is;
|
||||||
|
#endif
|
||||||
|
ai = hosts[n];
|
||||||
|
|
||||||
|
sock_ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, SOCK_IPADDR_LEN);
|
||||||
|
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, hostname, ipaddr_str, port);
|
||||||
|
#endif
|
||||||
|
closesocket (is);
|
||||||
|
is = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (err != 0) {
|
||||||
|
#if DEBUGx
|
||||||
|
printf("Connect to %s on %s (%s), port %s failed.\n",
|
||||||
|
description, hostname, ipaddr_str, port);
|
||||||
|
#endif
|
||||||
|
(void) close (is);
|
||||||
|
is = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IGate documentation says to use no delay. */
|
||||||
|
/* Does it really make a difference? */
|
||||||
|
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. */
|
||||||
|
|
||||||
|
|
||||||
|
server_sock = is;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(ai_head);
|
||||||
|
|
||||||
|
// no, caller should handle this.
|
||||||
|
// function should be generally be silent unless debug option.
|
||||||
|
|
||||||
|
if (server_sock == -1) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf("Unable to connect to %s at %s (%s), port %s\n",
|
||||||
|
description, hostname, ipaddr_str, port );
|
||||||
|
}
|
||||||
|
|
||||||
|
return (server_sock);
|
||||||
|
|
||||||
|
} /* end sock_connect */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: sock_bind
|
||||||
|
*
|
||||||
|
* Purpose: We also have a bunch of duplicate code for the server side.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
*
|
||||||
|
* TODO: Use this instead of own copy in audio.c
|
||||||
|
* TODO: Use this instead of own copy in audio_portaudio.c
|
||||||
|
* TODO: Use this instead of own copy in audio_win.c
|
||||||
|
* TODO: Use this instead of own copy in kissnet.c
|
||||||
|
* TODO: Use this instead of own copy in server.c
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
// Not implemented yet.
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Addresses don't get mixed up very well.
|
||||||
|
* IPv6 always shows up last so we'd probably never
|
||||||
|
* end up using any of them for APRS-IS server.
|
||||||
|
* Add our own shuffle.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void shuffle (struct addrinfo *host[], int nhosts)
|
||||||
|
{
|
||||||
|
int j, k;
|
||||||
|
|
||||||
|
assert (RAND_MAX >= nhosts); /* for % to work right */
|
||||||
|
|
||||||
|
if (nhosts < 2) return;
|
||||||
|
|
||||||
|
srand (time(NULL));
|
||||||
|
|
||||||
|
for (j=0; j<nhosts; j++) {
|
||||||
|
k = rand() % nhosts;
|
||||||
|
assert (k >=0 && k<nhosts);
|
||||||
|
if (j != k) {
|
||||||
|
struct addrinfo *temp;
|
||||||
|
temp = host[j]; host[j] = host[k]; host[k] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: sock_ia_to_text
|
||||||
|
*
|
||||||
|
* Purpose: Convert binary IP Address to text form.
|
||||||
|
*
|
||||||
|
* Inputs: Family - AF_INET or AF_INET6.
|
||||||
|
*
|
||||||
|
* pAddr - Pointer to the IP Address storage location.
|
||||||
|
*
|
||||||
|
* StringBufSize - Number of bytes in pStringBuf.
|
||||||
|
*
|
||||||
|
* Outputs: pStringBuf - Text result is placed here.
|
||||||
|
*
|
||||||
|
* Returns: pStringBuf
|
||||||
|
*
|
||||||
|
* Description: Can't use InetNtop because it is supported only on Windows Vista and later.
|
||||||
|
* At one time Dire Wolf worked on Win XP. Haven't tried it for years.
|
||||||
|
* Maybe some other dependency on a newer OS version has crept in.
|
||||||
|
*
|
||||||
|
* TODO: Use this instead of own copy in aclients.c
|
||||||
|
* TODO: Use this instead of own copy in appserver.c
|
||||||
|
* TODO: Use this instead of own copy in igate.c
|
||||||
|
* TODO: Use this instead of own copy in tnctest.c
|
||||||
|
* TODO: Use this instead of own copy in ttcalc.c
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
char *sock_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__
|
||||||
|
snprintf (pStringBuf, StringBufSize, "%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__
|
||||||
|
snprintf (pStringBuf, StringBufSize, "%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:
|
||||||
|
snprintf (pStringBuf, StringBufSize, "Invalid address family!");
|
||||||
|
}
|
||||||
|
return pStringBuf;
|
||||||
|
|
||||||
|
} /* end sock_ia_to_text */
|
||||||
|
|
||||||
|
/* end sock.c */
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
/* sock.h - Socket helper functions. */
|
||||||
|
|
||||||
|
#ifndef SOCK_H
|
||||||
|
#define SOCK_H 1
|
||||||
|
|
||||||
|
#define SOCK_IPADDR_LEN 48 // Size of string to hold IPv4 or IPv6 address.
|
||||||
|
// I think 40 would be adequate but we'll make
|
||||||
|
// it a little larger just to be safe.
|
||||||
|
// Use INET6_ADDRSTRLEN (from netinet/in.h) instead?
|
||||||
|
|
||||||
|
int sock_init (void);
|
||||||
|
|
||||||
|
int sock_connect (char *hostname, char *port, char *description, int allow_ipv6, int debug, char *ipaddr_str);
|
||||||
|
/* ipaddr_str needs to be at least SOCK_IPADDR_LEN bytes */
|
||||||
|
|
||||||
|
char *sock_ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t StringBufSize);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue