jcs
/subtext
/amendments
/4
telnet: Add support for telnet nodes
jcs made amendment 4 over 2 years ago
--- console.c Sun Nov 28 21:12:14 2021
+++ console.c Tue Nov 30 15:07:20 2021
@@ -26,18 +26,17 @@
#define FONT_HEIGHT 10
struct node_funcs console_node_funcs = {
- console_output,
console_input,
+ console_output,
};
void console_redraw(struct console *console);
-void console_cursor(struct console *console, short line, short column);
void console_parse_csi(struct console *console);
struct console *
console_init(void)
{
- char title[256] = { 0 };
+ char title[64] = { 0 };
struct console *console;
Rect bounds = { 0 };
Rect data_bounds = { 0, 0, 0, 1 }; /* tlbr */
@@ -256,20 +255,6 @@ console_redraw(struct console *console)
InvertRect(&cursor);
}
}
-
-#if 0
-void
-console_cursor(struct console *console, short line, short column)
-{
- Rect cursor;
-
- cursor.left = 6 + ((console->win->portRect.left + column) * 6);
- cursor.top = 6 + ((console->win->portRect.top + line) * 10) + 2;
- cursor.right = cursor.left + 6;
- cursor.bottom = cursor.top + 10;
- InvertRect(&cursor);
-}
-#endif
void
console_parse_csi(struct console *console)
--- MacTCP.h Thu Sep 17 17:48:48 2020
+++ MacTCP.h Tue Nov 30 09:11:17 2021
@@ -533,6 +533,20 @@ enum {
NumOfHistoBuckets = 7
};
+enum {
+ ConnectionStateClosed = 0,
+ ConnectionStateListening = 2,
+ ConnectionStateSYNReceived = 4,
+ ConnectionStateSYNSent = 6,
+ ConnectionStateEstablished = 8,
+ ConnectionStateFINWait1 = 10,
+ ConnectionStateFINWait2 = 12,
+ ConnectionStateCloseWait = 14,
+ ConnectionStateClosing = 16,
+ ConnectionStateLastAck = 18,
+ ConnectionStateTimeWait = 20
+};
+
struct TCPConnectionStats {
unsigned long dataPktsRcvd;
unsigned long dataPktsSent;
--- main.c Sun Nov 28 21:17:44 2021
+++ main.c Tue Nov 30 14:45:17 2021
@@ -20,6 +20,7 @@
#include "subtext.h"
#include "console.h"
#include "session.h"
+#include "telnet.h"
#include "uthread.h"
#include "util.h"
@@ -41,7 +42,7 @@ main(void)
GrafPtr old_port;
short event_in, n;
char key;
-
+
uthread_init();
InitGraf(&thePort);
@@ -55,7 +56,7 @@ main(void)
MaxApplZone();
err_init();
-
+
mbar = GetNewMBar(MBAR_ID);
SetMenuBar(mbar);
apple_menu = GetMHandle(APPLE_MENU_ID);
@@ -69,9 +70,11 @@ main(void)
strcpy(config.name, "Kludge BBS");
strcpy(config.hostname, "klud.ge");
+ telnet_init();
cur_console = console_init();
while (!quitting) {
+ telnet_idle();
uthread_coordinate();
WaitNextEvent(everyEvent, &event, 1L, 0L);
--- session.h Fri Nov 26 13:41:08 2021
+++ session.h Tue Nov 30 13:23:40 2021
@@ -31,10 +31,9 @@ enum session_input_state {
SESSION_INPUT_CHAR
};
-/* TODO: move these to node struct to be session->node->output(..) */
struct node_funcs {
- short (*output)(struct session *session);
short (*input)(struct session *session);
+ short (*output)(struct session *session);
};
struct session {
--- tcp.c Tue Nov 16 12:53:10 2021
+++ tcp.c Tue Nov 30 09:22:58 2021
@@ -27,9 +27,8 @@ _TCPInit(void)
pb.ioParam.ioPermssn = fsCurPerm;
osErr = PBOpen(&pb, false);
- if (noErr == osErr) {
+ if (noErr == osErr)
gIPPDriverRefNum = pb.ioParam.ioRefNum;
- }
return osErr;
}
@@ -39,12 +38,12 @@ _TCPGetOurIP(ip_addr *ip, long *netMask)
{
OSErr osErr;
GetAddrParamBlock pb;
- int i;
memset(&pb, 0, sizeof(pb));
pb.csCode = ipctlGetAddr;
pb.ioCRefNum = gIPPDriverRefNum;
+ pb.ioResult = 1;
osErr = PBControl((ParmBlkPtr)&pb, true);
while (pb.ioResult > 0)
@@ -70,9 +69,10 @@ _TCPCreate(TCPiopb *pb, StreamPtr *stream, Ptr rcvBufP
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPCreate;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPCreate;
+ pb->ioResult = 1;
pb->csParam.create.rcvBuff = rcvBufPtr;
pb->csParam.create.rcvBuffLen = rcvBufLen;
@@ -98,17 +98,18 @@ _TCPPassiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPPassiveOpen;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPPassiveOpen;
pb->tcpStream = stream;
+ pb->ioResult = 1;
pb->csParam.open.ulpTimeoutValue = 0;
pb->csParam.open.ulpTimeoutAction = 1;
pb->csParam.open.validityFlags = 0xC0;
pb->csParam.open.commandTimeoutValue = 30;
- pb->csParam.open.remoteHost = *remoteIP;
- pb->csParam.open.remotePort = *remotePort;
+ pb->csParam.open.remoteHost = 0;
+ pb->csParam.open.remotePort = 0;
pb->csParam.open.localHost = 0;
pb->csParam.open.localPort = *localPort;
pb->csParam.open.tosFlags = 0;
@@ -123,9 +124,12 @@ _TCPPassiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr
osErr = PBControl((ParmBlkPtr) pb, async);
if (!async && (noErr == osErr)) {
- *remoteIP = pb->csParam.open.remoteHost;
- *remotePort = pb->csParam.open.remotePort;
- *localIP = pb->csParam.open.localHost;
+ if (remoteIP)
+ *remoteIP = pb->csParam.open.remoteHost;
+ if (remotePort)
+ *remotePort = pb->csParam.open.remotePort;
+ if (localIP)
+ *localIP = pb->csParam.open.localHost;
*localPort = pb->csParam.open.localPort;
}
@@ -143,10 +147,11 @@ _TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPActiveOpen;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPActiveOpen;
pb->tcpStream = stream;
+ pb->ioResult = 1;
pb->csParam.open.ulpTimeoutValue = 30;
pb->csParam.open.ulpTimeoutAction = 1;
@@ -184,10 +189,11 @@ _TCPSend(TCPiopb *pb, StreamPtr stream, wdsEntry *wdsP
{
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPSend;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPSend;
pb->tcpStream = stream;
+ pb->ioResult = 1;
pb->csParam.send.ulpTimeoutValue = 30;
pb->csParam.send.ulpTimeoutAction = 1;
@@ -211,10 +217,11 @@ _TCPNoCopyRcv(TCPiopb *pb, StreamPtr stream, Ptr rdsPt
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPNoCopyRcv;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPNoCopyRcv;
pb->tcpStream = stream;
+ pb->ioResult = 1;
pb->csParam.receive.commandTimeoutValue = 30;
pb->csParam.receive.urgentFlag = 0;
@@ -235,10 +242,11 @@ _TCPRcv(TCPiopb *pb, StreamPtr stream, Ptr rcvBufPtr,
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPRcv;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPRcv;
pb->tcpStream = stream;
+ pb->ioResult = 1;
pb->csParam.receive.commandTimeoutValue = 30;
pb->csParam.receive.urgentFlag = 0;
@@ -260,10 +268,11 @@ _TCPBfrReturn(TCPiopb *pb, StreamPtr stream, Ptr rdsPt
{
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPRcvBfrReturn;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPRcvBfrReturn;
pb->tcpStream = stream;
+ pb->ioResult = 1;
pb->csParam.receive.rdsPtr = rdsPtr;
pb->csParam.receive.userDataPtr = userData;
@@ -277,10 +286,11 @@ _TCPClose(TCPiopb *pb, StreamPtr stream, Ptr userData,
{
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPClose;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPClose;
pb->tcpStream = stream;
+ pb->ioResult = 1;
pb->csParam.close.ulpTimeoutValue = 30;
pb->csParam.close.ulpTimeoutAction = 1;
@@ -296,10 +306,11 @@ _TCPAbort(TCPiopb *pb, StreamPtr stream, Ptr userData,
{
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPAbort;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPAbort;
pb->tcpStream = stream;
+ pb->ioResult = 1;
pb->csParam.abort.userDataPtr = userData;
@@ -314,11 +325,12 @@ _TCPStatus(TCPiopb *pb, StreamPtr stream, struct TCPSt
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPStatus;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPStatus;
pb->tcpStream = stream;
pb->csParam.status.userDataPtr = userData;
+ pb->ioResult = 1;
osErr = PBControl((ParmBlkPtr)pb, async);
if (!async && (noErr == osErr)) {
@@ -336,10 +348,11 @@ _TCPRelease(TCPiopb *pb, StreamPtr stream, Ptr userDat
memset(pb, 0, sizeof(*pb));
+ pb->csCode = TCPRelease;
pb->ioCompletion = ioCompletion;
pb->ioCRefNum = gIPPDriverRefNum;
- pb->csCode = TCPRelease;
pb->tcpStream = stream;
+ pb->ioResult = 1;
pb->csParam.status.userDataPtr = userData;
--- telnet.c Tue Nov 30 15:01:24 2021
+++ telnet.c Tue Nov 30 15:01:24 2021
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "session.h"
+#include "telnet.h"
+#include "tcp.h"
+#include "util.h"
+
+enum {
+ TELNET_PB_STATE_UNUSED = 0,
+ TELNET_PB_STATE_LISTENING,
+ TELNET_PB_STATE_CONNECTED
+};
+
+struct telnet_node {
+ short id;
+ short state;
+ TCPiopb pb, listen_pb;
+ StreamPtr stream;
+ unsigned char *tcp_buf;
+ long tcp_buf_len;
+ wdsEntry tcp_wds[2];
+ struct session *session;
+};
+
+#define TELNET_SLOTS 5
+struct telnet_node telnet_nodes[TELNET_SLOTS];
+short telnet_local_port;
+TCPiopb telnet_exit_pb;
+TCPStatusPB telnet_status_pb;
+
+void telnet_atexit(void);
+short telnet_output(struct session *session);
+short telnet_input(struct session *session);
+
+struct node_funcs telnet_node_funcs = {
+ telnet_input,
+ telnet_output,
+};
+
+void
+telnet_init(void)
+{
+ struct telnet_node *node;
+ short error, i;
+
+ if (_TCPInit() != 0)
+ err(1, "Failed initializing MacTCP");
+
+ _atexit(telnet_atexit);
+
+ /* TODO: get from resource */
+ telnet_local_port = 23;
+
+ for (i = 0; i < TELNET_SLOTS; i++) {
+ node = &telnet_nodes[i];
+ node->id = i;
+ memset(node, 0, sizeof(struct telnet_node));
+
+ if (i > 1)
+ continue;
+
+ /* docs say buf should be 4*MTU+1024 */
+ /* TODO: actually lookup MTU */
+ node->tcp_buf_len = (4 * 1500) + 1024;
+ node->tcp_buf = xmalloc(node->tcp_buf_len);
+
+ error = _TCPCreate(&node->pb, &node->stream, (Ptr)node->tcp_buf,
+ node->tcp_buf_len, nil, nil, nil, false);
+ if (error)
+ err(1, "TCPCreate[%d] failed: %d", i, error);
+
+ error = _TCPPassiveOpen(&node->listen_pb, node->stream, nil, nil,
+ nil, (ip_port *)&telnet_local_port, nil, nil, true);
+ if (error)
+ err(1, "TCPPassiveOpen[%d] failed: %d", i, error);
+ node->state = TELNET_PB_STATE_LISTENING;
+ }
+}
+
+void
+telnet_atexit(void)
+{
+ struct telnet_node *node;
+ short i, error;
+
+ for (i = 0; i < TELNET_SLOTS; i++) {
+ node = &telnet_nodes[i];
+
+ if (node->state > TELNET_PB_STATE_UNUSED) {
+ error = _TCPClose(&telnet_exit_pb, node->stream, nil, nil,
+ false);
+ if (error == noErr)
+ error = _TCPAbort(&telnet_exit_pb, node->stream, nil, nil,
+ false);
+ if (error == noErr)
+ error = _TCPRelease(&telnet_exit_pb, node->stream, nil,
+ nil, false);
+ }
+
+ if (node->tcp_buf)
+ free(node->tcp_buf);
+ }
+}
+
+void
+telnet_idle(void)
+{
+ struct telnet_node *node;
+ short i, error;
+
+ for (i = 0; i < TELNET_SLOTS; i++) {
+ node = &telnet_nodes[i];
+ if (node->state == TELNET_PB_STATE_UNUSED)
+ continue;
+
+ switch (node->state) {
+ case TELNET_PB_STATE_LISTENING:
+ if (node->listen_pb.ioResult > 0)
+ continue;
+
+ error = _TCPStatus(&node->pb, node->stream, &telnet_status_pb,
+ nil, nil, false);
+ if (error == connectionDoesntExist)
+ continue;
+ if (error != noErr)
+ err(1, "TCPStatus[%d] %d", i, error);
+ if (telnet_status_pb.connectionState ==
+ ConnectionStateEstablished) {
+ char tty[7];
+ sprintf(tty, "ttyt%d", i);
+ node->state = TELNET_PB_STATE_CONNECTED;
+ node->session = session_create(tty, &telnet_node_funcs);
+ node->session->cookie = (void *)node;
+ }
+ break;
+ case TELNET_PB_STATE_CONNECTED:
+ break;
+ }
+ }
+}
+
+short
+telnet_output(struct session *session)
+{
+ struct telnet_node *node = (struct telnet_node *)session->cookie;
+ short error;
+
+ if (session->obuflen == 0)
+ return 0;
+
+ if (node->pb.ioResult > 0)
+ /* previous _TCPSend has not completed yet */
+ return 0;
+
+ if (node->tcp_wds[0].length) {
+ /* previous _TCPSend completed, shift out those bytes */
+ BlockMove(session->obuf + node->tcp_wds[0].length,
+ session->obuf, session->obuflen - node->tcp_wds[0].length);
+ session->obuflen -= node->tcp_wds[0].length;
+
+ if (session->obuflen == 0)
+ return 0;
+ }
+
+ /*
+ * _TCPSend only knows how many wds pointers were passed in when it
+ * reads the next one and its pointer is zero (or size is zero?), so
+ * even though we're only sending one wds, memory after wds[0]
+ * has to be zeroed out.
+ */
+ memset(&node->tcp_wds, 0, sizeof(node->tcp_wds));
+ node->tcp_wds[0].ptr = session->obuf;
+ node->tcp_wds[0].length = session->obuflen;
+
+ error = _TCPSend(&node->pb, node->stream, node->tcp_wds, nil, nil,
+ true);
+ if (error)
+ err(1, "TCPSend[%d] failed: %d", node->id, error);
+ /* XXX: make this not fatal */
+
+ return session->obuflen;
+}
+
+short
+telnet_input(struct session *session)
+{
+ uthread_yield();
+ return 0;
+}
+
--- telnet.h Mon Nov 29 15:26:06 2021
+++ telnet.h Mon Nov 29 15:26:06 2021
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+void telnet_init(void);
+void telnet_idle(void);