diff --git a/H316/h316_imp.h b/H316/h316_imp.h index 07177ef3..78e37a66 100644 --- a/H316/h316_imp.h +++ b/H316/h316_imp.h @@ -192,7 +192,7 @@ extern t_stat mi_tx_service (uint32 quantum); t_stat udp_create (DEVICE *pdtr, char *premote, int32 *plink); t_stat udp_release (DEVICE *dptr, int32 link); t_stat udp_send (DEVICE *pdtr, int32 link, uint16 *pdata, uint16 count); -t_stat udp_send_self (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count); +t_stat udp_set_link_loopback (DEVICE *dptr, int32 link, t_bool enable_loopback); int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbufg); #endif // #ifndef _H316_IMP_H_ diff --git a/H316/h316_mi.c b/H316/h316_mi.c index be6f256a..7cc7f361 100644 --- a/H316/h316_mi.c +++ b/H316/h316_mi.c @@ -319,6 +319,7 @@ MIDB *const mi_midbs [MI_NUM] = {&mi1_db, &mi2_db, &mi3_db, &mi4_db, void mi_reset_rx (uint16 line) { PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE; + udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE); PMIDB(line)->rxerror = PMIDB(line)->rxpending = FALSE; PMIDB(line)->rxtotal = 0; CLR_RX_IRQ(line); CLR_RX_IEN(line); @@ -328,6 +329,7 @@ void mi_reset_rx (uint16 line) void mi_reset_tx (uint16 line) { PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE; + udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE); PMIDB(line)->txtotal = PMIDB(line)->txdelay = 0; CLR_TX_IRQ(line); CLR_TX_IEN(line); } @@ -424,10 +426,7 @@ void mi_start_tx (uint16 line) if (PMIDB(line)->iloop) { mi_rx_local(line, next, count); } else if (PMIDB(line)->link != NOLINK) { - if (PMIDB(line)->lloop) - ret = udp_send_self(PDEVICE(line), PMIDB(line)->link, &M[next], count); - else - ret = udp_send(PDEVICE(line), PMIDB(line)->link, &M[next], count); + ret = udp_send(PDEVICE(line), PMIDB(line)->link, &M[next], count); if (ret != SCPE_OK) mi_link_error(line); } @@ -581,15 +580,21 @@ int32 mi_io (uint16 line, int32 inst, int32 fnc, int32 dat, int32 dev) case 001: // MnUNXP - un-cross patch modem ... sim_debug(IMP_DBG_IOT,PDEVICE(line),"un-cross patch modem (PC=%06o)\n", PC-1); - PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE; return dat; + PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE; + udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE); + return dat; case 002: // MnLXP - enable line cross patch ... sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable line cross patch (PC=%06o)\n", PC-1); - PMIDB(line)->lloop = TRUE; PMIDB(line)->iloop = FALSE; return dat; + PMIDB(line)->lloop = TRUE; + udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, TRUE); + PMIDB(line)->iloop = FALSE; return dat; case 003: // MnIXP - enable interface cross patch ... sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable interface cross patch (PC=%06o)\n", PC-1); - PMIDB(line)->iloop = TRUE; PMIDB(line)->lloop = FALSE; return dat; + PMIDB(line)->iloop = TRUE; PMIDB(line)->lloop = FALSE; + udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE); + return dat; case 004: // MnIN - start modem input ... mi_debug_mio(line, PDIB(line)->rxdmc, "input"); diff --git a/H316/h316_udp.c b/H316/h316_udp.c index d2455a7f..adf898dc 100644 --- a/H316/h316_udp.c +++ b/H316/h316_udp.c @@ -29,7 +29,8 @@ udp socket routines 26-Jun-13 RLA Rewritten from TCP version - + 26-Nov-13 MP Rewritten to use TMXR layer packet semantics thus + allowing portability to all simh hosts. OVERVIEW @@ -134,15 +135,7 @@ */ #ifdef VM_IMPTIP #include "sim_defs.h" // simh machine independent definitions -#ifdef _WIN32 // WINSOCK definitions -#include // at least Windows puts it all in one file! -#elif defined(__linux__) // Linux definitions -#include // struct socketaddr_in, et al -#include // INADDR_NONE, et al -#include // gethostbyname() -#include // fcntl() (what else??) -#include // getpid(), more? -#endif +#include "sim_tmxr.h" // The MUX layer exposes packet send and receive semantics #include "h316_defs.h" // H316 emulator definitions #include "h316_imp.h" // ARPAnet IMP/TIP definitions @@ -158,30 +151,15 @@ // the fragments arrive intact then the destination should reassemble them. #define MAXDATA 16384 // longest possible IMP packet (in H316 words) -// Compatibility hacks for WINSOCK vs Linux ... -#ifdef __linux__ -#define WSAGetLastError() errno -#define closesocket close -#define SOCKET int32 -#define SOCKADDR struct sockaddr -#define WSAEWOULDBLOCK EWOULDBLOCK -#define INVALID_SOCKET ((SOCKET) (-1)) -#define SOCKET_ERROR (-1) -#endif - // UDP connection data structure ... // One of these blocks is allocated for every simulated modem link. struct _UDP_LINK { t_bool used; // TRUE if this UDP_LINK is in use - uint32 ipremote; // IP address of the remote system - uint16 rxport; // OUR receiving port number - uint16 txport; // HIS receiving port number (on ipremote) - struct sockaddr_in rxaddr; // OUR receiving address (goes with rxsock!) - struct sockaddr_in txaddr; // HIS transmitting address (pairs with txsock!) - SOCKET rxsock; // socket for receiving incoming packets - SOCKET txsock; // socket for sending outgoing packets + char rhostport[64]; // Remote host:port + char lport[64]; // Local port uint32 rxsequence; // next message sequence number for receive uint32 txsequence; // next message sequence number for transmit + DEVICE *dptr; // Device associated with link }; typedef struct _UDP_LINK UDP_LINK; @@ -206,44 +184,9 @@ typedef struct _UDP_PACKET UDP_PACKET; #define UDP_HEADER_LEN (2*sizeof(uint32) + sizeof(uint16)) // Locals ... -t_bool udp_wsa_started = FALSE; // TRUE if WSAStartup() has been called -UDP_LINK udp_links[MAXLINKS] = {0}; // data for every active connection - - -t_stat udp_startup (DEVICE *dptr) -{ - // WINSOCK requires that WSAStartup be called exactly once before any other - // network calls are made. That's a bit inconvenient, but this routine deals - // with it by using a static variable to call WSAStartup the first time thru - // and then never again. -#ifdef _WIN32 - WORD wVersionRequested = MAKEWORD(2,2); - WSADATA wsaData; int32 ret; - if (!udp_wsa_started) { - ret = WSAStartup (wVersionRequested, &wsaData); - if (ret != 0) { - fprintf(stderr,"UDP - WINSOCK startup error %d\n", ret); - return SCPE_IERR; - } else - sim_debug(IMP_DBG_UDP, dptr, "WSAStartup() called\n"); - udp_wsa_started = TRUE; - } -#endif - return SCPE_OK; -} - -t_stat udp_shutdown (DEVICE *dptr) -{ - // This routine calls WSACleanup() after the last socket has been closed. - // It's essentially the opposite of udp_startup() ... -#ifdef _WIN32 - if (udp_wsa_started) { - WSACleanup(); udp_wsa_started = FALSE; - sim_debug(IMP_DBG_UDP, dptr, "WSACleanup() called\n"); - } -#endif - return SCPE_OK; -} +UDP_LINK udp_links[MAXLINKS] = {0}; // data for every active connection +TMLN udp_lines[MAXLINKS] = { 0 }; // line descriptors +TMXR udp_tmxr = { MAXLINKS, NULL, 0, udp_lines};// datagram mux int32 udp_find_free_link (void) { @@ -253,91 +196,12 @@ int32 udp_find_free_link (void) for (i = 0; i < MAXLINKS; ++i) { if (udp_links[i].used == 0) { memset(&udp_links[i], 0, sizeof(UDP_LINK)); - // Just in case these values aren't zero! - udp_links[i].rxsock = udp_links[i].txsock = INVALID_SOCKET; return i; } } return NOLINK; } -char *udp_format_remote (int32 link) -{ - // Format the remote address and port in the format "w.x.y.z:pppp" . It's - // a bit ugly (OK, it's a lot ugly!) but it's just for error messages... - static char buf[64]; - sprintf(buf, "%d.%d.%d.%d:%d", - (udp_links[link].ipremote >> 24) & 0xFF, - (udp_links[link].ipremote >> 16) & 0xFF, - (udp_links[link].ipremote >> 8) & 0xFF, - udp_links[link].ipremote & 0xFF, - udp_links[link].txport); - return buf; -} - -/* get_ipaddr IP address:port - - Inputs: - cptr = pointer to input string - Outputs: - ipa = pointer to IP address (may be NULL), 0 = none - ipp = pointer to IP port (may be NULL), 0 = none - result = status -*/ - -static t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp) -{ -char gbuf[CBUFSIZE]; -char *addrp, *portp, *octetp; -uint32 i, addr, port, octet; -t_stat r; - -if ((cptr == NULL) || (*cptr == 0)) - return SCPE_ARG; -strncpy (gbuf, cptr, CBUFSIZE); -addrp = gbuf; /* default addr */ -if ((portp = strchr (gbuf, ':'))) /* x:y? split */ - *portp++ = 0; -else if (strchr (gbuf, '.')) /* x.y...? */ - portp = NULL; -else { - portp = gbuf; /* port only */ - addrp = NULL; /* no addr */ - } -if (portp) { /* port string? */ - if (ipp == NULL) /* not wanted? */ - return SCPE_ARG; - port = (int32) get_uint (portp, 10, 65535, &r); - if ((r != SCPE_OK) || (port == 0)) - return SCPE_ARG; - } -else port = 0; -if (addrp) { /* addr string? */ - if (ipa == NULL) /* not wanted? */ - return SCPE_ARG; - for (i = addr = 0; i < 4; i++) { /* four octets */ - octetp = strchr (addrp, '.'); /* find octet end */ - if (octetp != NULL) /* split string */ - *octetp++ = 0; - else if (i < 3) /* except last */ - return SCPE_ARG; - octet = (int32) get_uint (addrp, 10, 255, &r); - if (r != SCPE_OK) - return SCPE_ARG; - addr = (addr << 8) | octet; - addrp = octetp; - } - if (((addr & 0377) == 0) || ((addr & 0377) == 255)) - return SCPE_ARG; - } -else addr = 0; -if (ipp) /* return req values */ - *ipp = port; -if (ipa) - *ipa = addr; -return SCPE_OK; -} - t_stat udp_parse_remote (int32 link, char *premote) { // This routine will parse a remote address string in any of these forms - @@ -356,102 +220,46 @@ t_stat udp_parse_remote (int32 link, char *premote) // yourself!! In both cases, "w.x.y.z" is a dotted IP for the remote machine // and "name.domain.com" is its name (which will be looked up to get the IP). // If the host name/IP is omitted then it defaults to "localhost". - char *end, *colon; int32 port; struct hostent *he; + char *end; int32 lport, rport; t_stat ret; + char host[64], port[16]; if (*premote == '\0') return SCPE_2FARG; - // Look for the local port number. If it's not there, set rxport to zero for now. - port = strtoul(premote, &end, 10); udp_links[link].rxport = 0; - if ((*end == ':') && (port > 0)) { - udp_links[link].rxport = port; premote = end+1; + memset (udp_links[link].lport, 0, sizeof(udp_links[link].lport)); + memset (udp_links[link].rhostport, 0, sizeof(udp_links[link].rhostport)); + // Handle the llll::rrrr case first + if (2 == sscanf (premote, "%d::%d", &lport, &rport)) { + if ((lport < 1) || (lport >65535) || (rport < 1) || (rport >65535)) return SCPE_ARG; + sprintf (udp_links[link].lport, "%d", lport); + sprintf (udp_links[link].rhostport, "localhost:%d", rport); + return SCPE_OK; } - // Look for "name:port" and extract the remote port... - if ((colon = strchr(premote, ':')) == NULL) return SCPE_ARG; - *colon++ = '\0'; port = strtoul(colon, &end, 10); - if ((*end != '\0') || (port == 0)) return SCPE_ARG; - udp_links[link].txport = port; - if (udp_links[link].rxport == 0) udp_links[link].rxport = port; - - // Now try to parse the host as a dotted IP address ... - if (get_ipaddr(premote, &udp_links[link].ipremote, NULL) == SCPE_OK) return SCPE_OK; - - // Special kludge - allow just ":port" to mean "localhost:port" ... - if(*premote == '\0') { - if (udp_links[link].rxport == udp_links[link].txport) - fprintf(stderr,"WARNING - use different transmit and receive ports!\n"); - premote = "localhost"; + // Look for the local port number and save it away. + lport = strtoul(premote, &end, 10); + if ((*end == ':') && (lport > 0)) { + sprintf (udp_links[link].lport, "%d", lport); + premote = end+1; } - // Not a dotted IP - try to lookup a host name ... - if ((he = gethostbyname(premote)) == NULL) return SCPE_OPENERR; - udp_links[link].ipremote = * (unsigned long *) he->h_addr_list[0]; - if (udp_links[link].ipremote == INADDR_NONE) { - fprintf(stderr,"WARNING - unable to resolve \"%s\"\n", premote); - return SCPE_OPENERR; - } - udp_links[link].ipremote = ntohl(udp_links[link].ipremote); + ret = sim_parse_addr (premote, host, sizeof(host), "localhost", port, sizeof(port), NULL, NULL); + if (ret != SCPE_OK) return SCPE_ARG; + sprintf (udp_links[link].rhostport, "%s:%s", host, port); + if (udp_links[link].lport[0] == '\0') + strcpy (udp_links[link].lport, port); + + if ((strcmp (udp_links[link].lport, port) == 0) && + (strcmp ("localhost", host) == 0)) + fprintf(stderr,"WARNING - use different transmit and receive ports!\n"); + return SCPE_OK; } -t_stat udp_socket_error (int32 link, const char *msg) +t_stat udp_error (int32 link, const char *msg) { // This routine is called whenever a SOCKET_ERROR is returned for any I/O. fprintf(stderr,"UDP%d - %s failed with error %d\n", link, msg, WSAGetLastError()); return SCPE_IOERR; } -t_stat udp_create_rx_socket (int32 link) -{ - // This routine will create the receiver socket for the virtual modem. - // Sockets are always UDP and, in the case of the receiver, bound to the port - // specified. Receiving sockets are also always set to NON BLOCKING mode. - int32 iret; uint32 flags = 1; - - // Creating the socket works on both Windows and Linux ... - udp_links[link].rxsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (udp_links[link].rxsock == INVALID_SOCKET) - return udp_socket_error(link, "RX socket()"); - udp_links[link].rxaddr.sin_family = AF_INET; - udp_links[link].rxaddr.sin_port = htons(udp_links[link].rxport); - udp_links[link].rxaddr.sin_addr.s_addr = htonl(INADDR_ANY); - iret = bind(udp_links[link].rxsock, (SOCKADDR *) &udp_links[link].rxaddr, sizeof(struct sockaddr_in)); - if (iret != 0) - return udp_socket_error(link, "bind()"); - - // But making it non-blocking is a problem ... -#ifdef _WIN32 - iret = ioctlsocket(udp_links[link].rxsock, FIONBIO, (u_long *) &flags); - if (iret != 0) - return udp_socket_error(link, "ioctlsocket()"); -#elif defined(__linux__) - flags = fcntl(udp_links[link].rxsock, F_GETFL, 0); - if (flags == -1) return udp_socket_error(link, "fcntl(F_GETFL)"); - iret = fcntl(udp_links[link].rxsock, F_SETFL, flags | O_NONBLOCK); - if (iret == -1) return udp_socket_error(link, "fcntl(F_SETFL)"); - iret = fcntl(udp_links[link].rxsock, F_SETOWN, getpid()); - if (iret == -1) return udp_socket_error(link, "fcntl(F_SETOWN)"); -#endif - return SCPE_OK; -} - -t_stat udp_create_tx_socket (int32 link) -{ - // This routine will create the transmitter socket for the virtual modem. - // In the case of the transmitter, we don't bind the socket at this time - - // WINSOCK will automatically bind it for us to a free port on the first IO. - // Also note that transmitting sockets are blocking; we don't have code (yet!) - // to allow them to be nonblocking. - udp_links[link].txsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (udp_links[link].txsock == INVALID_SOCKET) - return udp_socket_error(link, "TX socket()"); - - // Initialize the txaddr structure too - note that this isn't used now; it's - // the sockaddr we will use when we later do a sendto() the remote host! - udp_links[link].txaddr.sin_family = AF_INET; - udp_links[link].txaddr.sin_port = htons(udp_links[link].txport); - udp_links[link].txaddr.sin_addr.s_addr = htonl(udp_links[link].ipremote); - return SCPE_OK; -} - t_stat udp_create (DEVICE *dptr, char *premote, int32 *pln) { // Create a logical UDP link to the specified remote system. The "remote" @@ -467,22 +275,26 @@ t_stat udp_create (DEVICE *dptr, char *premote, int32 *pln) // which is a handle used to identify this connection to all future udp_xyz() // calls. t_stat ret; + char linkinfo[128]; int32 link = udp_find_free_link(); if (link < 0) return SCPE_MEM; - // Make sure WINSOCK is initialized ... - if ((ret = udp_startup(dptr)) != SCPE_OK) return ret; - // Parse the remote name and set up the ipaddr and port ... if ((ret = udp_parse_remote(link, premote)) != SCPE_OK) return ret; - // Create the sockets for the transmitter and receiver ... - if ((ret = udp_create_rx_socket(link)) != SCPE_OK) return ret; - if ((ret = udp_create_tx_socket(link)) != SCPE_OK) return ret; + // Create the socket connection to the destination ... + sprintf(linkinfo, "Line=%d,%s,UDP,Connect=%s", link, udp_links[link].lport, udp_links[link].rhostport); + ret = tmxr_open_master (&udp_tmxr, linkinfo); + if (ret != SCPE_OK) return ret; // All done - mark the TCP_LINK data as "used" and return the index. udp_links[link].used = TRUE; *pln = link; - sim_debug(IMP_DBG_UDP, dptr, "link %d - listening on port %d and sending to %s\n", link, udp_links[link].rxport, udp_format_remote(link)); + udp_lines[link].dptr = udp_links[link].dptr = dptr; // save device + udp_tmxr.uptr = dptr->units; + udp_tmxr.last_poll_time = 1; // h316'a use of TMXR doesn't poll periodically for connects + tmxr_poll_conn (&udp_tmxr); // force connection initialization now + udp_tmxr.last_poll_time = 1; // h316'a use of TMXR doesn't poll periodically for connects + sim_debug(IMP_DBG_UDP, dptr, "link %d - listening on port %s and sending to %s\n", link, udp_links[link].lport, udp_links[link].rhostport); return SCPE_OK; } @@ -491,26 +303,18 @@ t_stat udp_release (DEVICE *dptr, int32 link) // Close a link that was created by udp_create() and release any resources // allocated to it. We always return SCPE_OK unless the link specified is // already unused. - int32 iret, i; if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR; if (!udp_links[link].used) return SCPE_IERR; + if (dptr != udp_links[link].dptr) return SCPE_IERR; - // Close the sockets associated with this connection - that's easy ... - iret = closesocket(udp_links[link].rxsock); - if (iret != 0) udp_socket_error(link, "closesocket()"); - iret = closesocket(udp_links[link].txsock); - if (iret != 0) udp_socket_error(link, "closesocket()"); + tmxr_detach_ln (&udp_lines[link]); udp_links[link].used = FALSE; sim_debug(IMP_DBG_UDP, dptr, "link %d - closed\n", link); - // If we just closed the last link, then call udp_shutdown() ... - for (i = 0; i < MAXLINKS; ++i) { - if (udp_links[i].used) return SCPE_OK; - } - return udp_shutdown(dptr); + return SCPE_OK; } -t_stat udp_send_to (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count, SOCKADDR *pdest) +t_stat udp_send (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count) { // This routine does all the work of sending an IMP data packet. pdata // is a pointer (usually into H316 simulated memory) to the IMP packet data, @@ -520,10 +324,11 @@ t_stat udp_send_to (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count, SOCKA // doesn't necessarily need to have the same endian-ness as this machine. // Second, notice that transmitting sockets are NOT set to non blocking so // this routine might wait, but we assume the wait will never be too long. - UDP_PACKET pkt; int pktlen; uint16 i; int32 iret; + UDP_PACKET pkt; int pktlen; uint16 i; t_stat iret; if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR; if (!udp_links[link].used) return SCPE_IERR; if ((pdata == NULL) || (count == 0) || (count > MAXDATA)) return SCPE_IERR; + if (dptr != udp_links[link].dptr) return SCPE_IERR; // Build the UDP packet, filling in our own header information and copying // the H316 words from memory. REMEMBER THAT EVERYTHING IS IN NETWORK ORDER! @@ -534,28 +339,19 @@ t_stat udp_send_to (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count, SOCKA pktlen = UDP_HEADER_LEN + count*sizeof(uint16); // Send it and we're outta here ... - iret = sendto(udp_links[link].txsock, (const char *) &pkt, pktlen, 0, pdest, sizeof (struct sockaddr_in)); - if (iret == SOCKET_ERROR) return udp_socket_error(link, "sendto()"); + iret = tmxr_put_packet_ln (&udp_lines[link], (const uint8 *)&pkt, (size_t)pktlen); + if (iret != SCPE_OK) return udp_error(link, "tmxr_put_packet_ln()"); sim_debug(IMP_DBG_UDP, dptr, "link %d - packet sent (sequence=%d, length=%d)\n", link, ntohl(pkt.sequence), ntohs(pkt.count)); return SCPE_OK; } -t_stat udp_send (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count) +t_stat udp_set_link_loopback (DEVICE *dptr, int32 link, t_bool enable_loopback) { - // Send an IMP packet to the remote simh. This is the usual case - the only - // reason there's any other options at all is so we can emulate loopback. - return udp_send_to (dptr, link, pdata, count, (SOCKADDR *) &(udp_links[link].txaddr)); -} + if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR; + if (!udp_links[link].used) return SCPE_IERR; + if (dptr != udp_links[link].dptr) return SCPE_IERR; -t_stat udp_send_self (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count) -{ - // Send an IMP packet to our own receiving socket. This might seem silly, - // but it's used to emulate the line loopback function... - struct sockaddr_in self; - self.sin_family = AF_INET; - self.sin_port = htons(udp_links[link].rxport); - self.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - return udp_send_to (dptr, link, pdata, count, (SOCKADDR *) &self); + return tmxr_set_line_loopback (&udp_lines[link], enable_loopback); } int32 udp_receive_packet (int32 link, UDP_PACKET *ppkt) @@ -568,27 +364,22 @@ int32 udp_receive_packet (int32 link, UDP_PACKET *ppkt) // Note that this routine only receives the packet - it doesn't handle any // of the checking for valid packets, unexpected packets, duplicate or out of // sequence packets. That's strictly the caller's problem! - int32 pktsiz; - struct sockaddr_in sender; -#if defined (macintosh) || defined (__linux) || defined (__linux__) || \ - defined (__APPLE__) || defined (__OpenBSD__) || \ - defined(__NetBSD__) || defined(__FreeBSD__) || \ - (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) -socklen_t sndsiz = (socklen_t)sizeof(sender); -#elif defined (_WIN32) || defined (__EMX__) || \ - (defined (__ALPHA) && defined (__unix__)) || \ - defined (__hpux) -int sndsiz = (int)sizeof(sender); -#else -size_t sndsiz = sizeof(sender); -#endif + size_t pktsiz; + uint8 *pbuf; + t_stat ret; - pktsiz = recvfrom(udp_links[link].rxsock, (char *) ppkt, sizeof(UDP_PACKET), - 0, (SOCKADDR *) &sender, &sndsiz); - if (pktsiz >= 0) return pktsiz; - if (WSAGetLastError() == WSAEWOULDBLOCK) return 0; - udp_socket_error(link, "recvfrom()"); - return NOLINK; + udp_lines[link].rcve = TRUE; // Enable receiver + tmxr_poll_rx (&udp_tmxr); + ret = tmxr_get_packet_ln (&udp_lines[link], &pbuf, &pktsiz); + udp_lines[link].rcve = FALSE; // Disable receiver + if (ret != SCPE_OK) { + udp_error(link, "tmxr_get_packet_ln()"); + return NOLINK; + } + if (pbuf == NULL) return 0; + // Got a packet, so copy it to the packet buffer + memcpy (ppkt, pbuf, pktsiz); + return pktsiz; } int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbuf) @@ -611,6 +402,7 @@ int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbuf) UDP_PACKET pkt; int32 pktlen, explen, implen, i; uint32 magic, pktseq; if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR; if (!udp_links[link].used) return SCPE_IERR; + if (dptr != udp_links[link].dptr) return SCPE_IERR; while ((pktlen = udp_receive_packet(link, &pkt)) > 0) { // First do some header checks for a valid UDP packet ... diff --git a/sim_sock.c b/sim_sock.c index aa5fc102..596f1bb8 100644 --- a/sim_sock.c +++ b/sim_sock.c @@ -599,7 +599,7 @@ if (hostp != NULL) { hostp[strlen(hostp)-1] = '\0'; } } -if (host) /* host wanted? */ +if (host) { /* host wanted? */ if (hostp != NULL) { if (strlen(hostp) >= host_len) return SCPE_ARG; /* no room */ @@ -613,12 +613,14 @@ if (host) /* host wanted? */ strcpy (host, default_host); } else { - if (default_host) + if (default_host) { if (strlen(default_host) >= host_len) return SCPE_ARG; /* no room */ else strcpy (host, default_host); + } } + } if (validate_addr) { struct addrinfo *ai_host, *ai_validate, *ai; t_stat status; diff --git a/sim_tmxr.c b/sim_tmxr.c index 025093b5..eb5e194b 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -664,7 +664,10 @@ else { /* Telnet connection */ written = sim_write_sock (lp->sock, &(lp->txb[i]), length); if (written == SOCKET_ERROR) /* did an error occur? */ - return -1; /* return error indication */ + if (lp->datagram) + return written; /* ignore errors on datagram sockets */ + else + return -1; /* return error indication */ else return written; } @@ -1543,19 +1546,19 @@ return val; NULL, but success (SCPE_OK) is returned */ -t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize) +t_stat tmxr_get_packet_ln (TMLN *lp, uint8 **pbuf, size_t *psize) { return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0); } -t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte) +t_stat tmxr_get_packet_ln_ex (TMLN *lp, uint8 **pbuf, size_t *psize, uint8 frame_byte) { int32 c; size_t pktsize; size_t fc_size = (frame_byte ? 1 : 0); while (TMXR_VALID & (c = tmxr_getc_ln (lp))) { - if (lp->rxpboffset + 1 > lp->rxpbsize) { + if (lp->rxpboffset + 3 > lp->rxpbsize) { lp->rxpbsize += 512; lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize); } @@ -1563,8 +1566,15 @@ while (TMXR_VALID & (c = tmxr_getc_ln (lp))) { tmxr_debug (TMXR_DBG_PRCV, lp, "Received Unexpected Framing Byte", (char *)&lp->rxpb[lp->rxpboffset], 1); continue; } - lp->rxpb[lp->rxpboffset] = c & 0xFF; - lp->rxpboffset += 1; + if ((lp->datagram) && (lp->rxpboffset == fc_size)) { + /* Datagram packet length is provided as a part of the natural datagram + delivery, for TCP lines, we read the packet length from the data stream. + So, here we stuff packet size into head of packet buffer so it looks like + it was delivered by TCP and the below return logic doesn't have to worry */ + lp->rxpb[lp->rxpboffset++] = (uint8)(((1 + lp->rxbpi - lp->rxbpr) >> 8) & 0xFF); + lp->rxpb[lp->rxpboffset++] = (uint8)((1 + lp->rxbpi - lp->rxbpr) & 0xFF); + } + lp->rxpb[lp->rxpboffset++] = c & 0xFF; if (lp->rxpboffset >= (2 + fc_size)) { pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size]; if (pktsize == (lp->rxpboffset - 2)) { @@ -1606,15 +1616,17 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ nbytes = 0; if (lp->rxbpi == 0) /* need input? */ nbytes = tmxr_read (lp, /* yes, read */ - lp->rxbsz - TMXR_GUARD); /* leave spc for Telnet cruft */ + lp->rxbsz - TMXR_GUARD); /* leave spc for Telnet cruft */ else if (lp->tsta) /* in Telnet seq? */ nbytes = tmxr_read (lp, /* yes, read to end */ lp->rxbsz - lp->rxbpi); if (nbytes < 0) { /* line error? */ - if (!lp->txbfd || lp->notelnet) - lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */ - tmxr_close_ln (lp); /* disconnect line */ + if (!lp->datagram) { /* ignore errors reading UDP sockets */ + if (!lp->txbfd || lp->notelnet) + lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */ + tmxr_close_ln (lp); /* disconnect line */ + } } else if (nbytes > 0) { /* if data rcvd */ @@ -1816,6 +1828,7 @@ t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 fra { t_stat r; size_t fc_size = (frame_byte ? 1 : 0); +size_t pktlen_size = (lp->datagram ? 0 : 2); if (!lp->conn) return SCPE_LOST; @@ -1823,17 +1836,19 @@ if (lp->txppoffset < lp->txppsize) { tmxr_debug (TMXR_DBG_PXMT, lp, "Skipped Sending Packet - Transmit Busy", (char *)&lp->txpb[3], size); return SCPE_STALL; } -if (lp->txpbsize < size + 2 + fc_size) { - lp->txpbsize = size + 2 + fc_size; +if (lp->txpbsize < size + pktlen_size + fc_size) { + lp->txpbsize = size + pktlen_size + fc_size; lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize); } lp->txpb[0] = frame_byte; -lp->txpb[0+fc_size] = (size >> 8) & 0xFF; -lp->txpb[1+fc_size] = size & 0xFF; -memcpy (lp->txpb + 2 + fc_size, buf, size); -lp->txppsize = size + 2 + fc_size; +if (!lp->datagram) { + lp->txpb[0+fc_size] = (size >> 8) & 0xFF; + lp->txpb[1+fc_size] = size & 0xFF; + } +memcpy (lp->txpb + pktlen_size + fc_size, buf, size); +lp->txppsize = size + pktlen_size + fc_size; lp->txppoffset = 0; -tmxr_debug (TMXR_DBG_PXMT, lp, "Sending Packet", (char *)&lp->txpb[2+fc_size], size); +tmxr_debug (TMXR_DBG_PXMT, lp, "Sending Packet", (char *)&lp->txpb[pktlen_size+fc_size], size); ++lp->txpcnt; while ((lp->txppoffset < lp->txppsize) && (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset])))) @@ -1896,7 +1911,6 @@ if (nbytes) { /* >0? write */ sbytes = tmxr_write (lp, nbytes); /* write all data */ else sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);/* write to end buf */ - if (sbytes >= 0) { /* ok? */ tmxr_debug (TMXR_DBG_XMT, lp, "Sent", &(lp->txb[lp->txbpr]), sbytes); lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ diff --git a/sim_tmxr.h b/sim_tmxr.h index 637462c4..32b8ac33 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -185,8 +185,8 @@ int32 tmxr_poll_conn (TMXR *mp); t_stat tmxr_reset_ln (TMLN *lp); t_stat tmxr_detach_ln (TMLN *lp); int32 tmxr_getc_ln (TMLN *lp); -t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize); -t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte); +t_stat tmxr_get_packet_ln (TMLN *lp, uint8 **pbuf, size_t *psize); +t_stat tmxr_get_packet_ln_ex (TMLN *lp, uint8 **pbuf, size_t *psize, uint8 frame_byte); void tmxr_poll_rx (TMXR *mp); t_stat tmxr_putc_ln (TMLN *lp, int32 chr); t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size);