diff --git a/builds/cmake/Configure.cmake b/builds/cmake/Configure.cmake index 9faa0e50eb7..3327b0e3647 100644 --- a/builds/cmake/Configure.cmake +++ b/builds/cmake/Configure.cmake @@ -116,6 +116,7 @@ set(CASE_SENSITIVITY "true") set(SUPPORT_RAW_DEVICES 1) set(include_files_list + afunix.h aio.h assert.h atomic.h @@ -179,6 +180,7 @@ set(include_files_list sys/time.h sys/timeb.h sys/types.h + sys/un.h sys/uio.h sys/wait.h termio.h diff --git a/builds/install/misc/firebird.conf b/builds/install/misc/firebird.conf index 4f7daf736ac..59142044eb1 100644 --- a/builds/install/misc/firebird.conf +++ b/builds/install/misc/firebird.conf @@ -781,6 +781,21 @@ #RemoteServicePort = 3050 +# ---------------------------- +# Unix domain socket path to be used for client database and service +# connections on platforms with Unix domain socket support. If set, +# the server listens on this socket instead of the TCP service name/port. +# The name should not contain colons, except for a Windows drive letter. +# +# Event notification connections use a separate Unix domain socket in +# the same directory as this socket. Firebird chooses its name as +# fb_event... +# +# Type: string +# +#RemoteServiceUnixSocket = + + # ---------------------------- # The TCP port number to be used for server Event Notification # messages. The value of 0 (Zero) means that the server will choose diff --git a/configure.ac b/configure.ac index 90ed73c0e97..de2dd4df9df 100644 --- a/configure.ac +++ b/configure.ac @@ -893,7 +893,7 @@ AC_CHECK_HEADERS(iconv.h) AC_CHECK_HEADERS(linux/falloc.h) AC_CHECK_HEADERS(utime.h) -AC_CHECK_HEADERS(socket.h sys/socket.h sys/sockio.h winsock2.h) +AC_CHECK_HEADERS(afunix.h socket.h sys/socket.h sys/sockio.h sys/un.h winsock2.h) AC_CHECK_DECLS(SOCK_CLOEXEC,,,[[ #ifdef HAVE_SYS_SOCKET_H #include diff --git a/doc/Firebird_conf.txt b/doc/Firebird_conf.txt index 70d635e6342..89a24d888b9 100644 --- a/doc/Firebird_conf.txt +++ b/doc/Firebird_conf.txt @@ -142,6 +142,7 @@ DeadThreadsCollection integer default 50 PriorityBoost integer default 5 RemoteServiceName string default gds_db RemoteServicePort integer default 3050 (TCP port number) +RemoteServiceUnixSocket string default empty (Unix domain socket path) IpcName string default "FIREBIRD" (Windows only) MaxUnflushedWrites integer diff --git a/doc/README.connection_strings b/doc/README.connection_strings index 6bc1d1f3fcf..e3846a89d40 100644 --- a/doc/README.connection_strings +++ b/doc/README.connection_strings @@ -110,6 +110,13 @@ Examples: inet://C:\db\mydb.fdb inet://mydb + Local connection via Unix domain socket: + + unix:///run/firebird/firebird.sock:/db/mydb.fdb + unix:///run/firebird/firebird.sock:mydb + unix:///run/firebird/firebird.sock:service_mgr + unix://C:\firebird\firebird.sock:C:\db\mydb.fdb + Local connection via shared memory: xnet://C:\db\mydb.fdb @@ -135,7 +142,11 @@ to connect locally using a specific transport protocol, please specify: inet:// or + unix://: + or xnet:// Note: XNET (shared memory) protocol is available on Windows only. - +Unix domain socket protocol is available on platforms with AF_UNIX support. +The socket path must not contain ':' except for a Windows drive letter, as in +unix://C:\firebird\firebird.sock:C:\db\mydb.fdb. diff --git a/src/common/config/config.h b/src/common/config/config.h index 3db3227bac8..d2831ded6f3 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -136,6 +136,7 @@ enum ConfigKey KEY_DEADLOCK_TIMEOUT, KEY_REMOTE_SERVICE_NAME, KEY_REMOTE_SERVICE_PORT, + KEY_REMOTE_SERVICE_UNIX_SOCKET, KEY_IPC_NAME, KEY_MAX_UNFLUSHED_WRITES, KEY_MAX_UNFLUSHED_WRITE_TIME, @@ -229,6 +230,7 @@ inline constexpr ConfigEntry entries[MAX_CONFIG_KEY] = {TYPE_INTEGER, "DeadlockTimeout", false, 10}, // seconds {TYPE_STRING, "RemoteServiceName", false, FB_SERVICE_NAME}, {TYPE_INTEGER, "RemoteServicePort", false, 0}, + {TYPE_STRING, "RemoteServiceUnixSocket", true, nullptr}, {TYPE_STRING, "IpcName", false, FB_IPC_NAME}, #ifdef WIN_NT {TYPE_INTEGER, "MaxUnflushedWrites", false, 100}, @@ -521,6 +523,9 @@ class Config : public RefCounted, public GlobalStorage // Service port for INET CONFIG_GET_PER_DB_KEY(unsigned short, getRemoteServicePort, KEY_REMOTE_SERVICE_PORT, getInt); + // Unix domain socket for remote protocol + CONFIG_GET_GLOBAL_STR(getRemoteServiceUnixSocket, KEY_REMOTE_SERVICE_UNIX_SOCKET); + // Name for IPC-related objects CONFIG_GET_PER_DB_STR(getIpcName, KEY_IPC_NAME); diff --git a/src/include/gen/autoconfig.h.in b/src/include/gen/autoconfig.h.in index ccc3c22e212..1305a9f3971 100644 --- a/src/include/gen/autoconfig.h.in +++ b/src/include/gen/autoconfig.h.in @@ -330,6 +330,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SIGNAL_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_AFUNIX_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SOCKET_H 1 @@ -351,6 +354,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TYPES_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_UN_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_UIO_H 1 diff --git a/src/include/gen/autoconfig_msvc.h b/src/include/gen/autoconfig_msvc.h index 37207739681..c4230cf923f 100644 --- a/src/include/gen/autoconfig_msvc.h +++ b/src/include/gen/autoconfig_msvc.h @@ -92,6 +92,7 @@ #endif /* Headers */ +#define HAVE_AFUNIX_H #define HAVE_ASSERT_H #define HAVE_CTYPE_H #undef HAVE_UNISTD_H diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index 6c961feb63f..1394caf1236 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -79,10 +79,22 @@ #include #endif +#if !defined(WIN_NT) +#include +#ifdef HAVE_SYS_UN_H +#include +#endif +#endif + #ifdef WIN_NT +#include #include #endif +#if (defined(WIN_NT) && defined(HAVE_AFUNIX_H)) || defined(HAVE_SYS_UN_H) +#define HAVE_AF_UNIX_SUPPORT +#endif + #if defined(WIN_NT) #include "../common/isc_proto.h" #include "../remote/os/win32/xnet_proto.h" @@ -93,6 +105,10 @@ const char* const PROTOCOL_INET = "inet"; const char* const PROTOCOL_INET4 = "inet4"; const char* const PROTOCOL_INET6 = "inet6"; +#ifdef HAVE_AF_UNIX_SUPPORT +const char* const PROTOCOL_UNIX = "unix"; +#endif + #ifdef WIN_NT const char* const PROTOCOL_XNET = "xnet"; #endif @@ -1167,6 +1183,46 @@ static void authReceiveResponse(bool havePacket, ClntAuthBlock& authItr, rem_por static AtomicCounter remote_event_id; +#ifdef HAVE_AF_UNIX_SUPPORT +static bool analyzeUnixProtocol(PathName& expandedName, PathName& nodeName) +{ + nodeName.erase(); + + const PathName prefix = PathName(PROTOCOL_UNIX) + "://"; + + if (prefix.length() > expandedName.length()) + return false; + + if (IgnoreCaseComparator::compare(prefix.c_str(), expandedName.c_str(), prefix.length()) != 0) + return false; + + PathName savedName = expandedName; + expandedName.erase(0, prefix.length()); + + PathName::size_type separator = expandedName.find(':'); + +#ifdef WIN_NT + if (separator == 1) + { + const char driveLetter = expandedName[0]; + if ((driveLetter >= 'A' && driveLetter <= 'Z') || (driveLetter >= 'a' && driveLetter <= 'z')) + separator = expandedName.find(':', separator + 1); + } +#endif + + if (separator == PathName::npos || separator == 0 || separator == expandedName.length() - 1) + { + expandedName = savedName; + return false; + } + + nodeName = expandedName.substr(0, separator); + expandedName.erase(0, separator + 1); + + return true; +} +#endif + static constexpr unsigned ANALYZE_USER_VFY = 0x01; static constexpr unsigned ANALYZE_LOOPBACK = 0x02; static constexpr unsigned ANALYZE_MOUNTS = 0x04; @@ -7921,14 +7977,24 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned else #endif +#ifdef HAVE_AF_UNIX_SUPPORT + if (analyzeUnixProtocol(attach_name, node_name)) + { + ISC_utf8ToSystem(node_name); + port = INET_analyze(&cBlock, attach_name, node_name.c_str(), flags & ANALYZE_USER_VFY, pb, + cBlock.getConfig(), ref_db_name, cryptCb, AF_UNIX); + } + else +#endif + if (ISC_analyze_protocol(PROTOCOL_INET4, attach_name, node_name, INET_SEPARATOR, needFile)) inet_af = AF_INET; else if (ISC_analyze_protocol(PROTOCOL_INET6, attach_name, node_name, INET_SEPARATOR, needFile)) inet_af = AF_INET6; - if (inet_af != AF_UNSPEC || + if (!port && (inet_af != AF_UNSPEC || ISC_analyze_protocol(PROTOCOL_INET, attach_name, node_name, INET_SEPARATOR, needFile) || - ISC_analyze_tcp(attach_name, node_name, needFile)) + ISC_analyze_tcp(attach_name, node_name, needFile))) { if (node_name.isEmpty()) node_name = INET_LOCALHOST; diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index ae8be9fd84e..581c9930fdc 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include "../common/file_params.h" #include @@ -80,6 +81,10 @@ #ifdef WIN_NT #define FD_SETSIZE 2048 +#include +#include +#include +#include #endif #ifndef WIN_NT @@ -87,6 +92,10 @@ #include #include #include +#include +#ifdef HAVE_SYS_UN_H +#include +#endif #include #if defined(HAVE_POLL_H) @@ -97,6 +106,18 @@ #endif // !WIN_NT +#if (defined(WIN_NT) && defined(HAVE_AFUNIX_H)) || defined(HAVE_SYS_UN_H) +#define HAVE_AF_UNIX_SUPPORT +#endif + +#ifdef HAVE_AF_UNIX_SUPPORT +#if defined(WIN_NT) +using UnixSocketAddress = SOCKADDR_UN; +#else +using UnixSocketAddress = sockaddr_un; +#endif +#endif + constexpr int INET_RETRY_CALL = 5; #include "../remote/remote.h" @@ -521,22 +542,51 @@ static void disconnect(rem_port*); static void force_close(rem_port*); static int cleanup_ports(const int, const int, void*); +#ifdef HAVE_AF_UNIX_SUPPORT +static string makeAuxUnixSocketPath(const string& mainSocketPath); +static void makeUnixSocketAddress(bool releasePort, rem_port* port, const char* path, + UnixSocketAddress* address, socklen_t* length); +static void removeUnixSocketPath(const string& path); +static rem_port* unix_connect(rem_port* port, const TEXT* socketPath, PACKET* packet, USHORT flag); +static rem_port* unix_listener_socket(rem_port* port, USHORT flag, const TEXT* socketPath); +#endif + #ifdef NO_FORK static int fork(); #endif +namespace +{ + struct ForkSocket + { + ForkSocket() + : socket(0), unixSocket(false) + { + } + + ForkSocket(SOCKET aSocket, bool aUnixSocket) + : socket(aSocket), unixSocket(aUnixSocket) + { + } + + SOCKET socket; + bool unixSocket; + }; +} + typedef Array SocketsArray; +typedef Array ForkSocketsArray; #ifdef WIN_NT static int wsaExitHandler(const int, const int, void*); -static int fork(SOCKET, USHORT); +static int fork(SOCKET, USHORT, bool); static THREAD_ENTRY_DECLARE forkThread(THREAD_ENTRY_PARAM); static GlobalPtr forkMutex; static HANDLE forkEvent = INVALID_HANDLE_VALUE; static bool forkThreadStarted = false; -static SocketsArray* forkSockets; +static ForkSocketsArray* forkSockets; #endif static void get_peer_info(rem_port*); @@ -624,6 +674,89 @@ static GlobalPtr port_mutex; static GlobalPtr inet_ports; static GlobalPtr ports_to_close; +#ifdef HAVE_AF_UNIX_SUPPORT +static AtomicCounter unixAuxSequence; + +static void makeUnixSocketAddress(bool releasePort, rem_port* port, const char* path, + UnixSocketAddress* address, socklen_t* length) +{ + const size_t pathLength = path ? strlen(path) : 0; + + if (!pathLength || pathLength >= sizeof(address->sun_path)) + { + gds__log("INET/unix: invalid Unix socket path length: %s", path ? path : ""); + inet_gen_error(releasePort, port, + Arg::Gds(isc_net_lookup_err) << Arg::Gds(isc_host_unknown)); + } + + memset(address, 0, sizeof(*address)); + address->sun_family = AF_UNIX; + memcpy(address->sun_path, path, pathLength + 1); + *length = offsetof(UnixSocketAddress, sun_path) + pathLength + 1; +} + +#ifdef WIN_NT +static bool isUnixSocketPath(const string& path) +{ + const HANDLE handle = CreateFileA(path.c_str(), FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr); + + if (handle == INVALID_HANDLE_VALUE) + return false; + + FILE_ATTRIBUTE_TAG_INFO info; + const bool result = GetFileInformationByHandleEx(handle, FileAttributeTagInfo, + &info, sizeof(info)) && + (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + info.ReparseTag == IO_REPARSE_TAG_AF_UNIX; + + CloseHandle(handle); + + return result; +} +#endif + +static void removeUnixSocketPath(const string& path) +{ + if (path.isEmpty()) + return; + +#ifdef WIN_NT + if (isUnixSocketPath(path)) + DeleteFileA(path.c_str()); +#else + struct stat st; + if (lstat(path.c_str(), &st) == 0 && S_ISSOCK(st.st_mode)) + unlink(path.c_str()); +#endif +} + +static string makeAuxUnixSocketPath(const string& mainSocketPath) +{ +#ifdef WIN_NT + const FB_SIZE_T slash = mainSocketPath.find_last_of("\\/"); + const char* const separator = (slash != string::npos && mainSocketPath[slash] == '/') ? "/" : "\\"; +#else + const FB_SIZE_T slash = mainSocketPath.rfind('/'); + const char* const separator = "/"; +#endif + string directory = slash == string::npos ? "." : mainSocketPath.substr(0, slash); + + string path; + path.printf("%s%sfb_event.%lu.%ld", directory.c_str(), separator, +#ifdef WIN_NT + static_cast(GetCurrentProcessId()), +#else + static_cast(getpid()), +#endif + static_cast(unixAuxSequence.exchangeAdd(1))); + + return path; +} + +#endif // HAVE_AF_UNIX_SUPPORT + rem_port* INET_analyze(ClntAuthBlock* cBlock, const PathName& file_name, @@ -908,9 +1041,20 @@ rem_port* INET_connect(const TEXT* name, if ((!name || !name[0]) && !packet) { +#ifdef HAVE_AF_UNIX_SUPPORT + const char* const socketPath = port->getPortConfig()->getRemoteServiceUnixSocket(); + if (socketPath && socketPath[0]) + return unix_connect(port, socketPath, packet, flag); +#endif + name = port->getPortConfig()->getRemoteBindAddress(); } +#ifdef HAVE_AF_UNIX_SUPPORT + if (af == AF_UNIX) + return unix_connect(port, name, packet, flag); +#endif + if (name) { host = name; @@ -1062,6 +1206,126 @@ rem_port* INET_connect(const TEXT* name, return port; } + +#ifdef HAVE_AF_UNIX_SUPPORT + +static rem_port* unix_connect(rem_port* port, const TEXT* socketPath, PACKET* packet, USHORT flag) +{ + UnixSocketAddress address; + socklen_t addressLength; + makeUnixSocketAddress(true, port, socketPath, &address, &addressLength); + + port->port_flags |= PORT_unix; + port->port_address = socketPath; + port->port_protocol_id = "UNIX"; + + delete port->port_connection; + port->port_connection = REMOTE_make_string(socketPath); + delete port->port_version; + port->port_version = REMOTE_make_string("unix"); + + port->port_handle = os_utils::socket(AF_UNIX, SOCK_STREAM, 0); + if (port->port_handle == INVALID_SOCKET) + { + inet_error(true, port, packet ? "socket" : "listen", packet ? isc_net_connect_err : + isc_net_connect_listen_err, INET_ERRNO); + } + + if (!packet) + return unix_listener_socket(port, flag, socketPath); + + const int n = connect(port->port_handle, reinterpret_cast(&address), addressLength); + if (n != -1) + { + port->port_peer_name = socketPath; + if (send_full(port, packet)) + return port; + } + + SOCLOSE(port->port_handle); + port->port_handle = INVALID_SOCKET; + inet_error(true, port, "connect", isc_net_connect_err, INET_ERRNO); + + return port; +} + +static rem_port* unix_listener_socket(rem_port* port, USHORT flag, const TEXT* socketPath) +{ + UnixSocketAddress address; + socklen_t addressLength; + makeUnixSocketAddress(true, port, socketPath, &address, &addressLength); + + removeUnixSocketPath(socketPath); + + if (bind(port->port_handle, reinterpret_cast(&address), addressLength) < 0) + inet_error(true, port, "bind", isc_net_connect_listen_err, INET_ERRNO); + + port->port_flags |= PORT_unix_unlink; + + if (listen(port->port_handle, SOMAXCONN) < 0) + inet_error(false, port, "listen", isc_net_connect_listen_err, INET_ERRNO); + + inet_ports->registerPort(port); + + if (flag & SRVR_multi_client) + { + port->port_dummy_packet_interval = 0; + port->port_dummy_timeout = 0; + port->port_server_flags |= (SRVR_server | SRVR_multi_client); + return port; + } + + while (true) + { + SOCKET s = os_utils::accept(port->port_handle, NULL, NULL); + const int inetErrNo = INET_ERRNO; + if (s == INVALID_SOCKET) + { + if (INET_shutting_down) + return NULL; + inet_error(true, port, "accept", isc_net_connect_err, inetErrNo); + } + +#ifdef WIN_NT + if (flag & SRVR_debug) +#else + if ((flag & SRVR_debug) || !fork()) +#endif + { + SOCLOSE(port->port_handle); + port->port_handle = s; + port->port_server_flags |= SRVR_server; + port->port_flags |= PORT_server; + port->port_flags &= ~PORT_unix_unlink; + return port; + } + +#ifdef WIN_NT + MutexLockGuard forkGuard(forkMutex, FB_FUNCTION); + if (!forkThreadStarted) + { + forkThreadStarted = true; + forkEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + forkSockets = FB_NEW ForkSocketsArray(*getDefaultMemoryPool()); + + Thread::start(forkThread, (void*) flag, THREAD_medium); + } + forkSockets->add(ForkSocket(s, true)); + SetEvent(forkEvent); +#else + MutexLockGuard guard(waitThreadMutex, FB_FUNCTION); + + if (!procCount++) + Thread::start(waitThread, 0, THREAD_medium); + + SOCLOSE(s); +#endif + } +} + +#endif // HAVE_AF_UNIX_SUPPORT + + static rem_port* listener_socket(rem_port* port, USHORT flag, const addrinfo* pai) { /************************************** @@ -1204,11 +1468,11 @@ static rem_port* listener_socket(rem_port* port, USHORT flag, const addrinfo* pa { forkThreadStarted = true; forkEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - forkSockets = FB_NEW SocketsArray(*getDefaultMemoryPool()); + forkSockets = FB_NEW ForkSocketsArray(*getDefaultMemoryPool()); Thread::start(forkThread, (void*) flag, THREAD_medium); } - forkSockets->add(s); + forkSockets->add(ForkSocket(s, false)); SetEvent(forkEvent); #else MutexLockGuard guard(waitThreadMutex, FB_FUNCTION); @@ -1235,7 +1499,7 @@ static rem_port* listener_socket(rem_port* port, USHORT flag, const addrinfo* pa } -rem_port* INET_reconnect(SOCKET handle) +rem_port* INET_reconnect(SOCKET handle, bool unixSocket) { /************************************** * @@ -1255,12 +1519,34 @@ rem_port* INET_reconnect(SOCKET handle) port->port_flags |= PORT_server; port->port_server_flags |= SRVR_server; - if (! setKeepAlive(port->port_handle)) { - gds__log("inet server err: setting KEEPALIVE socket option \n"); +#ifdef HAVE_AF_UNIX_SUPPORT + if (unixSocket) + { + port->port_flags |= PORT_unix; + port->port_protocol_id = "UNIX"; + + UnixSocketAddress address; + socklen_t addressLength = sizeof(address); + memset(&address, 0, sizeof(address)); + + if (getsockname(port->port_handle, reinterpret_cast(&address), &addressLength) == 0 && + address.sun_path[0]) + { + port->port_address = address.sun_path; + } + else + gds__log("inet server err: getting Unix socket name \n"); } + else +#endif + { + if (! setKeepAlive(port->port_handle)) { + gds__log("inet server err: setting KEEPALIVE socket option \n"); + } - if (! setNoNagleOption(port)) { - gds__log("inet server err: setting NODELAY socket option \n"); + if (! setNoNagleOption(port)) { + gds__log("inet server err: setting NODELAY socket option \n"); + } } return port; @@ -1534,7 +1820,16 @@ static rem_port* aux_connect(rem_port* port, PACKET* packet) port->port_handle = n; port->port_flags |= PORT_async; - get_peer_info(port); + if (port->port_flags & PORT_unix) + { +#ifdef HAVE_AF_UNIX_SUPPORT + removeUnixSocketPath(port->port_address); +#endif + port->port_protocol_id = "UNIX"; + port->port_flags &= ~PORT_unix_unlink; + } + else + get_peer_info(port); return port; } @@ -1546,6 +1841,44 @@ static rem_port* aux_connect(rem_port* port, PACKET* packet) new_port->port_dummy_timeout = new_port->port_dummy_packet_interval; P_RESP* response = &packet->p_resp; +#ifdef HAVE_AF_UNIX_SUPPORT + if (port->port_flags & PORT_unix) + { + string auxPath; + auxPath.assign(reinterpret_cast(response->p_resp_data.cstr_address), + response->p_resp_data.cstr_length); + + UnixSocketAddress address; + socklen_t addressLength; + makeUnixSocketAddress(false, port, auxPath.c_str(), &address, &addressLength); + + SOCKET n = os_utils::socket(AF_UNIX, SOCK_STREAM, 0); + if (n == INVALID_SOCKET) + { + const int savedError = INET_ERRNO; + port->auxAcceptError(packet); + inet_error(false, port, "socket", isc_net_event_connect_err, savedError); + } + + const int status = connect(n, reinterpret_cast(&address), addressLength); + if (status < 0) + { + const int savedError = INET_ERRNO; + SOCLOSE(n); + port->auxAcceptError(packet); + inet_error(false, port, "connect", isc_net_event_connect_err, savedError); + } + + new_port->port_handle = n; + new_port->port_flags |= PORT_unix; + new_port->port_protocol_id = "UNIX"; + new_port->port_peer_name = port->port_peer_name; + new_port->port_address = auxPath; + + return new_port; + } +#endif + // NJK - Determine address and port to use. // // The address returned by the server may be incorrect if it is behind a NAT box @@ -1610,6 +1943,56 @@ static rem_port* aux_request( rem_port* port, PACKET* packet) * **************************************/ +#ifdef HAVE_AF_UNIX_SUPPORT + if (port->port_flags & PORT_unix) + { + const string auxPath = makeAuxUnixSocketPath(port->port_address); + + UnixSocketAddress address; + socklen_t addressLength; + makeUnixSocketAddress(false, port, auxPath.c_str(), &address, &addressLength); + + SOCKET n = os_utils::socket(AF_UNIX, SOCK_STREAM, 0); + if (n == INVALID_SOCKET) + inet_error(false, port, "socket", isc_net_event_listen_err, INET_ERRNO); + + removeUnixSocketPath(auxPath); + + if (bind(n, reinterpret_cast(&address), addressLength) < 0) + { + const int savedError = INET_ERRNO; + SOCLOSE(n); + inet_error(false, port, "bind", isc_net_event_listen_err, savedError); + } + + if (listen(n, 1) < 0) + { + const int savedError = INET_ERRNO; + removeUnixSocketPath(auxPath); + SOCLOSE(n); + inet_error(false, port, "listen", isc_net_event_listen_err, savedError); + } + + rem_port* const new_port = alloc_port(port->port_parent, + (port->port_flags & PORT_no_oob) | PORT_async | PORT_connecting | PORT_unix | PORT_unix_unlink); + port->port_async = new_port; + new_port->port_dummy_packet_interval = port->port_dummy_packet_interval; + new_port->port_dummy_timeout = new_port->port_dummy_packet_interval; + + new_port->port_server_flags = port->port_server_flags; + new_port->port_channel = n; + new_port->port_peer_name = port->port_peer_name; + new_port->port_address = auxPath; + new_port->port_protocol_id = "UNIX"; + + P_RESP* response = &packet->p_resp; + response->p_resp_data.cstr_length = (ULONG) auxPath.length(); + memcpy(response->p_resp_data.cstr_address, auxPath.c_str(), auxPath.length()); + + return new_port; + } +#endif + // listen on (local) address of the original socket SockAddr our_address; if (our_address.getsockname(port->port_handle) < 0) @@ -1771,7 +2154,13 @@ static void disconnect(rem_port* port) return; port->port_state = rem_port::DISCONNECTED; - port->port_flags &= ~PORT_connecting; + +#ifdef HAVE_AF_UNIX_SUPPORT + if ((port->port_flags & PORT_unix_unlink) && (port->port_flags & PORT_unix)) + removeUnixSocketPath(port->port_address); +#endif + + port->port_flags &= ~(PORT_connecting | PORT_unix_unlink); if (port->port_async) { @@ -1922,7 +2311,7 @@ static int wsaExitHandler(const int, const int, void*) } -static int fork(SOCKET old_handle, USHORT flag) +static int fork(SOCKET old_handle, USHORT flag, bool unixSocket) { /************************************** * @@ -1947,7 +2336,8 @@ static int fork(SOCKET old_handle, USHORT flag) } string cmdLine; - cmdLine.printf("%s -i -h %" HANDLEFORMAT"@%" ULONGFORMAT, name, new_handle, GetCurrentProcessId()); + cmdLine.printf("%s -i%s -h %" HANDLEFORMAT"@%" ULONGFORMAT, + name, unixSocket ? " -u" : "", new_handle, GetCurrentProcessId()); STARTUPINFO start_crud; start_crud.cb = sizeof(STARTUPINFO); @@ -1988,18 +2378,18 @@ THREAD_ENTRY_DECLARE forkThread(THREAD_ENTRY_PARAM arg) while (!INET_shutting_down) { - SOCKET s = 0; + ForkSocket forkSocket; { // scope MutexLockGuard forkGuard(forkMutex, FB_FUNCTION); if (!forkSockets || forkSockets->getCount() == 0) break; - s = (*forkSockets)[0]; + forkSocket = (*forkSockets)[0]; forkSockets->remove((FB_SIZE_T) 0); } - fork(s, flag); - SOCLOSE(s); + fork(forkSocket.socket, flag, forkSocket.unixSocket); + SOCLOSE(forkSocket.socket); } } @@ -2174,7 +2564,14 @@ static rem_port* select_accept( rem_port* main_port) inet_error(true, port, "accept", isc_net_connect_err, INET_ERRNO); } - setKeepAlive(port->port_handle); + if (main_port->port_flags & PORT_unix) + { + port->port_flags |= PORT_unix; + port->port_protocol_id = "UNIX"; + port->port_address = main_port->port_address; + } + else + setKeepAlive(port->port_handle); port->port_flags |= PORT_server; @@ -2515,6 +2912,12 @@ void get_peer_info(rem_port* port) * Port just connected. Obtain some info about connection and peer. * **************************************/ + if (port->port_flags & PORT_unix) + { + port->port_protocol_id = "UNIX"; + return; + } + port->port_protocol_id = "TCPv4"; SockAddr address; diff --git a/src/remote/inet_proto.h b/src/remote/inet_proto.h index 0db635a6dd9..f0ad7b8fe5a 100644 --- a/src/remote/inet_proto.h +++ b/src/remote/inet_proto.h @@ -38,7 +38,7 @@ rem_port* INET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, const Firebird::PathName*, Firebird::ICryptKeyCallback*, int af = AF_UNSPEC); rem_port* INET_connect(const TEXT*, struct packet*, USHORT, Firebird::ClumpletReader*, Firebird::RefPtr*, int af = AF_UNSPEC); -rem_port* INET_reconnect(SOCKET); +rem_port* INET_reconnect(SOCKET, bool unixSocket); rem_port* INET_server(SOCKET); void setStopMainThread(FPTR_INT func); diff --git a/src/remote/remote.h b/src/remote/remote.h index f0aaae273af..9e5b282ec02 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -1258,6 +1258,8 @@ inline constexpr USHORT PORT_connecting = 0x0400; // Aux connection waits for a //inline constexpr USHORT PORT_z_data = 0x0800; // Zlib incoming buffer has data left after decompression inline constexpr USHORT PORT_compressed = 0x1000; // Compress outgoing stream (does not affect incoming) inline constexpr USHORT PORT_released = 0x2000; // release(), complementary to the first addRef() in constructor, was called +inline constexpr USHORT PORT_unix = 0x4000; // Port uses Unix domain socket transport +inline constexpr USHORT PORT_unix_unlink = 0x8000; // Unlink port_address when Unix domain socket port is disconnected // forward decl class RemotePortGuard; diff --git a/src/remote/server/os/win32/srvr_w32.cpp b/src/remote/server/os/win32/srvr_w32.cpp index c1658460cba..20223510f30 100644 --- a/src/remote/server/os/win32/srvr_w32.cpp +++ b/src/remote/server/os/win32/srvr_w32.cpp @@ -121,7 +121,7 @@ static THREAD_ENTRY_DECLARE inet_connect_wait_thread(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE xnet_connect_wait_thread(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE process_connection_thread(THREAD_ENTRY_PARAM); -static HANDLE parse_args(LPCSTR, USHORT*); +static HANDLE parse_args(LPCSTR, USHORT*, bool*); static void service_connection(rem_port*); static int wait_threads(const int reason, const int mask, void* arg); @@ -230,7 +230,8 @@ int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE /*hPrevInst*/, LPSTR lpszArgs, if (Config::getServerMode() != MODE_CLASSIC) server_flag = SRVR_multi_client; - const HANDLE connection_handle = parse_args(lpszArgs, &server_flag); + bool connectionUnixSocket = false; + const HANDLE connection_handle = parse_args(lpszArgs, &server_flag, &connectionUnixSocket); // get priority class from the config file int priority = Config::getProcessPriorityLevel(); @@ -286,7 +287,7 @@ int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE /*hPrevInst*/, LPSTR lpszArgs, { if (server_flag & SRVR_inet) { - port = INET_reconnect((SOCKET) connection_handle); + port = INET_reconnect((SOCKET) connection_handle, connectionUnixSocket); if (port) { @@ -549,7 +550,7 @@ static THREAD_ENTRY_DECLARE start_connections_thread(THREAD_ENTRY_PARAM) } -static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) +static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag, bool* connectionUnixSocket) { /************************************** * @@ -669,6 +670,10 @@ static HANDLE parse_args(LPCSTR lpszArgs, USHORT* pserver_flag) *pserver_flag &= ~SRVR_high_priority; break; + case 'U': + *connectionUnixSocket = true; + break; + case 'S': delimited = false; while (*p && *p == ' ')