TMXR: Added option to use UDP transport for packet transport
This commit is contained in:
parent
25ded050ed
commit
4259b3c83a
5 changed files with 130 additions and 30 deletions
|
@ -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
|
||||
|
|
75
sim_sock.c
75
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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
79
sim_tmxr.c
79
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? */
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue