diff --git a/src/Makefile b/src/Makefile index 0f274da..67ed6b2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,5 +19,7 @@ finger: finger.o libncp.a finser: finser.o libncp.a $(CC) -o $@ $< $(NCP) -telnet: telnet.o libncp.a - $(CC) -o $@ $< $(NCP) +telnet: telnet.o tty.o libncp.a + $(CC) -o $@ telnet.o tty.o $(NCP) + +tty:: tty.h diff --git a/src/finger.c b/src/finger.c index 7719100..f26398f 100644 --- a/src/finger.c +++ b/src/finger.c @@ -7,12 +7,12 @@ int main (int argc, char **argv) { - char *command; + char command[1000]; char reply[1000]; int host, connection, size; - if (argc != 2) { - fprintf (stderr, "Usage: %s host\n", argv[0]); + if (argc < 2 || argc > 3) { + fprintf (stderr, "Usage: %s host [user(s)]\n", argv[0]); exit (1); } @@ -39,8 +39,9 @@ int main (int argc, char **argv) exit (1); } - command = "Sample Finger command from client.\r\n"; - if (ncp_write (connection, command, strlen (command)) == -1) { + size = snprintf (command, sizeof command, "%s\r\n", + argc == 3 ? argv[2] : ""); + if (ncp_write (connection, command, &size) == -1) { fprintf (stderr, "NCP write error.\n"); exit (1); } diff --git a/src/finser.c b/src/finser.c index 4775f14..363543b 100644 --- a/src/finser.c +++ b/src/finser.c @@ -39,7 +39,7 @@ int main (int argc, char **argv) "Sample response from Finger server.\r\n" "Data from client was: \"%s\".\r\n", command); size = strlen (reply); - if (ncp_write (connection, reply, size) == -1) { + if (ncp_write (connection, reply, &size) == -1) { fprintf (stderr, "NCP write error.\n"); exit (1); } diff --git a/src/libncp.c b/src/libncp.c index d1b838b..2556436 100644 --- a/src/libncp.c +++ b/src/libncp.c @@ -123,7 +123,7 @@ int ncp_open (int host, unsigned socket, int *connection) return -1; if (u32 (message + 2) != socket) return -1; - if (message[6] == 255) + if (message[7] == 255) return -2; *connection = message[6]; return 0; @@ -153,6 +153,7 @@ int ncp_read (int connection, void *data, int *length) type (WIRE_READ); add (connection); add (*length); + *length = 0; n = transact (); if (n == -1) return -1; @@ -163,16 +164,18 @@ int ncp_read (int connection, void *data, int *length) return 0; } -int ncp_write (int connection, void *data, int length) +int ncp_write (int connection, void *data, int *length) { type (WIRE_WRITE); add (connection); - memcpy (message + size, data, length); - size += length; + memcpy (message + size, data, *length); + size += *length; + *length = 0; if (transact () == -1) return -1; if (message[1] != connection) return -1; + *length = message[2] << 8 | message[3]; return 0; } diff --git a/src/ncp.c b/src/ncp.c index 67b6a2a..c78301d 100644 --- a/src/ncp.c +++ b/src/ncp.c @@ -15,6 +15,12 @@ #include "imp.h" #include "wire.h" +#define RFNM_TIMEOUT 10 +#define RRP_TIMEOUT 20 +#define ERP_TIMEOUT 20 +#define ALL_TIMEOUT 60 +#define RFC_TIMEOUT 3 + #define IMP_REGULAR 0 #define IMP_LEADER_ERROR 1 #define IMP_DOWN 2 @@ -57,18 +63,42 @@ #define CONN_CLIENT 000001 #define CONN_SERVER 000002 +#define CONN_SENT_RTS 000010 +#define CONN_SENT_STR 000020 #define CONN_GOT_RTS 000100 #define CONN_GOT_STR 000200 #define CONN_GOT_SOCKET 000400 -#define CONN_GOT_BOTH (CONN_SERVER | CONN_GOT_RTS | CONN_GOT_STR) -#define CONN_GOT_ALL (CONN_GOT_RTS | CONN_GOT_STR | CONN_GOT_SOCKET) +#define CONN_GOT_BOTH (CONN_LISTEN | CONN_GOT_RTS | CONN_GOT_STR) +#define CONN_GOT_ALL (CONN_OPEN | CONN_GOT_RTS | CONN_GOT_STR | CONN_GOT_SOCKET) +#define CONN_LISTEN 001000 +#define CONN_OPEN 002000 +#define CONN_READ 004000 +#define CONN_WRITE 010000 +#define CONN_CLOSE 020000 +#define CONN_APPS (CONN_LISTEN | CONN_OPEN | CONN_READ \ + | CONN_WRITE | CONN_CLOSE) + +#define CONN_GOT_RCV_CLS(CONN, OP) (connection[CONN].rcv.size OP -1) +#define CONN_GOT_SND_CLS(CONN, OP) (connection[CONN].snd.size OP -1) +#define CONN_SENT_RCV_CLS(CONN, OP) (connection[CONN].rcv.link OP -1) +#define CONN_SENT_SND_CLS(CONN, OP) (connection[CONN].snd.link OP -1) #define CONNECTIONS 20 +static void send_socket (int i); +static void just_drop (int i); +static void reply_read (uint8_t connection, uint8_t *data, int n); +static void reply_write (uint8_t connection, uint16_t length); +static void send_rts (int i); +static void send_str (int i); +static void send_cls_rcv (int i); +static void send_cls_snd (int i); + static int fd; static struct sockaddr_un server; static struct sockaddr_un client; static socklen_t len; +static unsigned long time_tick; typedef struct { @@ -78,11 +108,26 @@ typedef struct struct { - client_t client; + client_t client, reader, writer; int host; unsigned flags; int listen; + int all_msgs, all_bits; struct { int link, size; uint32_t lsock, rsock; } rcv, snd; + void (*rrp_callback) (int); + void (*rrp_timeout) (int); + unsigned long rrp_time; + void (*rfnm_callback) (int); + void (*rfnm_timeout) (int); + unsigned long rfnm_time; + void (*all_callback) (int); + void (*all_timeout) (int); + unsigned long all_time; + void (*rfc_timeout) (int); + unsigned long rfc_time; + uint8_t buffer[1024]; + uint8_t *ptr; + int length, remaining; } connection[CONNECTIONS]; struct @@ -98,6 +143,7 @@ static struct #define HOST_ALIVE 0001 client_t echo; + unsigned long erp_time; int outstanding_rfnm; } hosts[256]; @@ -122,6 +168,54 @@ static const char *type_name[] = static uint8_t packet[200]; static uint8_t app[200]; +static void when_rrp (int i, void (*cb) (int), void (*to) (int)) +{ + connection[i].rrp_callback = cb; + connection[i].rrp_timeout = to; + connection[i].rrp_time = time_tick + RRP_TIMEOUT; +} + +static void check_rrp (int host) +{ + void (*cb) (int); + int i; + for (i = 0; i < CONNECTIONS; i++) { + if (connection[i].host != host) + continue; + cb = connection[i].rrp_callback; + if (cb == NULL) + continue; + connection[i].rrp_callback = NULL; + connection[i].rrp_timeout = NULL; + cb (i); + } +} + +static void when_rfnm (int i, void (*cb) (int), void (*to) (int)) +{ + connection[i].rfnm_callback = cb; + connection[i].rfnm_timeout = to; + connection[i].rfnm_time = time_tick + RFNM_TIMEOUT; +} + +static void check_rfnm (int host) +{ + void (*cb) (int); + int i; + for (i = 0; i < CONNECTIONS; i++) { + if (connection[i].host != host) + continue; + if (connection[i].rfnm_callback == NULL) + continue; + if (hosts[connection[i].host].outstanding_rfnm >= 4) + continue; + cb = connection[i].rfnm_callback; + connection[i].rfnm_callback = NULL; + connection[i].rfnm_timeout = NULL; + cb (i); + } +} + static int find_link (int host, int link) { int i; @@ -188,6 +282,12 @@ static void destroy (int i) connection[i].rcv.lsock = connection[i].rcv.rsock = connection[i].snd.lsock = connection[i].snd.rsock = 0; connection[i].flags = 0; + connection[i].all_msgs = connection[i].all_bits = 0; + connection[i].rrp_callback = NULL; + connection[i].rrp_timeout = NULL; + connection[i].rfnm_callback = NULL; + connection[i].rfnm_timeout = NULL; + connection[i].rfc_timeout = NULL; } static void send_imp (int flags, int type, int destination, int link, int id, @@ -260,6 +360,8 @@ static int make_open (int host, connection[i].snd.lsock = snd_lsock; connection[i].snd.rsock = snd_rsock; connection[i].flags = 0; + connection[i].rrp_time = time_tick - 1; + connection[i].rfnm_time = time_tick - 1; return i; } @@ -405,6 +507,59 @@ static int process_nop (uint8_t source, uint8_t *data) return 0; } +static void check_all (int i) +{ + void (*cb) (int) = connection[i].all_callback; + int length, count; + if (cb == NULL) + return; + if (connection[i].all_msgs < 1) + return; + if (connection[i].all_bits < 8) + return; + length = connection[i].remaining; + if (8 * length > connection[i].all_bits) + length = connection[i].all_bits / 8; + count = 8 * length / connection[i].snd.size; + connection[i].ptr[-5] = 0; + connection[i].ptr[-4] = connection[i].snd.size; + connection[i].ptr[-3] = count >> 8; + connection[i].ptr[-2] = count; + connection[i].ptr[-1] = 0; + send_imp (0, IMP_REGULAR, connection[i].host, connection[i].snd.link, + 0, 0, connection[i].ptr - 5, 2 + (length + 6)/2); + connection[i].all_msgs--; + connection[i].all_bits -= connection[i].snd.size * count; + connection[i].remaining -= length; + connection[i].ptr += length; + if (connection[i].remaining == 0) { + cb (i); + connection[i].all_callback = NULL; + connection[i].all_timeout = NULL; + } else { + connection[i].all_time = time_tick + ALL_TIMEOUT; + } +} + +static void when_all (int i, void *data, int length, + void (*cb) (int), void (*to) (int)) +{ + int octets = (length + 7) / 8; + connection[i].all_callback = cb; + connection[i].all_timeout = to; + connection[i].all_time = time_tick + ALL_TIMEOUT; + connection[i].length = connection[i].remaining = octets; + connection[i].ptr = connection[i].buffer + 5; + memcpy (connection[i].ptr, data, octets); + check_all (i); +} + +static void unless_rfc (int i, void (*to) (int)) +{ + connection[i].rfc_timeout = to; + connection[i].rfc_time = time_tick + RFC_TIMEOUT; +} + static uint32_t sock (uint8_t *data) { uint32_t x; @@ -415,41 +570,50 @@ static uint32_t sock (uint8_t *data) return x; } -static void reply_open (uint8_t host, uint32_t socket, uint8_t connection) +static void reply_open (uint8_t host, uint32_t socket, uint8_t i, uint8_t e) { - uint8_t reply[7]; + uint8_t reply[8]; + fprintf (stderr, "NCP: Application open reply socket %u on host %03o: " + "connection %u, error %u.\n", socket, host, i, e); + connection[i].flags &= ~CONN_OPEN; reply[0] = WIRE_OPEN+1; reply[1] = host; reply[2] = socket >> 24; reply[3] = socket >> 16; reply[4] = socket >> 8; reply[5] = socket; - reply[6] = connection; + reply[6] = i; + reply[7] = e; if (sendto (fd, reply, sizeof reply, 0, (struct sockaddr *)&client, len) == -1) fprintf (stderr, "NCP: sendto %s error: %s.\n", client.sun_path, strerror (errno)); } -static void reply_listen (uint8_t host, uint32_t socket, uint8_t connection) +static void reply_listen (uint8_t host, uint32_t socket, uint8_t i) { uint8_t reply[7]; + fprintf (stderr, "NCP: Application listen reply socket %u on host %03o: " + "connection %u.\n", socket, host, i); + connection[i].flags &= ~CONN_LISTEN; reply[0] = WIRE_LISTEN+1; reply[1] = host; reply[2] = socket >> 24; reply[3] = socket >> 16; reply[4] = socket >> 8; reply[5] = socket; - reply[6] = connection; + reply[6] = i; if (sendto (fd, reply, sizeof reply, 0, (struct sockaddr *)&client, len) == -1) fprintf (stderr, "NCP: sendto %s error: %s.\n", client.sun_path, strerror (errno)); } -static void reply_close (uint8_t connection) +static void reply_close (uint8_t i) { uint8_t reply[2]; + fprintf (stderr, "NCP: Application close reply connection %u.\n", i); + connection[i].flags &= ~CONN_CLOSE; reply[0] = WIRE_CLOSE+1; - reply[1] = connection; + reply[1] = i; if (sendto (fd, reply, sizeof reply, 0, (struct sockaddr *)&client, len) == -1) fprintf (stderr, "NCP: sendto %s error: %s.\n", client.sun_path, strerror (errno)); @@ -459,17 +623,44 @@ static void maybe_reply (int i) { if ((connection[i].flags & CONN_GOT_BOTH) == CONN_GOT_BOTH) { fprintf (stderr, "NCP: Server got both RTS and STR from client.\n"); - connection[i].flags &= ~CONN_SERVER; + connection[i].rfc_timeout = NULL; reply_listen (connection[i].host, connection[i].listen, i); } else if ((connection[i].flags & CONN_GOT_ALL) == CONN_GOT_ALL) { fprintf (stderr, "NCP: Client got RTS, STR, and socket from server.\n"); - reply_open (connection[i].host, connection[i].listen, i); + connection[i].rfc_timeout = NULL; + reply_open (connection[i].host, connection[i].listen, i, 0); + } +} + +static void send_socket_timeout (int i) +{ + fprintf (stderr, "NCP: Timeout sending ICP socket, connection %d.\n", i); + CONN_SENT_SND_CLS(i, =); + ncp_cls (connection[i].host, + connection[i].snd.lsock, connection[i].snd.rsock); +} + +static void rfc_timeout (int i) +{ + fprintf (stderr, "NCP: Timed out completing RFC for connection %d.\n", i); + connection[i].snd.size = connection[i].rcv.size = -1; + connection[i].rfnm_timeout = NULL; + connection[i].all_timeout = NULL; + if (connection[i].rcv.link != -1) { + CONN_SENT_RCV_CLS(i, =); + ncp_cls (connection[i].host, + connection[i].rcv.lsock, connection[i].rcv.rsock); + } + if (connection[i].snd.link != -1) { + CONN_SENT_SND_CLS(i, =); + ncp_cls (connection[i].host, + connection[i].snd.lsock, connection[i].snd.rsock); } } static int process_rts (uint8_t source, uint8_t *data) { - int i; + int i, j; uint32_t lsock, rsock; uint8_t link; @@ -490,6 +681,8 @@ static int process_rts (uint8_t source, uint8_t *data) RTS to initiate a new connection. Reply with an STR for the initial part of ICP, which is to send the server data connection socket. */ + uint8_t tmp[4]; + uint32_t s = 0200; i = make_open (source, 0, 0, lsock, rsock); fprintf (stderr, "NCP: Listening to %u: new connection %d, link %u.\n", lsock, i, link); @@ -504,45 +697,75 @@ static int process_rts (uint8_t source, uint8_t *data) connection[i].snd.lsock, connection[i].snd.rsock, connection[i].snd.size); + connection[i].flags |= CONN_SENT_STR; + tmp[0] = (s >> 24) & 0xFF; + tmp[1] = (s >> 16) & 0xFF; + tmp[2] = (s >> 8) & 0xFF; + tmp[3] = (s >> 0) & 0xFF; + when_all (i, tmp, 32, send_socket, send_socket_timeout); + + j = make_open (source, + s, connection[i].snd.rsock+3, + s+1, connection[i].snd.rsock+2); + connection[j].flags |= CONN_LISTEN; + connection[j].snd.size = 8; + connection[j].rcv.link = 46; + connection[j].rcv.size = connection[j].snd.link = 0; + connection[j].listen = lsock; + fprintf (stderr, "NCP: New connection %d sockets %d:%d %d:%d link %d\n", + j, + connection[j].rcv.lsock, connection[j].rcv.rsock, + connection[j].snd.lsock, connection[j].snd.rsock, + connection[j].rcv.link); + unless_rfc (j, just_drop); } else if ((i = find_snd_sockets (source, lsock, rsock)) != -1) { - /* There already exists a connection for this socket pair, which - means an STR was sent previously. */ - fprintf (stderr, "NCP: Confirmed STR, connection %d link %u.\n", i, link); - connection[i].snd.link = link; - connection[i].flags |= CONN_GOT_RTS; - maybe_reply (i); - } else if ((i = find_rcv_sockets (source, lsock-1, rsock+1)) != -1) { - /* There already exists a connection for this socket pair, but - only an STR was sent previously. */ - connection[i].snd.lsock = lsock; - connection[i].snd.rsock = rsock; + /* There already exists a connection for this socket pair. */ connection[i].snd.link = link; - connection[i].snd.size = 8; connection[i].flags |= CONN_GOT_RTS; - fprintf (stderr, "NCP: Confirm RTS, send STR sockets %u:%u size %u.\n", - connection[i].snd.lsock, - connection[i].snd.rsock, - connection[i].snd.size); - ncp_str (connection[i].host, - connection[i].snd.lsock, - connection[i].snd.rsock, - connection[i].snd.size); + if (connection[i].flags & CONN_SENT_STR) { + fprintf (stderr, "NCP: Confirmed STR, connection %d link %u.\n", i, link); + connection[i].rfc_timeout = NULL; + } else { + fprintf (stderr, "NCP: Confirm RTS, send STR sockets %u:%u size %u.\n", + connection[i].snd.lsock, + connection[i].snd.rsock, + connection[i].snd.size); + ncp_str (connection[i].host, + connection[i].snd.lsock, + connection[i].snd.rsock, + connection[i].snd.size); + } maybe_reply (i); } else { - /* There is no connection for this socket pair. */ - i = make_open (source, 0, 0, lsock, rsock); - connection[i].snd.size = 8; //Send byte size. - connection[i].snd.link = link; - connection[i].flags |= CONN_GOT_RTS; - fprintf (stderr, "NCP: New connection %d link %u.\n", i, link); - fprintf (stderr, "NCP: Confirm RTS, send STR sockets %u:%u size %u.\n", - connection[i].snd.lsock, - connection[i].snd.rsock, - connection[i].snd.size); - ncp_str (connection[i].host, - connection[i].snd.lsock, - connection[i].snd.rsock, - connection[i].snd.size); + for (i = 0; i < CONNECTIONS; i++) { + if (connection[i].host == source && + connection[i].rcv.lsock+3 == lsock && + (connection[i].flags & CONN_CLIENT) != 0) + break; + } + + if (i == CONNECTIONS) { + fprintf (stderr, "NCP: Not listening to %u; refusing.\n", lsock); + i = make_open (source, 0, 0, lsock, rsock); + connection[i].snd.size = 0; + ncp_cls (source, lsock, rsock); + unless_rfc (i, just_drop); + } else { + j = make_open (source, lsock-1, rsock+1, lsock, rsock); + connection[j].snd.size = 8; + connection[j].snd.link = link; + connection[j].rcv.link = 49; + connection[j].flags |= CONN_OPEN | CONN_GOT_RTS; + connection[j].listen = connection[i].rcv.rsock; + fprintf (stderr, "NCP: New connection %d sockets %d:%d %d:%d link %u\n", + j, + connection[j].rcv.lsock, connection[j].rcv.rsock, + connection[j].snd.lsock, connection[j].snd.rsock, + connection[j].snd.link); + when_rfnm (j, send_str, send_cls_snd); + unless_rfc (j, just_drop); + maybe_reply (j); + } } return 9; @@ -559,7 +782,7 @@ There are two cases to consider: */ static int process_str (uint8_t source, uint8_t *data) { - int i; + int i, j; uint32_t lsock, rsock; uint8_t size; @@ -571,49 +794,58 @@ static int process_str (uint8_t source, uint8_t *data) rsock, lsock, size, source); if ((i = find_rcv_sockets (source, lsock, rsock)) != -1) { - /* There already exists a connection for this socket pair, which - means an RTS was sent previously. */ - fprintf (stderr, "NCP: Confirmed RTS, connection %d.\n", i); + /* There already exists a connection for this socket pair. */ connection[i].rcv.size = size; - if (connection[i].flags & CONN_CLIENT) { - ncp_all (source, connection[i].rcv.link, 1, 1000); + connection[i].flags |= CONN_GOT_STR; + if (connection[i].flags & CONN_SENT_RTS) { + fprintf (stderr, "NCP: Confirmed RTS, connection %d.\n", i); + if (connection[i].flags & CONN_CLIENT) { + ncp_all (source, connection[i].rcv.link, 1, 1000); + } else { + connection[i].rfc_timeout = NULL; + maybe_reply (i); + } } else { - connection[i].flags |= CONN_GOT_STR; + fprintf (stderr, "NCP: Confirm STR, send RTS sockets %u:%u link %u.\n", + connection[i].rcv.lsock, + connection[i].rcv.rsock, + connection[i].rcv.link); + ncp_rts (connection[i].host, + connection[i].rcv.lsock, + connection[i].rcv.rsock, + connection[i].rcv.link); maybe_reply (i); } - } else if ((i = find_snd_sockets (source, lsock+1, rsock-1)) != -1) { - /* There already exists a connection for this socket pair, but - only an RTS was sent previously. */ - connection[i].rcv.lsock = lsock; - connection[i].rcv.rsock = rsock; - connection[i].rcv.size = size; - connection[i].rcv.link = 45; - connection[i].flags |= CONN_GOT_STR; - fprintf (stderr, "NCP: Confirm STR, send RTS sockets %u:%u link %u.\n", - connection[i].rcv.lsock, - connection[i].rcv.rsock, - connection[i].rcv.link); - ncp_rts (connection[i].host, - connection[i].rcv.lsock, - connection[i].rcv.rsock, - connection[i].rcv.link); - maybe_reply (i); } else { - /* There is no connection for this socket pair. */ - i = make_open (source, lsock, rsock, 0, 0); - connection[i].rcv.size = size; - connection[i].rcv.link = 43; - connection[i].flags |= CONN_GOT_STR; - fprintf (stderr, "NCP: New connection %d link %u.\n", - i, connection[i].rcv.link); - fprintf (stderr, "NCP: Confirm STR, send RTS sockets %u:%u link %u.\n", - connection[i].rcv.lsock, - connection[i].rcv.rsock, - connection[i].rcv.link); - ncp_rts (connection[i].host, - connection[i].rcv.lsock, - connection[i].rcv.rsock, - connection[i].rcv.link); + for (i = 0; i < CONNECTIONS; i++) { + if (connection[i].host == source && + connection[i].snd.lsock+2 == lsock && + (connection[i].flags & CONN_CLIENT) != 0) + break; + } + + if (i == CONNECTIONS) { + fprintf (stderr, "NCP: Refusing RFC to socket %d.\n", lsock); + i = make_open (source, 0, 0, lsock, rsock); + connection[i].snd.size = 0; + ncp_cls (source, lsock, rsock); + unless_rfc (i, just_drop); + } else { + j = make_open (source, lsock, rsock, lsock+1, rsock-1); + connection[j].rcv.size = size; + connection[j].rcv.link = 47; + connection[j].snd.size = 8; + connection[j].flags |= CONN_OPEN | CONN_GOT_STR; + connection[j].listen = connection[i].rcv.rsock; + fprintf (stderr, "NCP: New connection %d sockets %d:%d %d:%d link %d\n", + j, + connection[j].rcv.lsock, connection[j].rcv.rsock, + connection[j].snd.lsock, connection[j].snd.rsock, + connection[j].rcv.link); + when_rfnm (j, send_rts, send_cls_rcv); + unless_rfc (j, just_drop); + maybe_reply (j); + } } return 9; @@ -633,97 +865,164 @@ static int process_cls (uint8_t source, uint8_t *data) { int i; uint32_t lsock, rsock; + rsock = sock (&data[0]); lsock = sock (&data[4]); - i = find_sockets (source, lsock, rsock); - if (i == -1) { + fprintf (stderr, "NCP: Received CLS sockets %u:%u from %03o.\n", + rsock, lsock, source); + + if ((i = find_rcv_sockets (source, lsock, rsock)) != -1) { + connection[i].rcv.size = -1; + if (connection[i].rcv.link != -1) { + fprintf (stderr, "NCP: Remote closed connection %d.\n", i); + CONN_SENT_RCV_CLS(i, =); + ncp_cls (connection[i].host, lsock, rsock); + } else + fprintf (stderr, "NCP: Connection %u confirmed closed.\n", i); + } else if ((i = find_snd_sockets (source, lsock, rsock)) != -1) { + connection[i].snd.size = -1; + if (connection[i].snd.link != -1) { + fprintf (stderr, "NCP: Remote closed connection %d.\n", i); + CONN_SENT_SND_CLS(i, =); + ncp_cls (connection[i].host, lsock, rsock); + } else + fprintf (stderr, "NCP: Connection %u confirmed closed.\n", i); + } else { fprintf (stderr, "NCP: Remote tried to close %u:%u which does not exist.\n", lsock, rsock); ncp_err (source, ERR_SOCKET, data - 1, 9); return 8; } - fprintf (stderr, "NCP: Received CLS sockets %u:%u from %03o.\n", - rsock, lsock, source); - - if (connection[i].rcv.lsock == lsock) - connection[i].rcv.lsock = connection[i].rcv.rsock = 0; - if (connection[i].snd.lsock == lsock) - connection[i].snd.lsock = connection[i].snd.rsock = 0; + if (connection[i].flags & CONN_OPEN) { + fprintf (stderr, "NCP: Connection %u refused.\n", i); + reply_open (source, rsock, 0, 255); + } else if (connection[i].flags & CONN_READ) { + reply_read (source, packet, 0); + } else if (connection[i].flags & CONN_WRITE) { + reply_write (source, 0); + } - if (connection[i].snd.size == -1) { - // Remote confirmed closing. - if (connection[i].rcv.lsock == 0 && connection[i].snd.lsock == 0) { - fprintf (stderr, "NCP: Connection %u confirmed closed.\n", i); - if ((connection[i].flags & (CONN_CLIENT | CONN_SERVER)) == 0) - reply_close (i); - destroy (i); - } - } else { - // Remote closed connection. - ncp_cls (connection[i].host, lsock, rsock); - if (connection[i].rcv.lsock == 0 && connection[i].snd.lsock == 0) { - fprintf (stderr, "NCP: Connection %u closed by remote.\n", i); - destroy (i); - } + if (CONN_GOT_RCV_CLS(i, ==) && CONN_SENT_RCV_CLS(i, ==) && + CONN_GOT_SND_CLS(i, ==) && CONN_SENT_SND_CLS(i, ==)) { + if (connection[i].flags & CONN_CLOSE) + reply_close (i); + destroy (i); } return 8; } +static void just_drop (int i) +{ + fprintf (stderr, "NCP: RFNM timeout, drop connection %d.\n", i); + destroy (i); +} + +static void cls_and_drop (int i) +{ + fprintf (stderr, "NCP: RFNM timeout, close connection %d.\n", i); + CONN_SENT_SND_CLS(i, =); + CONN_SENT_RCV_CLS(i, =); + ncp_cls (connection[i].host, + connection[i].snd.lsock, connection[i].snd.rsock); + ncp_cls (connection[i].host, + connection[i].rcv.lsock, connection[i].rcv.rsock); +} + +static void send_rts (int i) +{ + if (connection[i].flags & CONN_SENT_RTS) + return; + fprintf (stderr, "NCP: Send ICP RTS %u:%u link %d.\n", + connection[i].rcv.lsock, connection[i].rcv.rsock, + connection[i].rcv.link); + ncp_rts (connection[i].host, connection[i].rcv.lsock, + connection[i].rcv.rsock, connection[i].rcv.link); + connection[i].flags |= CONN_SENT_RTS; + unless_rfc (i, rfc_timeout); +} + +static void send_str (int i) +{ + if (connection[i].flags & CONN_SENT_STR) + return; + fprintf (stderr, "NCP: Send STR %u:%u link %d.\n", + connection[i].snd.lsock, connection[i].snd.rsock, + connection[i].snd.link); + ncp_str (connection[i].host, connection[i].snd.lsock, + connection[i].snd.rsock, connection[i].snd.size); + connection[i].flags |= CONN_SENT_STR; + unless_rfc (i, rfc_timeout); +} + +static void send_str_and_rts (int i) +{ + if ((connection[i].flags & CONN_SENT_STR) == 0) { + fprintf (stderr, "NCP: Send ICP STR %u:%u byte size %d.\n", + connection[i].snd.lsock, connection[i].snd.rsock, + connection[i].snd.size); + ncp_str (connection[i].host, connection[i].snd.lsock, + connection[i].snd.rsock, connection[i].snd.size); + connection[i].flags |= CONN_SENT_STR; + unless_rfc (i, rfc_timeout); + } + when_rfnm (i, send_rts, cls_and_drop); + check_rfnm (connection[i].host); +} + +static void send_cls_snd (int i) +{ + fprintf (stderr, "NCP: Close ICP %u:%u link %d.\n", + connection[i].snd.lsock, connection[i].snd.rsock, + connection[i].snd.link); + CONN_SENT_SND_CLS(i, =); + ncp_cls (connection[i].host, + connection[i].snd.lsock, connection[i].snd.rsock); +} + +static void send_cls_rcv (int i) +{ + fprintf (stderr, "NCP: Close ICP %u:%u link %d.\n", + connection[i].rcv.lsock, connection[i].rcv.rsock, + connection[i].rcv.link); + CONN_SENT_RCV_CLS(i, =); + ncp_cls (connection[i].host, + connection[i].rcv.lsock, connection[i].rcv.rsock); +} + +static void send_socket (int i) +{ + int j; + int s = + connection[i].buffer[5] << 24 | + connection[i].buffer[6] << 24 | + connection[i].buffer[7] << 24 | + connection[i].buffer[8]; + fprintf (stderr, "NCP: Send socket %u for ICP.\n", s); + j = find_rcv_sockets (connection[i].host, s, connection[i].snd.rsock + 3); + when_rfnm (j, send_str_and_rts, cls_and_drop); + when_rfnm (i, send_cls_snd, just_drop); + check_rfnm (connection[i].host); +} + static int process_all (uint8_t source, uint8_t *data) { - int i, j; + int i; uint8_t link = data[0]; + uint16_t msgs = data[1] << 8 | data[2]; + uint32_t bits = data[3] << 24 | data[4] << 16 | data[5] << 8 | data[6]; - fprintf (stderr, "NCP: Received ALL from %03o, link %u.\n", - source, link); + fprintf (stderr, "NCP: Received ALL from %03o, link %u, msgs %u, bits %u.\n", + source, link, msgs, bits); i = find_link (source, link); if (i == -1) { ncp_err (source, ERR_SOCKET, data - 1, 10); - } else if (connection[i].flags & CONN_SERVER) { - uint8_t tmp[9]; - uint32_t s = 0200; - tmp[0] = 0; - tmp[1] = 32; - tmp[2] = 0; - tmp[3] = 1; - tmp[4] = 0; - tmp[5] = (s >> 24) & 0xFF; - tmp[6] = (s >> 16) & 0xFF; - tmp[7] = (s >> 8) & 0xFF; - tmp[8] = (s >> 0) & 0xFF; - fprintf (stderr, "NCP: Send socket %u for ICP.\n", s); - send_imp (0, IMP_REGULAR, connection[i].host, connection[i].snd.link, - 0, 0, &tmp, 7); - j = make_open (connection[i].host, - s, connection[i].snd.rsock + 3, - s + 1, connection[i].snd.rsock + 2); - if (j == -1) { - // table full - return 7; - } - connection[j].flags |= CONN_SERVER; - connection[j].listen = listening[i].sock; - fprintf (stderr, "NCP: New connection %d.\n", j); - connection[j].snd.size = 8; - connection[j].rcv.link = 44; - fprintf (stderr, "NCP: Send ICP STR %u:%u byte size %d.\n", - connection[j].snd.lsock, connection[j].snd.rsock, - connection[j].snd.size); - ncp_str (connection[j].host, connection[j].snd.lsock, - connection[j].snd.rsock, connection[j].snd.size); - fprintf (stderr, "NCP: Send ICP RTS %u:%u link %d.\n", - connection[j].rcv.lsock, connection[j].rcv.rsock, - connection[j].rcv.link); - ncp_rts (connection[j].host, connection[j].rcv.lsock, - connection[j].rcv.rsock, connection[j].rcv.link); - fprintf (stderr, "NCP: Close ICP %u:%u link %d.\n", - connection[i].snd.lsock, connection[i].snd.rsock, connection[i].snd.link); - ncp_cls (connection[i].host, - connection[i].snd.lsock, connection[i].snd.rsock); - connection[i].snd.size = connection[i].rcv.size = -1; + return 7; } + connection[i].all_msgs += msgs; + connection[i].all_bits += bits; + check_all (i); return 7; } @@ -782,6 +1081,8 @@ static int process_eco (uint8_t source, uint8_t *data) static void reply_echo (uint8_t host, uint8_t data, uint8_t error) { uint8_t reply[4]; + fprintf (stderr, "NCP: Application echo reply host %03o, data %u, error %u\n", + host, data, error); reply[0] = WIRE_ECHO+1; reply[1] = host; reply[2] = data; @@ -830,7 +1131,7 @@ static int process_err (uint8_t source, uint8_t *data) if (i != -1) { if ((rsock & 1) == 0) rsock--; - reply_open (source, rsock, 255); + reply_open (source, rsock, 0, 255); destroy (i); } } @@ -877,6 +1178,7 @@ static int process_rrp (uint8_t source, uint8_t *data) { fprintf (stderr, "NCP: recieved RRP from %03o.\n", source); hosts[source].flags |= HOST_ALIVE; + check_rrp (source); return 0; } @@ -914,15 +1216,20 @@ static void process_ncp (uint8_t source, uint8_t *data, uint16_t count) } } -static void reply_read (uint8_t connection, uint8_t *data, int n) +static void reply_read (uint8_t i, uint8_t *data, int n) { static uint8_t reply[1000]; + fprintf (stderr, "NCP: Application read reply connection %d, length %d.\n", + i, n); + connection[i].flags &= ~CONN_READ; reply[0] = WIRE_READ+1; - reply[1] = connection; + reply[1] = i; memcpy (reply + 2, data, n); - if (sendto (fd, reply, n + 2, 0, (struct sockaddr *)&client, len) == -1) + if (sendto (fd, reply, n + 2, 0, + (struct sockaddr *)&connection[i].reader.addr, + connection[i].reader.len) == -1) fprintf (stderr, "NCP: sendto %s error: %s.\n", - client.sun_path, strerror (errno)); + connection[i].reader.addr.sun_path, strerror (errno)); } static void process_regular (uint8_t *packet, int length) @@ -956,8 +1263,9 @@ static void process_regular (uint8_t *packet, int length) if (connection[i].flags & CONN_CLIENT) { uint32_t s = sock (&packet[9]); fprintf (stderr, "NCP: ICP link %u socket %u.\n", link, s); - ncp_cls (source, connection[i].rcv.lsock, connection[i].rcv.rsock); - connection[i].snd.rsock = s; + when_rfnm (i, send_cls_rcv, just_drop); + connection[i].rfc_timeout = NULL; + connection[i].flags &= ~CONN_OPEN; j = find_rcv_sockets (source, connection[i].rcv.lsock+2, s+1); if (j == -1) @@ -969,23 +1277,11 @@ static void process_regular (uint8_t *packet, int length) connection[j].snd.size = 8; connection[j].rcv.link = 45; fprintf (stderr, "NCP: New connection %d.\n", j); - fprintf (stderr, "NCP: Send ICP STR %u:%u byte size %d.\n", - connection[j].snd.lsock, connection[j].snd.rsock, - connection[j].snd.size); - ncp_str (connection[j].host, - connection[j].snd.lsock, - connection[j].snd.rsock, - connection[j].snd.size); - fprintf (stderr, "NCP: Send ICP RTS %u:%u link %d.\n", - connection[j].rcv.lsock, connection[j].rcv.rsock, - connection[j].rcv.link); - ncp_rts (connection[j].host, - connection[j].rcv.lsock, - connection[j].rcv.rsock, - connection[j].rcv.link); + when_rfnm (j, send_str_and_rts, cls_and_drop); } connection[j].listen = connection[i].rcv.rsock; - connection[j].flags |= CONN_GOT_SOCKET; + connection[j].flags |= CONN_GOT_SOCKET | CONN_OPEN; + check_rfnm (connection[j].host); maybe_reply (j); return; @@ -1028,6 +1324,7 @@ static void process_rfnm (uint8_t *packet, int length) fprintf (stderr, "NCP: Ready for next message to host %03o link %u.\n", host, packet[2]); hosts[host].outstanding_rfnm--; + check_rfnm (host); } static void process_full (uint8_t *packet, int length) @@ -1174,9 +1471,32 @@ static void app_echo (void) memcpy (&hosts[host].echo.addr, &client, len); hosts[host].echo.len = len; + hosts[host].erp_time = time_tick + ERP_TIMEOUT; ncp_eco (host, app[2]); } +static void app_open_rfc_failed (int i) +{ + fprintf (stderr, "NCP: Timed out completing RFC for connection %d.\n", i); + reply_open (connection[i].host, connection[i].rcv.rsock, 0, 255); + when_rfnm (i, send_cls_rcv, just_drop); +} + +static void app_open_rts (int i) +{ + // Send first ICP RTS. + ncp_rts (connection[i].host, connection[i].rcv.lsock, + connection[i].rcv.rsock, connection[i].rcv.link); + connection[i].flags |= CONN_SENT_RTS; + unless_rfc (i, app_open_rfc_failed); +} + +static void app_open_fail (int i) +{ + fprintf (stderr, "NCP: Timed out waiting for RRP.\n"); + reply_open (connection[i].host, connection[i].rcv.rsock, 0, 255); +} + static void app_open (void) { uint32_t socket; @@ -1187,22 +1507,21 @@ static void app_open (void) fprintf (stderr, "NCP: Application open socket %u on host %03o.\n", socket, host); - if ((hosts[host].flags & HOST_ALIVE) == 0) { - // We haven't communicated with this host yet. - ncp_rst (host); - // Wait for RRP. - } - // Initiate a connection. i = make_open (host, 1002, socket, 0, 0); connection[i].rcv.link = 42; //Receive link. - connection[i].flags |= CONN_CLIENT; + connection[i].flags |= CONN_CLIENT | CONN_OPEN; memcpy (&connection[i].client.addr, &client, len); connection[i].client.len = len; - // Send first ICP RTS. - ncp_rts (connection[i].host, connection[i].rcv.lsock, - connection[i].rcv.rsock, connection[i].rcv.link); + if ((hosts[host].flags & HOST_ALIVE) == 0) { + // We haven't communicated with this host yet, send reset and wait. + ncp_rst (host); + when_rrp (i, app_open_rts, app_open_fail); + } else { + // Ok to send RTS directly. + app_open_rts (i); + } } static void app_listen (void) @@ -1234,17 +1553,47 @@ static void app_read (void) i = app[1]; fprintf (stderr, "NCP: Application read %u octets from connection %u.\n", app[2], i); + connection[i].flags |= CONN_READ; + memcpy (&connection[i].reader.addr, &client, len); + connection[i].reader.len = len; ncp_all (connection[i].host, connection[i].rcv.link, 1, 8 * app[2]); } -static void reply_write (uint8_t connection) +static void reply_write (uint8_t i, uint16_t length) { - uint8_t reply[2]; + uint8_t reply[4]; + fprintf (stderr, "NCP: Application write reply connection %u, length %u.\n", + i, length); + connection[i].flags &= ~CONN_WRITE; reply[0] = WIRE_WRITE+1; - reply[1] = connection; - if (sendto (fd, reply, sizeof reply, 0, (struct sockaddr *)&client, len) == -1) + reply[1] = i; + reply[2] = length >> 8; + reply[3] = length; + if (sendto (fd, reply, sizeof reply, 0, + (struct sockaddr *)&connection[i].writer.addr, + connection[i].writer.len) == -1) fprintf (stderr, "NCP: sendto %s error: %s.\n", - client.sun_path, strerror (errno)); + connection[i].writer.addr.sun_path, strerror (errno)); +} + +static void send_data_timeout (int i) +{ + fprintf (stderr, "NCP: Timeout sending data, connection %d, link %d, %d of %d bytes.\n", + i, connection[i].snd.link, connection[i].remaining, + connection[i].length); + reply_write (i, connection[i].length - connection[i].remaining); +} + +static void send_data_now (int i) +{ + fprintf (stderr, "NCP: Send data, connection %d, link %d, %d bytes.\n", + i, connection[i].snd.link, connection[i].length); + reply_write (i, connection[i].length); +} + +static void send_data (int i) +{ + when_rfnm (i, send_data_now, send_data_timeout); } static void app_write (int n) @@ -1252,15 +1601,12 @@ static void app_write (int n) int i = app[1]; fprintf (stderr, "NCP: Application write, %u bytes to connection %u.\n", n, i); - packet[16] = 0; - packet[17] = connection[i].snd.size; - packet[18] = n >> 8; - packet[19] = n; - packet[20] = 0; - memcpy(packet + 21, app + 2, n); - send_imp (0, IMP_REGULAR, connection[i].host, connection[i].snd.link, 0, 0, - NULL, 2 + (n + 6) / 2); - reply_write (i); + connection[i].flags |= CONN_WRITE; + memcpy (&connection[i].writer.addr, &client, len); + connection[i].writer.len = len; + if (n > sizeof connection[i].buffer - 5) + n = sizeof connection[i].buffer - 5; + when_all (i, app + 2, 8 * n, send_data, send_data_timeout); } static void app_interrupt (void) @@ -1274,7 +1620,10 @@ static void app_close (void) { int i = app[1]; fprintf (stderr, "NCP: Application close, connection %u.\n", i); - connection[i].snd.size = connection[i].rcv.size = -1; + connection[i].flags &= ~CONN_APPS; + connection[i].flags |= CONN_CLOSE; + CONN_SENT_RCV_CLS(i, =); + CONN_SENT_SND_CLS(i, =); ncp_cls (connection[i].host, connection[i].rcv.lsock, connection[i].rcv.rsock); ncp_cls (connection[i].host, connection[i].snd.lsock, connection[i].snd.rsock); } @@ -1310,6 +1659,50 @@ static void application (void) } } +static void tick (void) +{ + void (*to) (int); + int i; + time_tick++; + for (i = 0; i < CONNECTIONS; i++) { + to = connection[i].rrp_timeout; + if (to != NULL && connection[i].rrp_time == time_tick) { + connection[i].rrp_callback = NULL; + connection[i].rrp_timeout = NULL; + connection[i].rrp_time = time_tick - 1; + to (i); + } + to = connection[i].rfnm_timeout; + if (to != NULL && connection[i].rfnm_time == time_tick) { + connection[i].rfnm_callback = NULL; + connection[i].rfnm_timeout = NULL; + connection[i].rrp_time = time_tick - 1; + to (i); + } + to = connection[i].all_timeout; + if (to != NULL && connection[i].all_time == time_tick) { + connection[i].all_callback = NULL; + connection[i].all_timeout = NULL; + connection[i].all_time = time_tick - 1; + to (i); + } + to = connection[i].rfc_timeout; + if (to != NULL && connection[i].rfc_time == time_tick) { + connection[i].rfc_timeout = NULL; + connection[i].rfc_time = time_tick - 1; + to (i); + } + } + for (i = 0; i < 256; i++) { + if (hosts[i].echo.len == 0) + continue; + if (hosts[i].erp_time != time_tick) + continue; + reply_echo (i, 0, 0x20); + hosts[i].echo.len = 0; + } +} + static void cleanup (void) { unlink (server.sun_path); @@ -1337,6 +1730,7 @@ void ncp_init (void) signal (SIGQUIT, sigcleanup); signal (SIGTERM, sigcleanup); atexit (cleanup); + time_tick = 0; } int main (int argc, char **argv) @@ -1349,13 +1743,20 @@ int main (int argc, char **argv) for (;;) { int n; fd_set rfds; + struct timeval tv; FD_ZERO (&rfds); FD_SET (fd, &rfds); imp_fd_set (&rfds); - n = select (33, &rfds, NULL, NULL, NULL); + tv.tv_sec = 1; + tv.tv_usec = 0; + n = select (33, &rfds, NULL, NULL, &tv); if (n == -1) fprintf (stderr, "NCP: select error.\n"); - else if (n > 0) { + else if (n == 0) { + tick (); + tv.tv_sec = 1; + tv.tv_usec = 0; + } else { if (imp_fd_isset (&rfds)) { memset (packet, 0, sizeof packet); imp_receive_message (packet, &n); diff --git a/src/ncp.h b/src/ncp.h index 00c3733..6d0a195 100644 --- a/src/ncp.h +++ b/src/ncp.h @@ -5,6 +5,6 @@ extern int ncp_echo (int host, int data, int *reply); extern int ncp_open (int host, unsigned socket, int *connection); extern int ncp_listen (unsigned socket, int *host, int *connection); extern int ncp_read (int connection, void *data, int *length); -extern int ncp_write (int connection, void *data, int length); +extern int ncp_write (int connection, void *data, int *length); extern int ncp_interrupt (int connection); extern int ncp_close (int connection); diff --git a/src/telnet.c b/src/telnet.c index 3dce993..e006ebc 100644 --- a/src/telnet.c +++ b/src/telnet.c @@ -1,10 +1,13 @@ #include #include +#include #include #include #include #include +#include #include "ncp.h" +#include "tty.h" #define OLD_TELNET 1 #define NEW_TELNET 23 @@ -39,15 +42,36 @@ #define OPT_SUPDUP 025 #define OPT_SUPDUP_OUTPUT 026 -static char options[] = { +#define OMARK 0200 +#define OBREAK 0201 +#define ONOP 0202 +#define ONOECHO 0203 +#define OECHO 0204 +#define OHIDE 0205 +#define OASCII 0240 +#define OTRANSPARENT 0241 +#define OEBCDIC 0242 + +static pid_t reader_pid = 0; +static pid_t writer_pid = 0; + +static char client_options[] = { IAC, DO, OPT_ECHO, IAC, DO, OPT_SUPPRESS_GO_AHEAD, IAC, WILL, OPT_SUPPRESS_GO_AHEAD, - 0 }; -static void option (unsigned char c) +static char server_options[] = { + IAC, DONT, OPT_ECHO, + IAC, DO, OPT_SUPPRESS_GO_AHEAD, + IAC, WILL, OPT_SUPPRESS_GO_AHEAD, + IAC, WILL, OPT_ECHO, +}; + +static void option (int fd) { + unsigned char c; + read (fd, &c, 1); switch (c) { case OPT_BINARY: break; @@ -76,78 +100,205 @@ static void option (unsigned char c) } } -static int special (unsigned char c, const unsigned char *data) +static void special (unsigned char c, int rfd, int wfd) { switch (c) { case IAC: - write (1, &c, 1); - return 0; + write (wfd, &c, 1); + return; case DONT: - option (*data); - return 1; + option (rfd); + return; case DO: - option (*data); - return 1; + option (rfd); + return; case WONT: - option (*data); - return 1; + option (rfd); + return; case WILL: - option (*data); - return 1; + option (rfd); + return; case SB: - return 0; + return; case GA: - return 0; + return; case EL: - return 0; + return; case EC: - write (1, "\b \b", 3); - return 0; + write (wfd, "\b \b", 3); + return; case AYT: - return 0; + return; case AO: - return 0; + return; case IP: - return 0; + return; case BRK: - return 0; + return; case MARK: - return 0; + return; case NOP: - return 0; + return; case SE: - return 0; + return; default: - return 0; + return; } } -static void process (const unsigned char *data, int size) +static void process_new (unsigned char data, int rfd, int wfd) { - int i; + switch (data) { + case NUL: + break; + case IAC: + read (rfd, &data, 1); + special (data, rfd, wfd); + break; + default: + write (wfd, &data, 1); + break; + } +} - for (i = 0; i < size; i++) { - switch (data[i]) { - case NUL: - break; - case IAC: - i++; - i += special (data[i], &data[i + 1]); - break; - default: - write (1, &data[i], 1); - break; +static void process_old (unsigned char data, int rfd, int wfd) +{ + unsigned char crlf[] = { 015, 012 }; + switch (data) { + case NUL: + break; + case 001: case 002: case 003: case 004: case 005: case 006: + break; + case 016: case 017: case 020: case 021: case 022: case 023: + case 024: case 025: case 026: case 027: case 030: case 031: + case 032: case 034: case 035: case 036: case 037: + break; + case 0177: + break; + case 015: + read (rfd, &data, 1); + if (data == NUL) + write (1, crlf, 1); + else if (data == 012) + write (1, crlf, 2); + else + fprintf (stderr, "[CR without LF or NUL]"); + case OMARK: + break; + case OBREAK: + fprintf (stderr, "[BREAK]"); fflush (stderr); + break; + case ONOP: + fprintf (stderr, "[NOP]"); fflush (stderr); + break; + case ONOECHO: + fprintf (stderr, "[NOECHO]"); fflush (stderr); + break; + case OECHO: + fprintf (stderr, "[ECHO]"); fflush (stderr); + break; + case OHIDE: + fprintf (stderr, "[HIDE]"); fflush (stderr); + break; + case OASCII: + fprintf (stderr, "[ASCII]"); fflush (stderr); + break; + case OTRANSPARENT: + fprintf (stderr, "[TRANSPARENT]"); fflush (stderr); + break; + case OEBCDIC: + fprintf (stderr, "[EBCDIC]"); fflush (stderr); + break; + default: + write (wfd, &data, 1); + } +} + +static int reader (int connection) +{ + unsigned char data[200]; + int fds[2]; + int size; + + if (pipe (fds) == -1) + exit (1); + + reader_pid = fork(); + if (reader_pid) { + close (fds[1]); + return fds[0]; + } + close (fds[0]); + + if (ncp_init (NULL) == -1) + exit (1); + + for (;;) { + size = sizeof data; + if (ncp_read (connection, data, &size) == -1) { + fprintf (stderr, "NCP read error.\n"); + exit (1); } + if (size == 0) + exit (0); + if (write (fds[1], data, size) == -1) + exit (1); } } -static void telnet_client (int host, int sock, int new) +static int writer (int connection) { - unsigned char reply[1000]; - int connection, size; + unsigned char data[200], *ptr; + ssize_t n, m; + int fds[2]; + int size; + + if (pipe (fds) == -1) + exit (1); + + writer_pid = fork(); + if (writer_pid) { + close (fds[0]); + return fds[1]; + } + close (fds[1]); + + int flags = fcntl (fds[0], F_GETFL); + fcntl (fds[0], F_SETFL, flags | O_NONBLOCK); + + if (ncp_init (NULL) == -1) + exit (1); + + for (;;) { + n = read (fds[0], data, 200); + if (n == 0) + exit (0); + if (n < 0) + continue; + ptr = data; + for (ptr = data, m = n; m > 0; ptr += size, m -= size) { + size = m; + if (ncp_write (connection, ptr, &size) == -1) { + fprintf (stderr, "NCP write error.\n"); + exit (1); + } + if (size == 0) + exit (0); + } + } +} + +static void telnet_client (int host, int sock, + void (*process) (unsigned char, int, int)) +{ + int connection; + int reader_fd, writer_fd; + size_t size; printf ("TELNET to host %03o.\n", host); + tty_raw (); + switch (ncp_open (host, sock, &connection)) { case 0: break; @@ -160,18 +311,45 @@ static void telnet_client (int host, int sock, int new) exit (1); } - if (ncp_write (connection, options, strlen (options)) == -1) { - fprintf (stderr, "NCP write error.\n"); + reader_fd = reader (connection); + writer_fd = writer (connection); + + size = sizeof client_options; + if (write (writer_fd, client_options, size) == -1) { + fprintf (stderr, "write error.\n"); exit (1); } - size = sizeof reply; - if (ncp_read (connection, reply, &size) == -1) { - fprintf (stderr, "NCP read error.\n"); - exit (1); + for (;;) { + fd_set rfds; + int n; + FD_ZERO (&rfds); + FD_SET (0, &rfds); + FD_SET (reader_fd, &rfds); + n = select (reader_fd + 1, &rfds, NULL, NULL, NULL); + if (n <= 0) + break; + + if (FD_ISSET (0, &rfds)) { + unsigned char data; + read (0, &data, 1); + if (data == 035) + goto end; + write (writer_fd, &data, 1); + } + if (FD_ISSET (reader_fd, &rfds)) { + unsigned char data; + n = read (reader_fd, &data, 1); + if (n == 1) + process (data, reader_fd, 1); + } } - process (reply, size); + end: + kill (reader_pid, SIGTERM); + kill (writer_pid, SIGTERM); + tty_restore (); + printf ("TELNET> quit\n"); if (ncp_close (connection) == -1) { fprintf (stderr, "NCP close error.\n"); @@ -179,9 +357,11 @@ static void telnet_client (int host, int sock, int new) } } -static void telnet_server (int sock, int new) +static void telnet_server (int sock, int new, + void (*process) (unsigned char, int, int)) { - int host, connection; + int host, connection, size; + int reader_fd, writer_fd; char *banner; if (ncp_listen (sock, &host, &connection) == -1) { @@ -189,17 +369,61 @@ static void telnet_server (int sock, int new) exit (1); } - if (ncp_write (connection, options, strlen (options)) == -1) { - fprintf (stderr, "NCP write error.\n"); + reader_fd = reader (connection); + writer_fd = writer (connection); + + size = sizeof server_options; + if (write (writer_fd, server_options, size) == -1) { + fprintf (stderr, "write error.\n"); exit (1); } banner = "Welcome to Unix.\r\n"; - if (ncp_write (connection, banner, strlen (banner)) == -1) { - fprintf (stderr, "NCP write error.\n"); + size = strlen (banner); + if (write (writer_fd, banner, size) == -1) { + fprintf (stderr, "write error.\n"); exit (1); } + char *cmd[] = { "sh", NULL }; + int fd = tty_run (cmd); + + int flags = fcntl (fd, F_GETFL); + fcntl (fd, F_SETFL, flags | O_NONBLOCK); + flags = fcntl (reader_fd, F_GETFL); + fcntl (reader_fd, F_SETFL, flags | O_NONBLOCK); + + for (;;) { + fd_set rfds; + int n = fd > reader_fd ? fd : reader_fd; + FD_ZERO (&rfds); + FD_SET (fd, &rfds); + FD_SET (reader_fd, &rfds); + n = select (n + 1, &rfds, NULL, NULL, NULL); + if (n <= 0) + break; + + if (FD_ISSET (fd, &rfds)) { + unsigned char data[200]; + ssize_t n = read (fd, data, sizeof data); + if (n <= 0) + goto end; + data[n] = 0; + write (writer_fd, data, n); + } + if (FD_ISSET (reader_fd, &rfds)) { + unsigned char data; + ssize_t n = read (reader_fd, &data, 1); + if (n <= 0) + goto end; + process (data, reader_fd, fd); + } + } + + end: + kill (reader_pid, SIGTERM); + kill (writer_pid, SIGTERM); + if (ncp_close (connection) == -1) { fprintf (stderr, "NCP close error.\n"); exit (1); @@ -218,7 +442,7 @@ int main (int argc, char **argv) int host = -1; int sock = -1; - while ((opt = getopt (argc, argv, "cnos")) != -1) { + while ((opt = getopt (argc, argv, "cnosp:")) != -1) { switch (opt) { case 'c': client = 1; @@ -237,7 +461,7 @@ int main (int argc, char **argv) sock = OLD_TELNET; break; case 'p': - sock = atoi (argv[optind]); + sock = atoi (optarg); break; case 's': client = 0; @@ -268,9 +492,9 @@ int main (int argc, char **argv) } if (client) - telnet_client (host, sock, new); + telnet_client (host, sock, new ? process_new : process_old); else if (server) - telnet_server (sock, new); + telnet_server (sock, new, new ? process_new : process_old); return 0; } diff --git a/src/tty.c b/src/tty.c new file mode 100644 index 0000000..7f059c2 --- /dev/null +++ b/src/tty.c @@ -0,0 +1,60 @@ +#define _XOPEN_SOURCE 600 +#define _DEFAULT_SOURCE +#define _DARWIN_C_SOURCE +#include +#include +#include +#include +#include +#include "tty.h" + +#include + +static struct termios saved; + +void tty_restore (void) +{ + tcsetattr (0, TCSAFLUSH, &saved); +} + +void tty_raw (void) +{ + struct termios new; + tcgetattr (0, &saved); + new = saved; + new.c_iflag = 0; + new.c_oflag = 0; + new.c_lflag = 0; + new.c_cc[VMIN] = 1; + new.c_cc[VTIME] = 0; + tcsetattr (0, TCSAFLUSH, &new); +} + +int tty_run (char **cmd) +{ + int fd = posix_openpt (O_RDWR); + if (fd < 0 || + grantpt (fd) < 0 || + unlockpt (fd) < 0) { + exit (1); + } + + if (fork ()) + return fd; + + close (0); + close (1); + close (2); + + setsid (); + + if (open (ptsname (fd), O_RDWR) != 0) + exit (1); + close (fd); + dup (0); + dup (1); + ioctl (0, TIOCSCTTY); + + execvp (cmd[0], cmd); + exit (1); +} diff --git a/src/tty.h b/src/tty.h new file mode 100644 index 0000000..39a2f6c --- /dev/null +++ b/src/tty.h @@ -0,0 +1,3 @@ +void tty_raw (void); +void tty_restore (void); +int tty_run (char **cmd); diff --git a/src/wire.h b/src/wire.h index 46ef15d..320948e 100644 --- a/src/wire.h +++ b/src/wire.h @@ -14,13 +14,13 @@ static int wire_check (int type, int size) case WIRE_ECHO: return size == 3; case WIRE_ECHO+1: return size == 4; case WIRE_OPEN: return size == 6; - case WIRE_OPEN+1: return size == 7; + case WIRE_OPEN+1: return size == 8; case WIRE_LISTEN: return size == 5; case WIRE_LISTEN+1: return size == 7; case WIRE_READ: return size == 3; case WIRE_READ+1: return 1; case WIRE_WRITE: return 1; - case WIRE_WRITE+1: return size == 2; + case WIRE_WRITE+1: return size == 4; case WIRE_INTERRUPT: return size == 2; case WIRE_INTERRUPT+1: return size == 2; case WIRE_CLOSE: return size == 2; diff --git a/test/test.sh b/test/test.sh index d57173d..a85b0b8 100755 --- a/test/test.sh +++ b/test/test.sh @@ -38,7 +38,7 @@ echo "Test ICP and simple data transfer using the Finger protocol." NCP=ncp2 ../src/finser & PID=$! sleep 1 -NCP=ncp3 ../src/finger 002 & +NCP=ncp3 ../src/finger 002 "Sample Finger command from client." & sleep 10 kill $! $PID 2>/dev/null || :