552 lines
12 KiB
C
552 lines
12 KiB
C
/*
|
|
Copyright 2001-2022 John Wiseman G8BPQ
|
|
|
|
This file is part of LinBPQ/BPQ32.
|
|
|
|
LinBPQ/BPQ32 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
LinBPQ/BPQ32 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 LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
|
|
*/
|
|
|
|
/*
|
|
Netrom over TCP Support
|
|
|
|
This is intended for operation over radio links with an IP interface, eg New Packet Radio or possibly microwave links
|
|
|
|
To simplify interface to the rest of the oode dummy LINK and PORT records are created
|
|
|
|
Packet Format is Length (2 byte little endian) Call (10 bytes ASCII) NETROM L3/4 Packet, starting 0xcf (to detect framing errors).
|
|
|
|
A TCP message can contain multiple packets and/or partial packets
|
|
|
|
It uses the Telnet Server, with port defined in NETROMPORT
|
|
|
|
ROUTE definitions have an extra field, the TCP Port Number
|
|
|
|
*/
|
|
|
|
//#pragma data_seg("_BPQDATA")
|
|
|
|
|
|
#define _CRT_SECURE_NO_DEPRECATE
|
|
|
|
#include "time.h"
|
|
#include "stdio.h"
|
|
#include <fcntl.h>
|
|
//#include "vmm.h"
|
|
|
|
#include "cheaders.h"
|
|
#include "asmstrucs.h"
|
|
#include "telnetserver.h"
|
|
|
|
#define NETROM_PID 0xCF
|
|
|
|
void NETROMConnectionLost(struct ConnectionInfo * sockptr);
|
|
int DataSocket_ReadNETROM(struct ConnectionInfo * sockptr, SOCKET sock, struct NRTCPSTRUCT * Info, int portNo);
|
|
int NETROMTCPConnect(struct ROUTE * Route, struct ConnectionInfo * sockptr);
|
|
void NETROMConnected(struct ConnectionInfo * sockptr, SOCKET sock, struct NRTCPSTRUCT * Info);
|
|
VOID SendRTTMsg(struct ROUTE * Route);
|
|
BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE);
|
|
VOID NETROMMSG(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG);
|
|
int BPQTRACE(MESSAGE * Msg, BOOL TOAPRS);
|
|
VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason);
|
|
|
|
struct NRTCPMsg
|
|
{
|
|
short Length;
|
|
char Call[10];
|
|
unsigned char PID;
|
|
char Packet[1024];
|
|
};
|
|
|
|
struct NRTCPSTRUCT
|
|
{
|
|
struct ConnectionInfo * sockptr;
|
|
struct _LINKTABLE * LINK; // Dummy Link Record for this ROUTE
|
|
struct ROUTE * Route; // May need backlink
|
|
char Call[10];
|
|
};
|
|
|
|
struct NRTCPSTRUCT * NRTCPInfo[256] = {0};
|
|
|
|
// Do we want to use normal TCP server connections, which are limited, or our own. Let's try our own for now
|
|
|
|
struct ConnectionInfo * AllocateNRTCPRec()
|
|
{
|
|
struct ConnectionInfo * sockptr = 0;
|
|
struct NRTCPSTRUCT * Info;
|
|
int i;
|
|
|
|
for (i = 0; i < 255; i++)
|
|
{
|
|
if (NRTCPInfo[i] == 0)
|
|
{
|
|
// only allocate as many as needed
|
|
|
|
Info = NRTCPInfo[i] = (struct NRTCPSTRUCT *)zalloc(sizeof(struct NRTCPSTRUCT));
|
|
Info->sockptr = (struct ConnectionInfo *)zalloc(sizeof(struct ConnectionInfo));
|
|
Info->LINK = (struct _LINKTABLE *)zalloc(sizeof(struct _LINKTABLE));
|
|
Info->sockptr->Number = i;
|
|
}
|
|
else
|
|
Info = NRTCPInfo[i];
|
|
|
|
sockptr = Info->sockptr;
|
|
|
|
if (sockptr->SocketActive == FALSE)
|
|
{
|
|
sockptr->SocketActive = TRUE;
|
|
sockptr->ConnectTime = sockptr->LastSendTime = time(NULL);
|
|
|
|
Debugprintf("NRTCP Allocated %d", i);
|
|
return sockptr;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void checkNRTCPSockets(int portNo)
|
|
{
|
|
SOCKET sock;
|
|
int Active = 0;
|
|
SOCKET maxsock;
|
|
int retval;
|
|
int i;
|
|
|
|
struct timeval timeout;
|
|
fd_set readfd, writefd, exceptfd;
|
|
|
|
struct ConnectionInfo * sockptr;
|
|
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 0; // poll
|
|
|
|
maxsock = 0;
|
|
|
|
FD_ZERO(&readfd);
|
|
FD_ZERO(&writefd);
|
|
FD_ZERO(&exceptfd);
|
|
|
|
for (i = 0; i < 255; i++)
|
|
{
|
|
if (NRTCPInfo[i] == 0)
|
|
break; // only as many as have been used
|
|
|
|
sockptr = NRTCPInfo[i]->sockptr;
|
|
|
|
if (sockptr->SocketActive == 0)
|
|
continue;
|
|
|
|
if (sockptr->Connecting)
|
|
{
|
|
// look for complete or failed
|
|
|
|
FD_SET(sockptr->socket, &writefd);
|
|
FD_SET(sockptr->socket, &exceptfd);
|
|
}
|
|
else
|
|
{
|
|
FD_SET(sockptr->socket, &readfd);
|
|
FD_SET(sockptr->socket, &exceptfd);
|
|
}
|
|
|
|
Active++;
|
|
|
|
if (sockptr->socket > maxsock)
|
|
maxsock = sockptr->socket;
|
|
}
|
|
|
|
if (Active)
|
|
{
|
|
retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout);
|
|
|
|
if (retval == -1)
|
|
{
|
|
perror("data select");
|
|
Debugprintf("NRTCP Select Error %d Active %d", WSAGetLastError(), Active);
|
|
}
|
|
else
|
|
{
|
|
if (retval)
|
|
{
|
|
// see who has data
|
|
|
|
for (i = 0; i < 255; i++)
|
|
{
|
|
if (NRTCPInfo[i] == 0)
|
|
break;
|
|
|
|
sockptr = NRTCPInfo[i]->sockptr;
|
|
|
|
if (sockptr->SocketActive == 0)
|
|
continue;
|
|
|
|
sock = sockptr->socket;
|
|
|
|
if (FD_ISSET(sock, &writefd))
|
|
NETROMConnected(sockptr, sock, NRTCPInfo[i]);
|
|
|
|
if (FD_ISSET(sock, &readfd))
|
|
DataSocket_ReadNETROM(sockptr, sock, NRTCPInfo[i], portNo);
|
|
|
|
if (FD_ISSET(sock, &exceptfd))
|
|
NETROMConnectionLost(sockptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int NETROMOpenConnection(struct ROUTE * Route)
|
|
{
|
|
struct NRTCPSTRUCT * Info;
|
|
struct ConnectionInfo * sockptr;
|
|
|
|
Debugprintf("Opening NRTCP Connection");
|
|
|
|
if (Route->TCPSession)
|
|
{
|
|
// SESSION ALREADY EXISTS
|
|
|
|
sockptr = Route->TCPSession->sockptr;
|
|
|
|
if (sockptr->Connected || sockptr->Connecting)
|
|
return TRUE;
|
|
|
|
// previous connect failed
|
|
}
|
|
else
|
|
{
|
|
sockptr = AllocateNRTCPRec();
|
|
|
|
if (sockptr == NULL)
|
|
return 0;
|
|
|
|
Info = Route->TCPSession = NRTCPInfo[sockptr->Number];
|
|
memcpy(Info->Call, MYNETROMCALL, 10);
|
|
Route->NEIGHBOUR_LINK = Info->LINK;
|
|
|
|
Info->Route = Route;
|
|
Info->LINK->NEIGHBOUR = Route;
|
|
Info->LINK->LINKPORT = GetPortTableEntryFromPortNum(Route->NEIGHBOUR_PORT);
|
|
}
|
|
|
|
return NETROMTCPConnect(Route, sockptr);
|
|
|
|
}
|
|
|
|
int NETROMTCPConnect(struct ROUTE * Route, struct ConnectionInfo * sockptr)
|
|
{
|
|
int err;
|
|
u_long param=1;
|
|
BOOL bcopt=TRUE;
|
|
SOCKET sock;
|
|
struct sockaddr_in sinx;
|
|
int addrlen=sizeof(sinx);
|
|
char PortString[20];
|
|
struct addrinfo hints, *res = 0, *saveres;
|
|
int Port = Route->TCPPort;
|
|
|
|
sprintf(PortString, "%d", Port);
|
|
|
|
// get host info, make socket, and connect it
|
|
|
|
memset(&hints, 0, sizeof hints);
|
|
|
|
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
getaddrinfo(Route->TCPHost, PortString, &hints, &res);
|
|
|
|
if (!res)
|
|
{
|
|
err = WSAGetLastError();
|
|
Debugprintf("Resolve HostName %s Failed - Error %d", Route->TCPHost, err);
|
|
return FALSE; // Resolve failed
|
|
}
|
|
|
|
// Step thorough the list of hosts
|
|
|
|
saveres = res; // Save for free
|
|
|
|
sock = sockptr->socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
|
|
|
|
if (sock == INVALID_SOCKET)
|
|
{
|
|
Debugprintf, ("Netrom over TCP Create Socket Failed");
|
|
return FALSE;
|
|
}
|
|
|
|
ioctl(sock, FIONBIO, ¶m);
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4);
|
|
|
|
|
|
if (connect(sock, res->ai_addr, (int)res->ai_addrlen) == 0)
|
|
{
|
|
//
|
|
// Connected successful
|
|
//
|
|
|
|
sockptr->Connected = TRUE;
|
|
freeaddrinfo(saveres);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
freeaddrinfo(saveres);
|
|
|
|
err=WSAGetLastError();
|
|
|
|
if (err == 10035 || err == 115 || err == 36) //EWOULDBLOCK
|
|
{
|
|
// Connect in Progress
|
|
|
|
sockptr->Connecting = TRUE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Connect failed
|
|
|
|
closesocket(sockptr->socket);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
void NETROMConnectionAccepted(struct ConnectionInfo * sockptr)
|
|
{
|
|
// Not sure we can do much here until first message arrives with callsign
|
|
|
|
sockptr->Connected = TRUE;
|
|
Debugprintf("NRTCP Connection Accepted");
|
|
}
|
|
|
|
void NETROMConnected(struct ConnectionInfo * sockptr, SOCKET sock, struct NRTCPSTRUCT * Info)
|
|
{
|
|
// Connection Complete
|
|
|
|
Debugprintf("NRTCP Connected");
|
|
|
|
sockptr->Connecting = FALSE;
|
|
sockptr->Connected = TRUE;
|
|
|
|
Info->LINK->L2STATE = 5;
|
|
|
|
if (Info->Route->INP3Node)
|
|
SendRTTMsg(Info->Route);
|
|
}
|
|
|
|
int DataSocket_ReadNETROM(struct ConnectionInfo * sockptr, SOCKET sock, struct NRTCPSTRUCT * Info, int portNo)
|
|
{
|
|
int len=0, maxlen;
|
|
struct NRTCPMsg * Msg;
|
|
struct _L3MESSAGEBUFFER * L3Msg;
|
|
struct ROUTE * Route;
|
|
UCHAR axCall[7];
|
|
PMESSAGE Buffer;
|
|
|
|
ioctl(sock,FIONREAD,&len);
|
|
|
|
maxlen = InputBufferLen - sockptr->InputLen;
|
|
|
|
if (len > maxlen) len = maxlen;
|
|
|
|
len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0);
|
|
|
|
if (len == SOCKET_ERROR || len == 0)
|
|
{
|
|
// Failed or closed - clear connection
|
|
|
|
NETROMConnectionLost(sockptr);
|
|
return 0;
|
|
}
|
|
|
|
sockptr->InputLen += len;
|
|
|
|
// Process data
|
|
|
|
checkLen:
|
|
|
|
// See if we have a whole packet
|
|
|
|
Msg = (struct NRTCPMsg *)&sockptr->InputBuffer[0];
|
|
|
|
if (Msg->Length > sockptr->InputLen) // if not got whole frame wait
|
|
return 0;
|
|
|
|
if (Info->Call[0] == 0)
|
|
{
|
|
// first packet - do we need to do anything?
|
|
|
|
// This must be an incoming connection as Call is set before calling so need to find route record and set things up.
|
|
|
|
memcpy(Info->Call, Msg->Call, 10);
|
|
|
|
ConvToAX25(Msg->Call, axCall);
|
|
|
|
if (FindNeighbour(axCall, portNo, &Route))
|
|
{
|
|
Info->Route = Route;
|
|
Route->NEIGHBOUR_LINK = Info->LINK;
|
|
Info->LINK->NEIGHBOUR = Route;
|
|
Info->LINK->LINKPORT = GetPortTableEntryFromPortNum(Route->NEIGHBOUR_PORT);
|
|
Route->TCPSession = Info;
|
|
Info->LINK->L2STATE = 5;
|
|
|
|
if (Info->Route->INP3Node)
|
|
SendRTTMsg(Info->Route);
|
|
}
|
|
else
|
|
goto seeifMore; // Should we kill connection?
|
|
}
|
|
|
|
|
|
if (memcmp(Info->Call, Msg->Call, 10) != 0)
|
|
{
|
|
// something wrong - maybe connection reused
|
|
}
|
|
|
|
// Format as if come from an ax.25 link
|
|
|
|
L3Msg = GetBuff();
|
|
|
|
if (L3Msg == 0)
|
|
goto seeifMore;
|
|
|
|
L3Msg->LENGTH = (Msg->Length - 12) + MSGHDDRLEN;
|
|
L3Msg->Next = 0;
|
|
L3Msg->Port = 0;
|
|
L3Msg->L3PID = NETROM_PID;
|
|
memcpy(&L3Msg->L3SRCE, Msg->Packet, Msg->Length - 13);
|
|
|
|
// Create a dummy L2 message so we can trace it
|
|
|
|
Buffer = GetBuff();
|
|
|
|
if (Buffer)
|
|
{
|
|
Buffer->CHAIN = 0;
|
|
Buffer->CTL = 0;
|
|
Buffer->PORT = portNo;
|
|
|
|
ConvToAX25(Info->Call, Buffer->ORIGIN);
|
|
ConvToAX25(MYNETROMCALL, Buffer->DEST);
|
|
|
|
memcpy(Buffer->L2DATA, &L3Msg->L3SRCE[0], Msg->Length - 13);
|
|
Buffer->ORIGIN[6] |= 1; // Set end of calls
|
|
Buffer->PID = NETROM_PID;
|
|
Buffer->LENGTH = Msg->Length + 10;
|
|
time(&Buffer->Timestamp);
|
|
|
|
BPQTRACE(Buffer, FALSE);
|
|
ReleaseBuffer(Buffer);
|
|
}
|
|
|
|
NETROMMSG(Info->LINK, L3Msg);
|
|
|
|
seeifMore:
|
|
|
|
sockptr->InputLen -= Msg->Length;
|
|
|
|
if (sockptr->InputLen > 0)
|
|
{
|
|
memmove(sockptr->InputBuffer, &sockptr->InputBuffer[Msg->Length], sockptr->InputLen);
|
|
goto checkLen;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID TCPNETROMSend(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Frame)
|
|
{
|
|
struct NRTCPMsg Msg;
|
|
unsigned char * Data = (unsigned char *)&Frame->L3SRCE[0];
|
|
int DataLen = Frame->LENGTH - (MSGHDDRLEN + 1); // Not including PID
|
|
int Ret;
|
|
PMESSAGE Buffer;
|
|
|
|
Msg.Length = DataLen + 13; // include PID
|
|
memcpy(Msg.Call, MYNETROMCALL, 10);
|
|
Msg.PID = NETROM_PID;
|
|
memcpy(Msg.Packet, Data, DataLen);
|
|
|
|
if (Route->TCPSession == 0)
|
|
return;
|
|
|
|
Ret = send(Route->TCPSession->sockptr->socket, (char *)&Msg, DataLen + 13, 0);
|
|
|
|
// Create a dummy L2 message so we can trace it
|
|
|
|
Buffer = GetBuff();
|
|
|
|
if (Buffer)
|
|
{
|
|
Buffer->CHAIN = 0;
|
|
Buffer->CTL = 0;
|
|
Buffer->PORT = Route->NEIGHBOUR_PORT | 128; // TX Flag
|
|
|
|
ConvToAX25(Route->TCPSession->Call, Buffer->DEST);
|
|
ConvToAX25(MYNETROMCALL, Buffer->ORIGIN);
|
|
|
|
memcpy(Buffer->L2DATA, &Frame->L3SRCE[0], DataLen);
|
|
Buffer->ORIGIN[6] |= 1; // Set end of calls
|
|
Buffer->PID = NETROM_PID;
|
|
Buffer->LENGTH = DataLen + 15 + MSGHDDRLEN;
|
|
time(&Buffer->Timestamp);
|
|
|
|
BPQTRACE(Buffer, FALSE);
|
|
ReleaseBuffer(Buffer);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void NETROMConnectionLost(struct ConnectionInfo * sockptr)
|
|
{
|
|
struct NRTCPSTRUCT * Info = NRTCPInfo[sockptr->Number];
|
|
struct ROUTE * Route;
|
|
|
|
closesocket(sockptr->socket);
|
|
|
|
// If there is an attached route (there should be) clear all connections
|
|
|
|
if (Info)
|
|
{
|
|
Route = Info->Route;
|
|
|
|
if (sockptr->Connected)
|
|
L3LINKCLOSED(Info->LINK, LINKLOST);
|
|
|
|
if (sockptr->Connecting)
|
|
L3LINKCLOSED(Info->LINK, SETUPFAILED);
|
|
|
|
Route->TCPSession = 0;
|
|
|
|
Info->Call[0] = 0;
|
|
}
|
|
|
|
sockptr->SocketActive = FALSE;
|
|
|
|
memset(sockptr, 0, sizeof(struct ConnectionInfo));
|
|
}
|
|
|