From 5052eb307a959644677037be52b9a168a68775dd Mon Sep 17 00:00:00 2001 From: tedgreen99 Date: Thu, 12 May 2022 21:10:35 -0600 Subject: bitbanger: Add udp multicast support (#9744) Example usage: -bitb udp.234.99.99.99:15122 --- src/osd/modules/file/posixfile.cpp | 14 ++- src/osd/modules/file/posixfile.h | 5 +- src/osd/modules/file/posixsocket.cpp | 150 +++++++++++++++++++++++++++++++-- src/osd/modules/file/winfile.cpp | 15 +++- src/osd/modules/file/winfile.h | 5 +- src/osd/modules/file/winsocket.cpp | 159 +++++++++++++++++++++++++++++++++-- src/osd/osdfile.h | 13 +++ 7 files changed, 342 insertions(+), 19 deletions(-) (limited to 'src/osd') diff --git a/src/osd/modules/file/posixfile.cpp b/src/osd/modules/file/posixfile.cpp index 580288c7d10..69b7c60d4a0 100644 --- a/src/osd/modules/file/posixfile.cpp +++ b/src/osd/modules/file/posixfile.cpp @@ -226,7 +226,9 @@ std::error_condition osd_file::open(std::string const &path, std::uint32_t openf { std::string dst; if (posix_check_socket_path(path)) - return posix_open_socket(path, openflags, file, filesize); + return posix_open_tcp_socket(path, openflags, file, filesize); + else if (posix_check_udp_path(path)) + return posix_open_udp_socket(path, openflags, file, filesize); else if (posix_check_ptty_path(path)) return posix_open_ptty(openflags, file, filesize, dst); else if (posix_check_domain_path(path)) @@ -342,6 +344,16 @@ std::error_condition osd_file::openpty(ptr &file, std::string &name) noexcept } +//============================================================ +// osd_open_udp_socket +//============================================================ + +std::error_condition osd_file::open_udp_socket(std::string const &path, uint32_t openflags, ptr &file, std::uint64_t &filesize) noexcept +{ + return posix_open_udp_socket(path, openflags, file, filesize); +} + + //============================================================ // osd_file::remove //============================================================ diff --git a/src/osd/modules/file/posixfile.h b/src/osd/modules/file/posixfile.h index eb6f010859e..2b0358dc396 100644 --- a/src/osd/modules/file/posixfile.h +++ b/src/osd/modules/file/posixfile.h @@ -24,7 +24,10 @@ //============================================================ bool posix_check_socket_path(std::string const &path) noexcept; -std::error_condition posix_open_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; +std::error_condition posix_open_tcp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; + +bool posix_check_udp_path(std::string const &path) noexcept; +std::error_condition posix_open_udp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; bool posix_check_domain_path(std::string const &path) noexcept; std::error_condition posix_open_domain(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; diff --git a/src/osd/modules/file/posixsocket.cpp b/src/osd/modules/file/posixsocket.cpp index 6a23e747ea1..f2f9c7a6b93 100644 --- a/src/osd/modules/file/posixsocket.cpp +++ b/src/osd/modules/file/posixsocket.cpp @@ -28,10 +28,13 @@ #include +#define DEBUG_SOCKET (0) + namespace { char const *const posixfile_socket_identifier = "socket."; char const *const posixfile_domain_identifier = "domain."; +char const *const posixfile_udp_identifier = "udp."; class posix_osd_socket : public osd_file @@ -42,9 +45,11 @@ public: posix_osd_socket& operator=(posix_osd_socket const &) = delete; posix_osd_socket& operator=(posix_osd_socket &&) = delete; - posix_osd_socket(int sock, bool listening) noexcept + posix_osd_socket(int sock, bool listening, uint32_t dest = 0, uint32_t dport = 0) noexcept : m_sock(sock) , m_listening(listening) + , m_dest(dest) + , m_dport(dport) { assert(m_sock >= 0); } @@ -112,11 +117,26 @@ public: virtual std::error_condition write(void const *buffer, std::uint64_t offset, std::uint32_t count, std::uint32_t &actual) noexcept override { - ssize_t const result = ::write(m_sock, buffer, count); - if (result < 0) - return std::error_condition(errno, std::generic_category()); + if (!m_dest) { + ssize_t const result = ::write(m_sock, buffer, count); + if (result < 0) + return std::error_condition(errno, std::generic_category()); - actual = std::uint32_t(size_t(result)); + actual = std::uint32_t(size_t(result)); + } + else + { + sockaddr_in dest; + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = m_dest; + dest.sin_port = htons(m_dport); + //printf("Sending to %d bytes %s port %d\n", count, inet_ntoa(dest.sin_addr), m_dport); + ssize_t const result = ::sendto(m_sock, reinterpret_cast(buffer), count, 0, reinterpret_cast (&dest), sizeof(dest)); + if (result < 0) + return std::error_condition(errno, std::generic_category()); + actual = std::uint32_t(size_t(result)); + + } return std::error_condition(); } @@ -135,6 +155,8 @@ public: private: int m_sock; bool m_listening; + uint32_t m_dest; + uint32_t m_dport; }; @@ -209,8 +231,19 @@ bool posix_check_domain_path(std::string const &path) noexcept } -std::error_condition posix_open_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept +bool posix_check_udp_path(std::string const &path) noexcept { + if (strncmp(path.c_str(), posixfile_udp_identifier, strlen(posixfile_udp_identifier)) == 0 && + strchr(path.c_str(), ':') != nullptr) return true; + return false; +} + + +std::error_condition posix_open_tcp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept +{ + if (DEBUG_SOCKET) + printf("Trying to open tcp socket %s\n", path.c_str()); + char hostname[256]; int port; std::sscanf(&path[strlen(posixfile_socket_identifier)], "%255[^:]:%d", hostname, &port); @@ -262,3 +295,108 @@ std::error_condition posix_open_domain(std::string const &path, std::uint32_t op return create_socket(sau, sock, openflags, file, filesize); } + +std::error_condition posix_open_udp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept +{ + if (DEBUG_SOCKET) + printf("Trying to open udp socket %s\n", path.c_str()); + + char hostname[256]; + int port; + std::sscanf(&path[strlen(posixfile_udp_identifier)], "%255[^:]:%d", hostname, &port); + + struct hostent const* const localhost = ::gethostbyname(hostname); + if (!localhost) + return std::errc::no_such_file_or_directory; + + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = htons(port); + sai.sin_addr = *reinterpret_cast(localhost->h_addr); + + int const sock = ::socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + { + if (DEBUG_SOCKET) + printf("socket() error\n"); + return std::error_condition(errno, std::generic_category()); + } + + int const flag = 1; + if (::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&flag), sizeof(flag)) < 0) + { + if (DEBUG_SOCKET) + printf("SO_REUSEADDR error\n"); + ::close(sock); + return std::error_condition(errno, std::generic_category()); + } + + // local socket support + osd_file::ptr result; + if (openflags & OPEN_FLAG_CREATE) + { + sai.sin_addr.s_addr = htonl(INADDR_ANY); + if (DEBUG_SOCKET) + printf("Listening on '%s' on port '%d'\n", hostname, port); + + // bind socket... + if (::bind(sock, reinterpret_cast(&sai), sizeof(struct sockaddr)) < 0) + { + printf("bind error\n"); + std::error_condition sockbinderr(errno, std::generic_category()); + ::close(sock); + return sockbinderr; + } + + // Setup multicast + struct ip_mreq mreq; + mreq.imr_multiaddr = *reinterpret_cast(localhost->h_addr); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (DEBUG_SOCKET) + printf("imr_multaddr: %08x\n", mreq.imr_multiaddr.s_addr); + if ((mreq.imr_multiaddr.s_addr & 0xff) >= 224 && (mreq.imr_multiaddr.s_addr & 0xff) <= 239) + { + if (DEBUG_SOCKET) + printf("Setting up udp multicast %s\n", hostname); + if (::setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)) < 0) { + if (DEBUG_SOCKET) + printf("IP_ADD_MEMBERSHIP error\n"); + std::error_condition membererr(errno, std::generic_category()); + ::close(sock); + return membererr; + } + } + + // Disable loopback on multicast + char const loop = 0; + if (::setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop))) + { + if (DEBUG_SOCKET) + printf("IP_MULTICAST_LOOP error\n"); + std::error_condition multierr(errno, std::generic_category()); + ::close(sock); + return multierr; + } + + // Set ttl on multicast + char const ttl = 15; + if (::setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) + { + if (DEBUG_SOCKET) + printf("IP_MULTICAST_TTL error\n"); + std::error_condition multierr(errno, std::generic_category()); + ::close(sock); + return multierr; + } + result.reset(new (std::nothrow) posix_osd_socket(sock, false, (*reinterpret_cast(localhost->h_addr)).s_addr, port)); + } + if (!result) + { + ::close(sock); + return std::errc::permission_denied; + } + file = std::move(result); + filesize = 0; + return std::error_condition(); +} diff --git a/src/osd/modules/file/winfile.cpp b/src/osd/modules/file/winfile.cpp index be4fa6f70c3..ff8146a3185 100644 --- a/src/osd/modules/file/winfile.cpp +++ b/src/osd/modules/file/winfile.cpp @@ -168,9 +168,11 @@ std::error_condition osd_file::open(std::string const &orig_path, uint32_t openf catch (...) { return std::errc::not_enough_memory; } if (win_check_socket_path(path)) - return win_open_socket(path, openflags, file, filesize); + return win_open_tcp_socket(path, openflags, file, filesize); else if (win_check_ptty_path(path)) return win_open_ptty(path, openflags, file, filesize); + else if (win_check_udp_path(path)) + return win_open_udp_socket(path, openflags, file, filesize); // convert path to TCHAR osd::text::tstring t_path; @@ -287,6 +289,17 @@ std::error_condition osd_file::openpty(ptr &file, std::string &name) noexcept +//============================================================ +// osd_open_udp_socket +//============================================================ + +std::error_condition open_udp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept +{ + return win_open_udp_socket(path, openflags, file, filesize); +} + + + //============================================================ // osd_rmfile //============================================================ diff --git a/src/osd/modules/file/winfile.h b/src/osd/modules/file/winfile.h index 83f0814733d..0b332b314ad 100644 --- a/src/osd/modules/file/winfile.h +++ b/src/osd/modules/file/winfile.h @@ -17,6 +17,7 @@ #include #include +#include //============================================================ @@ -27,7 +28,9 @@ bool win_init_sockets() noexcept; void win_cleanup_sockets() noexcept; bool win_check_socket_path(std::string const &path) noexcept; -std::error_condition win_open_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; +std::error_condition win_open_tcp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; +bool win_check_udp_path(std::string const &path) noexcept; +std::error_condition win_open_udp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; bool win_check_ptty_path(std::string const &path) noexcept; std::error_condition win_open_ptty(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; diff --git a/src/osd/modules/file/winsocket.cpp b/src/osd/modules/file/winsocket.cpp index d04538963ea..ff5977ed44d 100644 --- a/src/osd/modules/file/winsocket.cpp +++ b/src/osd/modules/file/winsocket.cpp @@ -25,10 +25,12 @@ #include #include +#define DEBUG_SOCKET (0) namespace { char const *const winfile_socket_identifier = "socket."; +char const *const winfile_udp_identifier = "udp."; class win_osd_socket : public osd_file @@ -39,9 +41,11 @@ public: win_osd_socket& operator=(win_osd_socket const &) = delete; win_osd_socket& operator=(win_osd_socket &&) = delete; - win_osd_socket(SOCKET s, bool l) noexcept + win_osd_socket(SOCKET s, bool l, uint32_t dest = 0, uint32_t dport = 0) noexcept : m_socket(s) , m_listening(l) + , m_dest(dest) + , m_dport(dport) { assert(INVALID_SOCKET != m_socket); } @@ -109,11 +113,24 @@ public: virtual std::error_condition write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) noexcept override { - auto const result = send(m_socket, reinterpret_cast(buffer), length, 0); - if (result < 0) - return wsa_error_to_file_error(WSAGetLastError()); - - actual = result; + if (!m_dest) { + auto const result = send(m_socket, reinterpret_cast(buffer), length, 0); + if (result < 0) + return wsa_error_to_file_error(WSAGetLastError()); + actual = result; + } + else + { + sockaddr_in dest; + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = m_dest; + dest.sin_port = htons(m_dport); + //printf("Sending to %d bytes %s port %d\n", length, inet_ntoa(dest.sin_addr), m_dport); + auto const result = sendto(m_socket, reinterpret_cast(buffer), length, 0, reinterpret_cast (&dest), sizeof(dest)); + if (result < 0) + return wsa_error_to_file_error(WSAGetLastError()); + actual = result; + } return std::error_condition(); } @@ -177,6 +194,8 @@ public: private: SOCKET m_socket; bool m_listening; + uint32_t m_dest; + uint32_t m_dport; }; } // anonymous namespace @@ -222,8 +241,11 @@ bool win_check_socket_path(std::string const &path) noexcept } -std::error_condition win_open_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept +std::error_condition win_open_tcp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept { + if (DEBUG_SOCKET) + printf("Trying to open tcp socket %s\n", path.c_str()); + char hostname[256]; int port; std::sscanf(&path[strlen(winfile_socket_identifier)], "%255[^:]:%d", hostname, &port); @@ -254,10 +276,13 @@ std::error_condition win_open_socket(std::string const &path, std::uint32_t open osd_file::ptr result; if (openflags & OPEN_FLAG_CREATE) { - //printf("Listening for client at '%s' on port '%d'\n", hostname, port); + if (DEBUG_SOCKET) + printf("Listening for client at '%s' on port '%d'\n", hostname, port); // bind socket... if (bind(sock, reinterpret_cast(&sai), sizeof(sai)) == SOCKET_ERROR) { + if (DEBUG_SOCKET) + printf("bind error\n"); int const err = WSAGetLastError(); closesocket(sock); return win_osd_socket::wsa_error_to_file_error(err); @@ -266,6 +291,8 @@ std::error_condition win_open_socket(std::string const &path, std::uint32_t open // start to listen... if (listen(sock, 1) == SOCKET_ERROR) { + if (DEBUG_SOCKET) + printf("listen error\n"); int const err = WSAGetLastError(); closesocket(sock); return win_osd_socket::wsa_error_to_file_error(err); @@ -276,9 +303,12 @@ std::error_condition win_open_socket(std::string const &path, std::uint32_t open } else { - //printf("Connecting to server '%s' on port '%d'\n", hostname, port); + if (DEBUG_SOCKET) + printf("Connecting to server '%s' on port '%d'\n", hostname, port); if (connect(sock, reinterpret_cast(&sai), sizeof(sai)) == SOCKET_ERROR) { + if (DEBUG_SOCKET) + printf("connect error\n"); int const err = WSAGetLastError(); closesocket(sock); return win_osd_socket::wsa_error_to_file_error(err); @@ -295,3 +325,114 @@ std::error_condition win_open_socket(std::string const &path, std::uint32_t open filesize = 0; return std::error_condition(); } + +bool win_check_udp_path(std::string const &path) noexcept +{ + if (strncmp(path.c_str(), winfile_udp_identifier, strlen(winfile_udp_identifier)) == 0 && + strchr(path.c_str(), ':') != nullptr) return true; + return false; +} + + +std::error_condition win_open_udp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept +{ + if (DEBUG_SOCKET) + printf("Trying to open udp socket %s\n", path.c_str()); + char hostname[256]; + int port; + std::sscanf(&path[strlen(winfile_udp_identifier)], "%255[^:]:%d", hostname, &port); + //std::sscanf(path.c_str(), "%255[^:]:%d", hostname, &port); + + struct hostent const* const localhost = gethostbyname(hostname); + if (!localhost) + return std::errc::no_such_file_or_directory; + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = htons(port); + sai.sin_addr = *reinterpret_cast(localhost->h_addr); + SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); + if (INVALID_SOCKET == sock) + return win_osd_socket::wsa_error_to_file_error(WSAGetLastError()); + + int const flag = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&flag), sizeof(flag)) == SOCKET_ERROR) + { + if (DEBUG_SOCKET) + printf("SO_REUSEADDR error\n"); + int const err = WSAGetLastError(); + closesocket(sock); + return win_osd_socket::wsa_error_to_file_error(err); + } + + // local socket support + osd_file::ptr result; + if (openflags & OPEN_FLAG_CREATE) + { + sai.sin_addr.s_addr = htonl(INADDR_ANY); + if (DEBUG_SOCKET) + printf("Listening on '%s' on port '%d'\n", hostname, port); + // bind socket... + if (bind(sock, reinterpret_cast(&sai), sizeof(struct sockaddr)) == SOCKET_ERROR) + { + if (DEBUG_SOCKET) + printf("bind error\n"); + int const err = WSAGetLastError(); + closesocket(sock); + return win_osd_socket::wsa_error_to_file_error(err); + } + // Setup multicast + struct ip_mreq mreq; + mreq.imr_multiaddr = *reinterpret_cast(localhost->h_addr); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (mreq.imr_multiaddr.S_un.S_un_b.s_b1 >= 224 && mreq.imr_multiaddr.S_un.S_un_b.s_b1 <= 239) + { + if (DEBUG_SOCKET) + printf("Setting up udp multicast %s\n", hostname); + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)) == SOCKET_ERROR) { + if (DEBUG_SOCKET) + printf("IP_ADD_MEMBERSHIP error\n"); + int const err = WSAGetLastError(); + closesocket(sock); + return win_osd_socket::wsa_error_to_file_error(err); + } + } + // Disable loopback on multicast + char const loop = 0; + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop))) + { + if (DEBUG_SOCKET) + printf("IP_MULTICAST_LOOP error\n"); + int const err = WSAGetLastError(); + closesocket(sock); + return win_osd_socket::wsa_error_to_file_error(err); + } + // Set ttl on multicast + char const ttl = 15; + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) + { + if (DEBUG_SOCKET) + printf("IP_MULTICAST_TTL error\n"); + int const err = WSAGetLastError(); + closesocket(sock); + return win_osd_socket::wsa_error_to_file_error(err); + } + result.reset(new (std::nothrow) win_osd_socket(sock, false, (*reinterpret_cast(localhost->h_addr)).s_addr, port)); + if (!result) + { + if (DEBUG_SOCKET) + printf("connect error\n"); + int const err = WSAGetLastError(); + closesocket(sock); + return win_osd_socket::wsa_error_to_file_error(err); + } + } + if (!result) + { + closesocket(sock); + return std::errc::permission_denied; + } + file = std::move(result); + filesize = 0; + return std::error_condition(); +} diff --git a/src/osd/osdfile.h b/src/osd/osdfile.h index bc3efbde805..2c984c0e3a9 100644 --- a/src/osd/osdfile.h +++ b/src/osd/osdfile.h @@ -102,6 +102,19 @@ public: /// \return Result of the operation. static std::error_condition openpty(ptr &file, std::string &name) noexcept; + /// \brief Create a UDP socket + /// + /// \param [in] path Address of the socket to open. + /// \param [in] openflags Combination of #OPEN_FLAG_READ, + /// #OPEN_FLAG_WRITE specifying the requested access mode + /// and open behaviour. + /// \param [out] file Receives the file handle if the operation + /// succeeds. Not valid if the operation fails. + /// \param [out] filesize Will be zero if the operation succeeded. + /// Not valid if the operation failed. + /// \return Result of the operation. + static std::error_condition open_udp_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; + /// \brief Close an open file virtual ~osd_file() { } -- cgit v1.2.3