minivmac4ios/Mini vMac/LTOVRTCP.h
2024-03-29 20:33:18 +01:00

286 lines
7.4 KiB
C++

/*
LTOVRTCP.h
Copyright (C) 2012 Michael Fort, Paul C. Pratt, Rob Mitchelmore
You can redistribute this file and/or modify it under the terms
of version 2 of the GNU General Public License as published by
the Free Software Foundation. You should have received a copy
of the license along with this file; see the file COPYING.
This file 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
license for more details.
*/
/*
LocalTalk OVeR Transmission Control Protocol
*/
#define TCP_dolog (dbglog_HAVE && 0)
#if TCP_dolog
LOCALPROC dbglog_writeSockErr(char *s)
{
dbglog_writeCStr(s);
dbglog_writeCStr(": err ");
#if use_winsock
dbglog_writeNum(WSAGetLastError());
#else
dbglog_writeNum(errno);
dbglog_writeCStr(" (");
dbglog_writeCStr(strerror(errno));
dbglog_writeCStr(")");
#endif
dbglog_writeReturn();
}
#endif
/*
Transmit buffer for localtalk data and its metadata
*/
LOCALVAR ui3b tx_buffer[6 + LT_TxBfMxSz] = "LLpppp";
/*
Receive buffer for LocalTalk data and its metadata
*/
LOCALVAR unsigned int rx_buffer_allocation = 1800;
LOCALVAR nw_connection_t connection = nil;
LOCALVAR NSData *nextPacket;
void welcome_next_packet(void)
{
// schedule receiving length
nw_connection_receive(connection, 2, 2, ^(dispatch_data_t _Nullable content, nw_content_context_t _Nullable context, bool is_complete, nw_error_t _Nullable error) {
uint8_t buf[2];
if (error != nil) {
NSLog(@"ERROR RECEIVING LENGTH: %@", error);
return;
}
[(NSData*)content getBytes:buf length:2];
uint16_t length = (buf[0] << 8) | buf[1];
// schedule receiving packet
nw_connection_receive(connection, (uint32_t)length, (uint32_t)length, ^(dispatch_data_t _Nullable content, nw_content_context_t _Nullable context, bool is_complete, nw_error_t _Nullable error) {
if (error != nil) {
NSLog(@"ERROR RECEIVING DATA: %@", error);
return;
}
nextPacket = (NSData*)content;
});
});
}
LOCALPROC start_tcp(void)
{
/* find server from LTOVRTCP_SERVER env, should be in the form 1.2.3.4:12345 */
char *server = NULL;
char buf[32];
short port = 0;
char *portStr = NULL;
if ((server = getenv("LTOVRTCP_SERVER")) && strlen(server) < sizeof(buf)) {
strcpy(buf, server);
char *separator = strchr(buf, ':');
if (separator == NULL) {
return;
}
*separator = 0;
separator++;
if (strlen(separator) > 1) {
port = (short)atoi(separator);
portStr = separator;
}
}
if (port == 0) {
return;
}
/* connect to server */
nw_endpoint_t endpoint = nw_endpoint_create_host(buf, portStr);
nw_parameters_t params = nw_parameters_create_secure_tcp(NW_PARAMETERS_DISABLE_PROTOCOL, ^(nw_protocol_options_t _Nonnull options) {
nw_tcp_options_set_no_delay(options, true);
});
connection = nw_connection_create(endpoint, params);
nw_connection_set_state_changed_handler(connection, ^(nw_connection_state_t state, nw_error_t _Nullable error) {
NSLog(@"connection state %@: %@", @[@"invalid", @"waiting", @"preparing", @"ready", @"failed", @"cancelled"][state], error);
});
nw_connection_set_queue(connection, dispatch_get_main_queue());
nw_connection_start(connection);
welcome_next_packet();
}
LOCALVAR unsigned char *MyRxBuffer = NULL;
/*
External function needed at startup to initialize the LocalTalk
functionality.
*/
LOCALFUNC blnr InitLocalTalk(void)
{
LT_PickStampNodeHint();
LT_TxBuffer = &tx_buffer[6];
MyRxBuffer = malloc(rx_buffer_allocation);
if (NULL == MyRxBuffer) {
return falseblnr;
}
/* Set up TCP socket */
start_tcp();
/* Initialized properly */
return trueblnr;
}
LOCALPROC UnInitLocalTalk(void)
{
nw_connection_cancel(connection);
if (NULL != MyRxBuffer) {
free(MyRxBuffer);
}
}
LOCALPROC embedPacketLength(ui4r length)
{
/*
embeds the length of the packet in the packet as a 16-bit big endian
*/
tx_buffer[0] = (length >> 8) & 0xff;
tx_buffer[1] = length & 0xff;
}
LOCALPROC embedMyPID(void)
{
/*
embeds my process ID in network byte order in the start of the
Tx buffer we assume a pid is at most 32 bits. As far as I know
there's no actual implementation of POSIX with 64-bit PIDs so we
should be ok.
*/
int i;
#if LT_MayHaveEcho
ui5r v = LT_MyStamp;
#else
ui5r v = (ui5r)getpid();
#endif
for (i = 0; i < 4; i++) {
tx_buffer[2+i] = (v >> (3 - i)*8) & 0xff;
}
}
GLOBALOSGLUPROC LT_TransmitPacket(void)
{
#if TCP_dolog
dbglog_writeln("writing to tcp");
#endif
embedPacketLength(LT_TxBuffSz + 4);
embedMyPID();
char *buf = malloc(LT_TxBfMxSz + 6);
memcpy(buf, tx_buffer, LT_TxBfMxSz + 6);
dispatch_data_t data = dispatch_data_create(buf, LT_TxBuffSz + 6, dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_FREE);
nw_connection_send(connection, data, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, ^(nw_error_t _Nullable error) {
if (error) {
NSLog(@"nw_connection_send error %@", error);
}
});
}
/*
pidInPacketIsMine returns 1 if the process ID embedded in the packet
is the same as the process ID of the current process
*/
LOCALFUNC int pidInPacketIsMine(void)
{
/* is the PID in the packet my own PID? */
int i;
ui5r v;
#if LT_MayHaveEcho
v = LT_MyStamp;
#else
v = (ui5r)getpid();
#endif
for (i = 0; i < 4; i++) {
if (MyRxBuffer[i] != ((v >> (3 - i)*8) & 0xff)) {
return 0;
}
}
return 1;
}
/*
packetIsOneISent returns 1 if this looks like a packet that this
process sent and 0 if it looks like a packet that a different
process sent. This provides loopback protection so that we do not
try to consume packets that we sent ourselves. We do this by
checking the process ID embedded in the packet and the IP address
the packet was sent from. It would be neater to just look at the
LocalTalk node ID embedded in the LLAP packet, but this doesn't
actually work, because during address acquisition it is entirely
legitimate (and, in the case of collision, *required*) for another
node to send a packet from what we think is our own node ID.
*/
#if ! LT_MayHaveEcho
LOCALFUNC int packetIsOneISent(void)
{
return pidInPacketIsMine();
}
#endif
LOCALFUNC int GetNextPacket(void)
{
if (nextPacket == nil) {
return 0;
}
int bytes = (int)nextPacket.length;
if (bytes <= rx_buffer_allocation) {
[nextPacket getBytes:MyRxBuffer length:bytes];
} else {
bytes = 0;
}
nextPacket = nil;
welcome_next_packet();
return bytes;
}
GLOBALOSGLUPROC LT_ReceivePacket(void)
{
int bytes;
#if ! LT_MayHaveEcho
label_retry:
#endif
bytes = GetNextPacket();
if (bytes > 0) {
#if LT_MayHaveEcho
CertainlyNotMyPacket = ! pidInPacketIsMine();
#endif
#if ! LT_MayHaveEcho
if (packetIsOneISent()) {
goto label_retry;
}
#endif
{
#if TCP_dolog
dbglog_writeCStr("passing ");
dbglog_writeNum(bytes - 4);
dbglog_writeCStr(" bytes to receiver");
dbglog_writeReturn();
#endif
LT_RxBuffer = MyRxBuffer + 4;
LT_RxBuffSz = bytes - 4;
}
}
}