From 4259b3c83a2a280be16ff429ff136aec6e97108e Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 25 Nov 2013 04:36:21 -0800 Subject: [PATCH] TMXR: Added option to use UDP transport for packet transport --- README.md | 1 + sim_sock.c | 75 ++++++++++++++++++++++++++++++++++++++++++++------- sim_sock.h | 1 + sim_tmxr.c | 79 +++++++++++++++++++++++++++++++++++++++++------------- sim_tmxr.h | 4 ++- 5 files changed, 130 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 59f648dc..7e6cbca8 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ A remote console session will close when an EOF character is entered (i.e. ^D or Serial Console Support Separate TCP listening ports per line Outgoing connections per line (virtual Null Modem cable). + Packet sending and reception semantics for simulated network device support using either TCP or UDP transport. #### Asynchronous I/O * Disk and Tape I/O can be asynchronous. Asynchronous support exists diff --git a/sim_sock.c b/sim_sock.c index 0aa039cc..23d56ace 100644 --- a/sim_sock.c +++ b/sim_sock.c @@ -69,6 +69,8 @@ /* OS dependent routines sim_master_sock create master socket + sim_connect_sock connect a socket to a remote destination + sim_connect_sock_ex connect a socket to a remote destination sim_accept_conn accept connection sim_read_sock read from socket sim_write_sock write from socket @@ -99,6 +101,11 @@ SOCKET sim_connect_sock (const char *hostport, const char *default_host, const c return INVALID_SOCKET; } +SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, t_bool datagram) +{ +return INVALID_SOCKET; +} + SOCKET sim_accept_conn (SOCKET master, char **connectaddr); { return INVALID_SOCKET; @@ -710,12 +717,12 @@ return 0; #endif /* endif !Win32 && !VMS */ -static SOCKET sim_create_sock (int af) +static SOCKET sim_create_sock_ex (int af, t_bool datagram) { SOCKET newsock; int32 err; -newsock = socket (af, SOCK_STREAM, 0); /* create socket */ +newsock = socket (af, (datagram ? SOCK_DGRAM : SOCK_STREAM), 0);/* create socket */ if (newsock == INVALID_SOCKET) { /* socket error? */ err = WSAGetLastError (); #if defined(WSAEAFNOSUPPORT) @@ -727,6 +734,11 @@ if (newsock == INVALID_SOCKET) { /* socket error? */ return newsock; } +static SOCKET sim_create_sock (int af) +{ +return sim_create_sock_ex (af, FALSE); +} + /* Some platforms and/or network stacks have varying support for listening on an IPv6 socket and receiving connections from both IPv4 and IPv6 client @@ -823,27 +835,70 @@ return newsock; /* got it! */ SOCKET sim_connect_sock (const char *hostport, const char *default_host, const char *default_port) { +return sim_connect_sock_ex (NULL, hostport, default_host, default_port, FALSE); +} + +SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, t_bool datagram) +{ SOCKET newsock = INVALID_SOCKET; int32 sta; char host[CBUFSIZE], port[CBUFSIZE]; t_stat r; struct addrinfo hints; -struct addrinfo *result = NULL; +struct addrinfo *result = NULL, *source = NULL; r = sim_parse_addr (hostport, host, sizeof(host), default_host, port, sizeof(port), default_port, NULL); if (r != SCPE_OK) - return newsock; + return INVALID_SOCKET; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; -hints.ai_protocol = IPPROTO_TCP; -hints.ai_socktype = SOCK_STREAM; +hints.ai_protocol = (datagram ? IPPROTO_UDP : IPPROTO_TCP); +hints.ai_socktype = (datagram ? SOCK_DGRAM : SOCK_STREAM); if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &result)) - return newsock; -newsock = sim_create_sock (result->ai_family); /* create socket */ + return INVALID_SOCKET; + +if (sourcehostport) { + + /* Validate the local/source side address which we'll bind to */ + r = sim_parse_addr (sourcehostport, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL); + if (r != SCPE_OK) { + p_freeaddrinfo (result); + return INVALID_SOCKET; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = result->ai_family; /* Same family as connect destination */ + hints.ai_protocol = (datagram ? IPPROTO_UDP : IPPROTO_TCP); + hints.ai_socktype = (datagram ? SOCK_DGRAM : SOCK_STREAM); + if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &source)) { + p_freeaddrinfo (result); + return INVALID_SOCKET; + } + + newsock = sim_create_sock_ex (result->ai_family, datagram);/* create socket */ + if (newsock == INVALID_SOCKET) { /* socket error? */ + p_freeaddrinfo (result); + p_freeaddrinfo (source); + return newsock; + } + + sta = bind (newsock, source->ai_addr, source->ai_addrlen); + p_freeaddrinfo(source); + source = NULL; + if (sta == SOCKET_ERROR) { /* bind error? */ + p_freeaddrinfo (result); + return sim_err_sock (newsock, "bind", 1); + } + } + if (newsock == INVALID_SOCKET) { /* socket error? */ - p_freeaddrinfo (result); - return newsock; + newsock = sim_create_sock_ex (result->ai_family, datagram);/* create socket */ + if (newsock == INVALID_SOCKET) { /* socket error? */ + p_freeaddrinfo (result); + return newsock; + } } sta = sim_setnonblock (newsock); /* set nonblocking */ diff --git a/sim_sock.h b/sim_sock.h index 44721c49..6be04067 100644 --- a/sim_sock.h +++ b/sim_sock.h @@ -104,6 +104,7 @@ t_stat sim_parse_addr (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr); SOCKET sim_master_sock (const char *hostport, t_stat *parse_status); SOCKET sim_connect_sock (const char *hostport, const char *default_host, const char *default_port); +SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, t_bool datagram); SOCKET sim_accept_conn (SOCKET master, char **connectaddr); int32 sim_check_conn (SOCKET sock, t_bool rd); int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes); diff --git a/sim_tmxr.c b/sim_tmxr.c index 02bb8dc5..d61f21e2 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -862,6 +862,10 @@ if (lp->destination || lp->port || lp->txlogname) { sprintf (growstring(&tptr, 32), ",Buffered=%d", lp->txbsz); if (!lp->txbfd && (lp->mp->buffered > 0)) sprintf (growstring(&tptr, 32), ",UnBuffered"); + if (lp->mp->datagram != lp->datagram) + sprintf (growstring(&tptr, 8), ",%s", lp->datagram ? "UDP" : "TCP"); + if (lp->port) + sprintf (growstring(&tptr, 12 + strlen (lp->port)), ",%s%s", lp->port, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : ""); if (lp->destination) { if (lp->serport) { char portname[CBUFSIZE]; @@ -870,10 +874,8 @@ if (lp->destination || lp->port || lp->txlogname) { sprintf (growstring(&tptr, 25 + strlen (lp->destination)), ",Connect=%s%s%s", portname, strcmp("9600-8N1", lp->serconfig) ? ";" : "", strcmp("9600-8N1", lp->serconfig) ? lp->serconfig : ""); } else - sprintf (growstring(&tptr, 25 + strlen (lp->destination)), ",Connect=%s%s", lp->destination, (lp->mp->notelnet != lp->notelnet) ? (lp->notelnet ? ";notelnet" : ";telnet") : ""); + sprintf (growstring(&tptr, 25 + strlen (lp->destination)), ",Connect=%s%s", lp->destination, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : ""); } - if (lp->port) - sprintf (growstring(&tptr, 12 + strlen (lp->port)), ",%s%s", lp->port, (lp->mp->notelnet != lp->notelnet) ? (lp->notelnet ? ";notelnet" : ";telnet") : ""); if (lp->txlogname) sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname); if (lp->loopback) @@ -1136,7 +1138,7 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR))) { sprintf (msg, "tmxr_poll_conn() - establishing outgoing connection to: %s", lp->destination); tmxr_debug_connect_line (lp, msg); - lp->connecting = sim_connect_sock (lp->destination, "localhost", NULL); + lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, lp->datagram); } } @@ -1204,7 +1206,7 @@ if ((lp->destination) && (!lp->serport)) { if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) { sprintf (msg, "tmxr_reset_ln_ex() - connecting to %s", lp->destination); tmxr_debug_connect_line (lp, msg); - lp->connecting = sim_connect_sock (lp->destination, "localhost", NULL); + lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, lp->datagram); } } tmxr_init_line (lp); /* initialize line state */ @@ -1382,7 +1384,7 @@ if (lp->mp && lp->modem_control) { /* This API ONLY works on mo sprintf (msg, "tmxr_set_get_modem_bits() - establishing outgoing connection to: %s", lp->destination); tmxr_debug_connect_line (lp, msg); - lp->connecting = sim_connect_sock (lp->destination, "localhost", NULL); + lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, lp->datagram); } } } @@ -1900,6 +1902,8 @@ if (nbytes) { /* >0? write */ lp->txbpr = 0; lp->txcnt = lp->txcnt + sbytes; /* update counts */ nbytes = nbytes - sbytes; + if ((nbytes == 0) && (lp->datagram)) /* if Empty buffer on datagram line */ + lp->txbpi = lp->txbpr = 0; /* Start next packet at beginning of buffer */ } if (sbytes < 0) { /* I/O Error? */ lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */ @@ -2012,7 +2016,7 @@ char tbuf[CBUFSIZE], listen[CBUFSIZE], destination[CBUFSIZE], SOCKET sock; SERHANDLE serport; char *tptr = cptr; -t_bool nolog, notelnet, listennotelnet, unbuffered, modem_control, loopback; +t_bool nolog, notelnet, listennotelnet, unbuffered, modem_control, loopback, datagram; TMLN *lp; t_stat r = SCPE_ARG; @@ -2031,6 +2035,7 @@ while (*tptr) { memset(port, '\0', sizeof(port)); memset(option, '\0', sizeof(option)); nolog = notelnet = listennotelnet = unbuffered = loopback = FALSE; + datagram = mp->datagram; if (line != -1) notelnet = listennotelnet = mp->notelnet; modem_control = mp->modem_control; @@ -2100,6 +2105,18 @@ while (*tptr) { modem_control = TRUE; continue; } + if ((0 == MATCH_CMD (gbuf, "DATAGRAM")) || (0 == MATCH_CMD (gbuf, "UDP"))) { + if ((NULL != cptr) && ('\0' != *cptr)) + return SCPE_2MARG; + notelnet = datagram = TRUE; + continue; + } + if ((0 == MATCH_CMD (gbuf, "STREAM")) || (0 == MATCH_CMD (gbuf, "TCP"))) { + if ((NULL != cptr) && ('\0' != *cptr)) + return SCPE_2MARG; + datagram = FALSE; + continue; + } if (0 == MATCH_CMD (gbuf, "CONNECT")) { if ((NULL == cptr) || ('\0' == *cptr)) return SCPE_ARG; @@ -2114,21 +2131,24 @@ while (*tptr) { strncpy (hostport, cptr, sizeof(hostport)-1); if ((cptr = strchr (hostport, ';'))) *(cptr++) = '\0'; - sock = sim_connect_sock (hostport, "localhost", NULL); - if (sock != INVALID_SOCKET) - sim_close_sock (sock, 0); - else - return SCPE_ARG; if (cptr) { get_glyph (cptr, cptr, 0); /* upcase this string */ if (0 == MATCH_CMD (cptr, "NOTELNET")) notelnet = TRUE; else if (0 == MATCH_CMD (cptr, "TELNET")) - notelnet = FALSE; + if (datagram) + return SCPE_ARG; + else + notelnet = FALSE; else return SCPE_ARG; } + sock = sim_connect_sock_ex (NULL, hostport, "localhost", NULL, datagram); + if (sock != INVALID_SOCKET) + sim_close_sock (sock, 0); + else + return SCPE_ARG; cptr = hostport; } strcpy(destination, cptr); @@ -2231,7 +2251,7 @@ while (*tptr) { } } } - if (listen[0]) { + if ((listen[0]) && (!datagram)) { sock = sim_master_sock (listen, &r); /* make master socket */ if (r != SCPE_OK) return r; @@ -2306,7 +2326,16 @@ while (*tptr) { } } else { - sock = sim_connect_sock (destination, "localhost", NULL); + lp->datagram = datagram; + if (datagram) { + if (listen[0]) { + lp->port = (char *)realloc (lp->port, 1 + strlen (listen)); + strcpy(lp->port, listen); /* save port */ + } + else + return SCPE_ARG; + } + sock = sim_connect_sock_ex (datagram ? listen : NULL, destination, "localhost", NULL, datagram); if (sock != INVALID_SOCKET) { _mux_detach_line (lp, FALSE, TRUE); lp->destination = (char *)malloc(1+strlen(destination)); @@ -2370,7 +2399,7 @@ while (*tptr) { lp->txlog = NULL; } } - if (listen[0]) { + if ((listen[0]) && (!datagram)) { if ((mp->lines == 1) && (mp->master)) /* single line mux can have either line specific OR mux listener but NOT both */ return SCPE_ARG; sock = sim_master_sock (listen, &r); /* make master socket */ @@ -2409,7 +2438,16 @@ while (*tptr) { } } else { - sock = sim_connect_sock (destination, "localhost", NULL); + lp->datagram = datagram; + if (datagram) { + if (listen[0]) { + lp->port = (char *)realloc (lp->port, 1 + strlen (listen)); + strcpy(lp->port, listen); /* save port */ + } + else + return SCPE_ARG; + } + sock = sim_connect_sock_ex (datagram ? listen : NULL, destination, "localhost", NULL, datagram); if (sock != INVALID_SOCKET) { _mux_detach_line (lp, FALSE, TRUE); lp->destination = (char *)malloc(1+strlen(destination)); @@ -3673,7 +3711,10 @@ if (ln >= 0) if ((lp->sock) || (lp->connecting)) { /* tcp connection? */ if (lp->destination) /* remote connection? */ - fprintf (st, "Connection to remote port %s\n", lp->destination);/* print port name */ + if (lp->datagram) + fprintf (st, "Datagram Connection from %s to remote port %s\n", lp->port, lp->destination);/* print port name */ + else + fprintf (st, "Connection to remote port %s\n", lp->destination);/* print port name */ else /* incoming connection */ fprintf (st, "Connection from IP address %s\n", lp->ipad); } @@ -3689,7 +3730,7 @@ if (lp->sock) { free (peername); } -if (lp->port) +if ((lp->port) && (!lp->datagram)) fprintf (st, "Listening on port %s\n", lp->port); /* print port name */ if (lp->serport) /* serial connection? */ diff --git a/sim_tmxr.h b/sim_tmxr.h index e8c08f35..13d0667c 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -114,7 +114,7 @@ struct tmln { int32 tsta; /* Telnet state */ int32 rcve; /* rcv enable */ int32 xmte; /* xmt enable */ - int32 dstb; /* disable Tlnt bin */ + int32 dstb; /* disable Telnet binary mode */ t_bool notelnet; /* raw binary data (no telnet interpretation) */ int32 rxbpr; /* rcv buf remove */ int32 rxbpi; /* rcv buf insert */ @@ -151,6 +151,7 @@ struct tmln { char *destination; /* Outgoing destination address:port */ t_bool loopback; /* Line in loopback mode */ t_bool halfduplex; /* Line in half-duplex mode */ + t_bool datagram; /* Line is datagram packet oriented */ int32 lpbpr; /* loopback buf remove */ int32 lpbpi; /* loopback buf insert */ int32 lpbcnt; /* loopback buf used count */ @@ -176,6 +177,7 @@ struct tmxr { uint32 last_poll_time; /* time of last connection poll */ t_bool notelnet; /* default telnet capability for incoming connections */ t_bool modem_control; /* multiplexer supports modem control behaviors */ + t_bool datagram; /* Lines are datagram packet oriented */ }; int32 tmxr_poll_conn (TMXR *mp);