TMXR: Added option to use UDP transport for packet transport

This commit is contained in:
Mark Pizzolato 2013-11-25 04:36:21 -08:00
parent 25ded050ed
commit 4259b3c83a
5 changed files with 130 additions and 30 deletions

View file

@ -68,6 +68,7 @@ A remote console session will close when an EOF character is entered (i.e. ^D or
Serial Console Support Serial Console Support
Separate TCP listening ports per line Separate TCP listening ports per line
Outgoing connections per line (virtual Null Modem cable). 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 #### Asynchronous I/O
* Disk and Tape I/O can be asynchronous. Asynchronous support exists * Disk and Tape I/O can be asynchronous. Asynchronous support exists

View file

@ -69,6 +69,8 @@
/* OS dependent routines /* OS dependent routines
sim_master_sock create master socket 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_accept_conn accept connection
sim_read_sock read from socket sim_read_sock read from socket
sim_write_sock write 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; 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); SOCKET sim_accept_conn (SOCKET master, char **connectaddr);
{ {
return INVALID_SOCKET; return INVALID_SOCKET;
@ -710,12 +717,12 @@ return 0;
#endif /* endif !Win32 && !VMS */ #endif /* endif !Win32 && !VMS */
static SOCKET sim_create_sock (int af) static SOCKET sim_create_sock_ex (int af, t_bool datagram)
{ {
SOCKET newsock; SOCKET newsock;
int32 err; 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? */ if (newsock == INVALID_SOCKET) { /* socket error? */
err = WSAGetLastError (); err = WSAGetLastError ();
#if defined(WSAEAFNOSUPPORT) #if defined(WSAEAFNOSUPPORT)
@ -727,6 +734,11 @@ if (newsock == INVALID_SOCKET) { /* socket error? */
return newsock; 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 Some platforms and/or network stacks have varying support for listening on
an IPv6 socket and receiving connections from both IPv4 and IPv6 client an IPv6 socket and receiving connections from both IPv4 and IPv6 client
@ -823,28 +835,71 @@ return newsock; /* got it! */
SOCKET sim_connect_sock (const char *hostport, const char *default_host, const char *default_port) 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; SOCKET newsock = INVALID_SOCKET;
int32 sta; int32 sta;
char host[CBUFSIZE], port[CBUFSIZE]; char host[CBUFSIZE], port[CBUFSIZE];
t_stat r; t_stat r;
struct addrinfo hints; 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); r = sim_parse_addr (hostport, host, sizeof(host), default_host, port, sizeof(port), default_port, NULL);
if (r != SCPE_OK) if (r != SCPE_OK)
return newsock; return INVALID_SOCKET;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_protocol = IPPROTO_TCP; hints.ai_protocol = (datagram ? IPPROTO_UDP : IPPROTO_TCP);
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = (datagram ? SOCK_DGRAM : SOCK_STREAM);
if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &result)) if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &result))
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; return newsock;
newsock = sim_create_sock (result->ai_family); /* create socket */ }
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? */ if (newsock == INVALID_SOCKET) { /* socket error? */
newsock = sim_create_sock_ex (result->ai_family, datagram);/* create socket */
if (newsock == INVALID_SOCKET) { /* socket error? */
p_freeaddrinfo (result); p_freeaddrinfo (result);
return newsock; return newsock;
} }
}
sta = sim_setnonblock (newsock); /* set nonblocking */ sta = sim_setnonblock (newsock); /* set nonblocking */
if (sta == SOCKET_ERROR) { /* fcntl error? */ if (sta == SOCKET_ERROR) { /* fcntl error? */

View file

@ -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); 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_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 (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); SOCKET sim_accept_conn (SOCKET master, char **connectaddr);
int32 sim_check_conn (SOCKET sock, t_bool rd); int32 sim_check_conn (SOCKET sock, t_bool rd);
int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes); int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes);

View file

@ -862,6 +862,10 @@ if (lp->destination || lp->port || lp->txlogname) {
sprintf (growstring(&tptr, 32), ",Buffered=%d", lp->txbsz); sprintf (growstring(&tptr, 32), ",Buffered=%d", lp->txbsz);
if (!lp->txbfd && (lp->mp->buffered > 0)) if (!lp->txbfd && (lp->mp->buffered > 0))
sprintf (growstring(&tptr, 32), ",UnBuffered"); 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->destination) {
if (lp->serport) { if (lp->serport) {
char portname[CBUFSIZE]; 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 : ""); 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 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) if (lp->txlogname)
sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname); sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname);
if (lp->loopback) 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))) { (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR))) {
sprintf (msg, "tmxr_poll_conn() - establishing outgoing connection to: %s", lp->destination); sprintf (msg, "tmxr_poll_conn() - establishing outgoing connection to: %s", lp->destination);
tmxr_debug_connect_line (lp, msg); 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)) { if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) {
sprintf (msg, "tmxr_reset_ln_ex() - connecting to %s", lp->destination); sprintf (msg, "tmxr_reset_ln_ex() - connecting to %s", lp->destination);
tmxr_debug_connect_line (lp, msg); 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 */ 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); sprintf (msg, "tmxr_set_get_modem_bits() - establishing outgoing connection to: %s", lp->destination);
tmxr_debug_connect_line (lp, msg); 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->txbpr = 0;
lp->txcnt = lp->txcnt + sbytes; /* update counts */ lp->txcnt = lp->txcnt + sbytes; /* update counts */
nbytes = nbytes - sbytes; 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? */ if (sbytes < 0) { /* I/O Error? */
lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */ 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; SOCKET sock;
SERHANDLE serport; SERHANDLE serport;
char *tptr = cptr; char *tptr = cptr;
t_bool nolog, notelnet, listennotelnet, unbuffered, modem_control, loopback; t_bool nolog, notelnet, listennotelnet, unbuffered, modem_control, loopback, datagram;
TMLN *lp; TMLN *lp;
t_stat r = SCPE_ARG; t_stat r = SCPE_ARG;
@ -2031,6 +2035,7 @@ while (*tptr) {
memset(port, '\0', sizeof(port)); memset(port, '\0', sizeof(port));
memset(option, '\0', sizeof(option)); memset(option, '\0', sizeof(option));
nolog = notelnet = listennotelnet = unbuffered = loopback = FALSE; nolog = notelnet = listennotelnet = unbuffered = loopback = FALSE;
datagram = mp->datagram;
if (line != -1) if (line != -1)
notelnet = listennotelnet = mp->notelnet; notelnet = listennotelnet = mp->notelnet;
modem_control = mp->modem_control; modem_control = mp->modem_control;
@ -2100,6 +2105,18 @@ while (*tptr) {
modem_control = TRUE; modem_control = TRUE;
continue; 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 (0 == MATCH_CMD (gbuf, "CONNECT")) {
if ((NULL == cptr) || ('\0' == *cptr)) if ((NULL == cptr) || ('\0' == *cptr))
return SCPE_ARG; return SCPE_ARG;
@ -2114,21 +2131,24 @@ while (*tptr) {
strncpy (hostport, cptr, sizeof(hostport)-1); strncpy (hostport, cptr, sizeof(hostport)-1);
if ((cptr = strchr (hostport, ';'))) if ((cptr = strchr (hostport, ';')))
*(cptr++) = '\0'; *(cptr++) = '\0';
sock = sim_connect_sock (hostport, "localhost", NULL);
if (sock != INVALID_SOCKET)
sim_close_sock (sock, 0);
else
return SCPE_ARG;
if (cptr) { if (cptr) {
get_glyph (cptr, cptr, 0); /* upcase this string */ get_glyph (cptr, cptr, 0); /* upcase this string */
if (0 == MATCH_CMD (cptr, "NOTELNET")) if (0 == MATCH_CMD (cptr, "NOTELNET"))
notelnet = TRUE; notelnet = TRUE;
else else
if (0 == MATCH_CMD (cptr, "TELNET")) if (0 == MATCH_CMD (cptr, "TELNET"))
if (datagram)
return SCPE_ARG;
else
notelnet = FALSE; notelnet = FALSE;
else else
return SCPE_ARG; 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; cptr = hostport;
} }
strcpy(destination, cptr); 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 */ sock = sim_master_sock (listen, &r); /* make master socket */
if (r != SCPE_OK) if (r != SCPE_OK)
return r; return r;
@ -2306,7 +2326,16 @@ while (*tptr) {
} }
} }
else { 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) { if (sock != INVALID_SOCKET) {
_mux_detach_line (lp, FALSE, TRUE); _mux_detach_line (lp, FALSE, TRUE);
lp->destination = (char *)malloc(1+strlen(destination)); lp->destination = (char *)malloc(1+strlen(destination));
@ -2370,7 +2399,7 @@ while (*tptr) {
lp->txlog = NULL; 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 */ if ((mp->lines == 1) && (mp->master)) /* single line mux can have either line specific OR mux listener but NOT both */
return SCPE_ARG; return SCPE_ARG;
sock = sim_master_sock (listen, &r); /* make master socket */ sock = sim_master_sock (listen, &r); /* make master socket */
@ -2409,7 +2438,16 @@ while (*tptr) {
} }
} }
else { 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) { if (sock != INVALID_SOCKET) {
_mux_detach_line (lp, FALSE, TRUE); _mux_detach_line (lp, FALSE, TRUE);
lp->destination = (char *)malloc(1+strlen(destination)); lp->destination = (char *)malloc(1+strlen(destination));
@ -3673,6 +3711,9 @@ if (ln >= 0)
if ((lp->sock) || (lp->connecting)) { /* tcp connection? */ if ((lp->sock) || (lp->connecting)) { /* tcp connection? */
if (lp->destination) /* remote connection? */ if (lp->destination) /* remote connection? */
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 */ fprintf (st, "Connection to remote port %s\n", lp->destination);/* print port name */
else /* incoming connection */ else /* incoming connection */
fprintf (st, "Connection from IP address %s\n", lp->ipad); fprintf (st, "Connection from IP address %s\n", lp->ipad);
@ -3689,7 +3730,7 @@ if (lp->sock) {
free (peername); free (peername);
} }
if (lp->port) if ((lp->port) && (!lp->datagram))
fprintf (st, "Listening on port %s\n", lp->port); /* print port name */ fprintf (st, "Listening on port %s\n", lp->port); /* print port name */
if (lp->serport) /* serial connection? */ if (lp->serport) /* serial connection? */

View file

@ -114,7 +114,7 @@ struct tmln {
int32 tsta; /* Telnet state */ int32 tsta; /* Telnet state */
int32 rcve; /* rcv enable */ int32 rcve; /* rcv enable */
int32 xmte; /* xmt 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) */ t_bool notelnet; /* raw binary data (no telnet interpretation) */
int32 rxbpr; /* rcv buf remove */ int32 rxbpr; /* rcv buf remove */
int32 rxbpi; /* rcv buf insert */ int32 rxbpi; /* rcv buf insert */
@ -151,6 +151,7 @@ struct tmln {
char *destination; /* Outgoing destination address:port */ char *destination; /* Outgoing destination address:port */
t_bool loopback; /* Line in loopback mode */ t_bool loopback; /* Line in loopback mode */
t_bool halfduplex; /* Line in half-duplex mode */ t_bool halfduplex; /* Line in half-duplex mode */
t_bool datagram; /* Line is datagram packet oriented */
int32 lpbpr; /* loopback buf remove */ int32 lpbpr; /* loopback buf remove */
int32 lpbpi; /* loopback buf insert */ int32 lpbpi; /* loopback buf insert */
int32 lpbcnt; /* loopback buf used count */ int32 lpbcnt; /* loopback buf used count */
@ -176,6 +177,7 @@ struct tmxr {
uint32 last_poll_time; /* time of last connection poll */ uint32 last_poll_time; /* time of last connection poll */
t_bool notelnet; /* default telnet capability for incoming connections */ t_bool notelnet; /* default telnet capability for incoming connections */
t_bool modem_control; /* multiplexer supports modem control behaviors */ t_bool modem_control; /* multiplexer supports modem control behaviors */
t_bool datagram; /* Lines are datagram packet oriented */
}; };
int32 tmxr_poll_conn (TMXR *mp); int32 tmxr_poll_conn (TMXR *mp);