/* * PROGRAM: JRD Remote Interface/Server * MODULE: inet.cpp * DESCRIPTION: TCP/UCP/IP Communications module. * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. * * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "EPSON" port * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "XENIX" port * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "IMP" port * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "NCR3000" port * * 2002-02-23 Sean Leyne - Code Cleanup, removed old M88K and NCR3000 port * * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "UNIXWARE" port * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix/MIPS" port * * 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port * * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port * * 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define * 2002.10.30 Sean Leyne - Code Cleanup, removed obsolete "SUN3_3" port * 2005.04.01 Konstantin Kuznetsov - allow setting NoNagle option in Classic * */ #include "firebird.h" #include #ifdef HAVE_SYS_TYPES_H #include #endif #include #include #include #include "../jrd/common.h" #include "../jrd/file_params.h" #include #include "../common/classes/timestamp.h" #include "../common/classes/init.h" #include "../jrd/ThreadStart.h" #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include // for socket() #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef WIN_NT #define FD_SETSIZE 1024 #endif #ifndef WIN_NT #include #include #include #include #include #ifdef HAVE_SYS_SELECT_H #include #endif #endif // !WIN_NT const int INET_RETRY_CALL = 5; #include "../remote/remote.h" #include "../jrd/ibase.h" #include "../common/thd.h" #include "../remote/inet_proto.h" #include "../remote/proto_proto.h" #include "../remote/remot_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/isc_proto.h" #include "../jrd/os/isc_i_proto.h" #include "../common/config/config.h" #include "../common/utils_proto.h" #include "../common/classes/ClumpletWriter.h" // Please review. Maybe not needed. See H_ERRNO in common.h. #if defined HPUX extern int h_errno; #endif using namespace Firebird; const USHORT MAX_PTYPE = ptype_lazy_send; #ifdef WIN_NT #include #include #include #include "../utilities/install/install_nt.h" #define SOCLOSE closesocket #define INET_RETRY_ERRNO WSAEINPROGRESS #define INET_ADDR_IN_USE WSAEADDRINUSE #define sleep(seconds) Sleep ((seconds) * 1000) const int NOTASOCKET = WSAENOTSOCK; #else // WIN_NT #ifndef SOCLOSE #define SOCLOSE close #endif #ifndef INET_ADDR_IN_USE #define INET_ADDR_IN_USE EADDRINUSE #endif #ifndef INET_RETRY_ERRNO #define INET_RETRY_ERRNO TRY_AGAIN #endif const int NOTASOCKET = EBADF; #endif // WIN_NT #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif // Can't find were it's used. //#ifndef SIGURG //#define SIGURG SIGINT //#endif #ifndef ENOBUFS #define ENOBUFS 0 #endif #ifndef FB_SEND_FLAGS #define FB_SEND_FLAGS 0 #endif #ifndef FB_SETOPT_FLAGS #define FB_SETOPT_FLAGS 0 #endif // //#define DEBUG 1 // #ifdef DEBUG #ifdef HAVE_SYS_TIMEB_H # include #endif const int TRACE_packets = 1 << 0; // bit 0 const int TRACE_operations = 1 << 1; // bit 1 const int TRACE_summary = 1 << 2; // bit 2 static int INET_trace = TRACE_summary | TRACE_packets | TRACE_operations; static time_t INET_start_time = 0; SLONG INET_force_error = -1; // simulate a network error static ULONG INET_count_send = 0; static ULONG INET_count_recv = 0; static ULONG INET_bytes_send = 0; static ULONG INET_bytes_recv = 0; static ULONG inet_debug_timer() { /************************************** * * i n e t _ d e b u g _ t i m e r * ************************************** * * Functional description * Utility function used in DEBUG mode only to put a timestamp * since start of connection on each debug output message. * * This has been implemented and tested on SOLARIS, and may * need tweeking on any other platform where DEBUG is needed. * **************************************/ #ifdef HAVE_GETTIMEOFDAY struct timeval tv; GETTIMEOFDAY(&tv); return (tv.tv_sec * 1000 + tv.tv_usec - INET_start_time); #else struct timeb now; ftime(&now); return (now.time * 1000 + now.millitm - INET_start_time); #endif // HAVE_GETTIMEOFDAY } #endif // DEBUG const SLONG MAX_DATA_LW = 1448; // Low Water mark const SLONG MAX_DATA_HW = 32768; // High Water mark const SLONG DEF_MAX_DATA = 8192; //const int MAXHOSTLEN = 64; const int SELECT_TIMEOUT = 60; // Dispatch thread select timeout (sec) struct slct_t { int slct_width; int slct_count; time_t slct_time; fd_set slct_fdset; }; static bool accept_connection(rem_port*, const P_CNCT*); #ifdef HAVE_SETITIMER static void alarm_handler(int); #endif static rem_port* alloc_port(rem_port*); static rem_port* aux_connect(rem_port*, PACKET*); static rem_port* aux_request(rem_port*, PACKET*); #if !defined(WIN_NT) static bool check_host(rem_port*); static THREAD_ENTRY_DECLARE waitThread(THREAD_ENTRY_PARAM); static Firebird::GlobalPtr waitThreadMutex; static unsigned int procCount = 0; #endif // WIN_NT static void disconnect(rem_port*); static void force_close(rem_port*); static int cleanup_ports(const int, const int, void*); #ifdef NO_FORK static int fork(); #endif #ifdef WIN_NT static void wsaExitHandler(void*); static int fork(SOCKET, USHORT); static THREAD_ENTRY_DECLARE forkThread(THREAD_ENTRY_PARAM); static Firebird::GlobalPtr forkMutex; static HANDLE forkEvent = INVALID_HANDLE_VALUE; static bool forkThreadStarted = false; typedef Firebird::Array SocketsArray; static SocketsArray* forkSockets; #endif static in_addr get_bind_address(); static int get_host_address(const char* name, in_addr* const host_addr_arr, const int arr_size); static void copy_p_cnct_repeat_array( p_cnct::p_cnct_repeat* pDest, const p_cnct::p_cnct_repeat* pSource, size_t nEntries); static int inet_destroy(XDR*); static void inet_gen_error(rem_port*, const Firebird::Arg::StatusVector& v); static bool_t inet_getbytes(XDR*, SCHAR *, u_int); static bool_t inet_getlong(XDR*, SLONG *); static u_int inet_getpostn(XDR*); static caddr_t inet_inline(XDR*, u_int); static void inet_error(rem_port*, const TEXT*, ISC_STATUS, int); static bool_t inet_putlong(XDR*, const SLONG*); static bool_t inet_putbytes(XDR*, const SCHAR*, u_int); static bool_t inet_read(XDR*); static bool_t inet_setpostn(XDR*, u_int); static rem_port* inet_try_connect( PACKET*, Rdb*, const Firebird::PathName&, const TEXT*, ISC_STATUS*, Firebird::ClumpletReader&); static bool_t inet_write(XDR*); //, int); #ifdef DEBUG static void packet_print(const TEXT*, const UCHAR*, int, ULONG); #endif static bool packet_receive(rem_port*, UCHAR*, SSHORT, SSHORT*); static bool packet_send(rem_port*, const SCHAR*, SSHORT); static rem_port* receive(rem_port*, PACKET *); static rem_port* select_accept(rem_port*); static void select_port(rem_port*, slct_t*, RemPortPtr&); static bool select_multi(rem_port*, UCHAR* buffer, SSHORT bufsize, SSHORT* length, RemPortPtr&); static bool select_wait(rem_port*, slct_t*); static int send_full(rem_port*, PACKET *); static int send_partial(rem_port*, PACKET *); static int xdrinet_create(XDR*, rem_port*, UCHAR *, USHORT, enum xdr_op); static bool setNoNagleOption(rem_port*); static FPTR_INT tryStopMainThread = 0; static XDR::xdr_ops inet_ops = { inet_getlong, inet_putlong, inet_getbytes, inet_putbytes, inet_getpostn, inet_setpostn, inet_inline, inet_destroy }; #define MAXCLIENTS NOFILE - 10 // Select uses bit masks of file descriptors in longs. #ifndef NBBY #define NBBY 8 #endif #ifndef NFDBITS #if !defined(WIN_NT) #define NFDBITS (sizeof(SLONG) * NBBY) #define FD_SET(n, p) ((p)->fds_bits[(n) / NFDBITS] |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n) / NFDBITS] &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n) / NFDBITS] & (1 << ((n) % NFDBITS))) #define FD_ZERO(p) memset(p, 0, sizeof(*(p))) #endif #endif #ifdef WIN_NT #define INTERRUPT_ERROR(x) (SYSCALL_INTERRUPTED(x) || (x) == WSAEINTR) #else #define INTERRUPT_ERROR(x) (SYSCALL_INTERRUPTED(x)) #endif SLONG INET_remote_buffer; static Firebird::GlobalPtr init_mutex; static bool INET_initialized = false; static bool INET_shutting_down = false; static slct_t INET_select = { 0, 0, 0 }; static int INET_max_clients; static rem_port* inet_async_receive = NULL; static Firebird::GlobalPtr port_mutex; static Firebird::GlobalPtr inet_ports; rem_port* INET_analyze(const Firebird::PathName& file_name, ISC_STATUS* status_vector, const TEXT* node_name, const TEXT* user_string, bool uv_flag, Firebird::ClumpletReader &dpb) { /************************************** * * I N E T _ a n a l y z e * ************************************** * * Functional description * File_name is on node_name. * Establish an external connection to node_name. * * If a connection is established, return a port block, otherwise * return NULL. * * If the "uv_flag" is non-zero, user verification also takes place. * **************************************/ // We need to establish a connection to a remote server. Allocate the necessary // blocks and get ready to go. Rdb* rdb = new Rdb; PACKET* packet = &rdb->rdb_packet; // Pick up some user identification information Firebird::ClumpletWriter user_id(Firebird::ClumpletReader::UnTagged, MAX_DPB_SIZE); Firebird::string buffer; int eff_gid; int eff_uid; ISC_get_user(&buffer, &eff_uid, &eff_gid, user_string); user_id.insertString(CNCT_user, buffer); ISC_get_host(buffer); buffer.lower(); user_id.insertString(CNCT_host, buffer); if ((eff_uid == -1) || uv_flag) { user_id.insertTag(CNCT_user_verification); } else { // Communicate group id info to server, as user maybe running under group // id other than default specified in /etc/passwd. eff_gid = htonl(eff_gid); user_id.insertBytes(CNCT_group, reinterpret_cast(&eff_gid), sizeof(eff_gid)); } // Establish connection to server // Note: prior to V3.1E a recievers could not in truth handle more // than 5 protocol descriptions, so we try them in chunks of 5 or less // If we want user verification, we can't speak anything less than version 7 P_CNCT* cnct = &packet->p_cnct; cnct->p_cnct_user_id.cstr_length = user_id.getBufferLength(); cnct->p_cnct_user_id.cstr_address = user_id.getBuffer(); static const p_cnct::p_cnct_repeat protocols_to_try1[] = { REMOTE_PROTOCOL(PROTOCOL_VERSION8, ptype_rpc, MAX_PTYPE, 1), REMOTE_PROTOCOL(PROTOCOL_VERSION10, ptype_rpc, MAX_PTYPE, 2), REMOTE_PROTOCOL(PROTOCOL_VERSION11, ptype_rpc, MAX_PTYPE, 3), REMOTE_PROTOCOL(PROTOCOL_VERSION12, ptype_rpc, MAX_PTYPE, 4) #ifdef SCROLLABLE_CURSORS , REMOTE_PROTOCOL(PROTOCOL_SCROLLABLE_CURSORS, ptype_rpc, MAX_PTYPE, 99) #endif }; cnct->p_cnct_count = FB_NELEM(protocols_to_try1); copy_p_cnct_repeat_array(cnct->p_cnct_versions, protocols_to_try1, cnct->p_cnct_count); // Try connection using first set of protocols. punt if error rem_port* port = inet_try_connect(packet, rdb, file_name, node_name, status_vector, dpb); if (!port) { return NULL; } if (packet->p_operation == op_reject && !uv_flag) { disconnect(port); // try again with next set of known protocols cnct->p_cnct_user_id.cstr_length = user_id.getBufferLength(); cnct->p_cnct_user_id.cstr_address = user_id.getBuffer(); static const p_cnct::p_cnct_repeat protocols_to_try2[] = { REMOTE_PROTOCOL(PROTOCOL_VERSION6, ptype_rpc, ptype_batch_send, 1), REMOTE_PROTOCOL(PROTOCOL_VERSION7, ptype_rpc, MAX_PTYPE, 2) }; cnct->p_cnct_count = FB_NELEM(protocols_to_try2); copy_p_cnct_repeat_array(cnct->p_cnct_versions, protocols_to_try2, cnct->p_cnct_count); port = inet_try_connect(packet, rdb, file_name, node_name, status_vector, dpb); if (!port) { return NULL; } } if (packet->p_operation == op_reject && !uv_flag) { disconnect(port); // try again with next set of known protocols cnct->p_cnct_user_id.cstr_length = user_id.getBufferLength(); cnct->p_cnct_user_id.cstr_address = user_id.getBuffer(); static const p_cnct::p_cnct_repeat protocols_to_try3[] = { REMOTE_PROTOCOL(PROTOCOL_VERSION3, ptype_rpc, ptype_batch_send, 1), REMOTE_PROTOCOL(PROTOCOL_VERSION4, ptype_rpc, ptype_batch_send, 2) }; cnct->p_cnct_count = FB_NELEM(protocols_to_try3); copy_p_cnct_repeat_array(cnct->p_cnct_versions, protocols_to_try3, cnct->p_cnct_count); port = inet_try_connect(packet, rdb, file_name, node_name, status_vector, dpb); if (!port) { return NULL; } } if (packet->p_operation != op_accept) { *status_vector++ = isc_arg_gds; *status_vector++ = isc_connect_reject; *status_vector++ = 0; disconnect(port); delete rdb; return NULL; } port->port_protocol = packet->p_acpt.p_acpt_version; // once we've decided on a protocol, concatenate the version // string to reflect it... Firebird::string temp; temp.printf("%s/P%d", port->port_version->str_data, port->port_protocol & FB_PROTOCOL_MASK); delete port->port_version; port->port_version = REMOTE_make_string(temp.c_str()); if (packet->p_acpt.p_acpt_architecture == ARCHITECTURE) { port->port_flags |= PORT_symmetric; } if (packet->p_acpt.p_acpt_type == ptype_rpc) { port->port_flags |= PORT_rpc; } if (packet->p_acpt.p_acpt_type != ptype_out_of_band) { port->port_flags |= PORT_no_oob; } if (packet->p_acpt.p_acpt_type == ptype_lazy_send) { port->port_flags |= PORT_lazy; } return port; } rem_port* INET_connect(const TEXT* name, PACKET* packet, ISC_STATUS* status_vector, USHORT flag, Firebird::ClumpletReader* dpb) { /************************************** * * I N E T _ c o n n e c t * ************************************** * * Functional description * Establish half of a communication link. If a connect packet is given, * the connection is on behalf of a remote interface. Otherwise the connect * is for a server process. * **************************************/ #ifdef DEBUG { if (INET_trace & TRACE_operations) { fprintf(stdout, "INET_connect\n"); fflush(stdout); } INET_start_time = inet_debug_timer(); // CVC: I don't see the point in replacing this with fb_utils::readenv(). const char* p = getenv("INET_force_error"); if (p != NULL) { INET_force_error = atoi(p); } } #endif rem_port* const port = alloc_port(NULL); port->port_status_vector = status_vector; REMOTE_get_timeout_params(port, dpb); status_vector[0] = isc_arg_gds; status_vector[1] = 0; status_vector[2] = isc_arg_end; Firebird::string host; Firebird::string protocol; if (name) { host = name; const size_t pos = host.find("/"); if (pos != Firebird::string::npos) { protocol = host.substr(pos + 1); host = host.substr(0, pos); } } if (host.hasData()) { delete port->port_connection; port->port_connection = REMOTE_make_string(host.c_str()); } else { host = port->port_host->str_data; } if (protocol.isEmpty()) { const unsigned short port2 = Config::getRemoteServicePort(); if (port2) { protocol.printf("%hu", port2); } else { protocol = Config::getRemoteServiceName(); } } // Set up Inter-Net socket address struct sockaddr_in address; memset(&address, 0, sizeof(address)); // U N I X style sockets address.sin_family = AF_INET; // define maximum numbers of addresses for a host that we can handle const int MAX_HOST_ADDRESS_NUMBER = 8; in_addr host_addr; in_addr host_addr_arr[MAX_HOST_ADDRESS_NUMBER]; int hostAddressNumber = 0; if (packet) { // client connection hostAddressNumber = get_host_address(host.c_str(), host_addr_arr, MAX_HOST_ADDRESS_NUMBER); if (hostAddressNumber > MAX_HOST_ADDRESS_NUMBER) { hostAddressNumber = MAX_HOST_ADDRESS_NUMBER; } if (! hostAddressNumber) { gds__log("INET/INET_connect: gethostbyname (%s) failed, error code = %d", host.c_str(), H_ERRNO); inet_gen_error(port, Arg::Gds(isc_net_lookup_err) << Arg::Gds(isc_host_unknown)); disconnect(port); return NULL; } host_addr = host_addr_arr[0]; } else { // server connection host_addr = get_bind_address(); } const struct servent* service = getservbyname(protocol.c_str(), "tcp"); #ifdef WIN_NT // On Windows NT/9x, getservbyname can only accomodate // 1 call at a time. In this case it returns the error // WSAEINPROGRESS. // If this happens, retry the operation a few times. // NOTE: This still does not guarantee success, but helps. if (!service) { for (int retry = 0; H_ERRNO == INET_RETRY_ERRNO && retry < INET_RETRY_CALL; retry++) { if ( (service = getservbyname(protocol.c_str(), "tcp")) ) break; } } #endif // WIN_NT // Modification by luz (slightly modified by FSG) // instead of failing here, try applying hard-wired // translation of "gds_db" into "3050" // This way, a connection to a remote FB server // works even from clients with missing "gds_db" // entry in "services" file, which is important // for zero-installation clients. if (!service) { if (protocol == FB_SERVICE_NAME) { // apply hardwired translation address.sin_port = htons(FB_SERVICE_PORT); } // modification by FSG 23.MAR.2001 else { // modification by FSG 23.MAR.2001 // The user has supplied something as protocol // let's see whether this is a port number // instead of a service name address.sin_port = htons(atoi(protocol.c_str())); } if (address.sin_port == 0) { // end of modification by FSG // this is the original code gds__log("INET/INET_connect: getservbyname failed, error code = %d", H_ERRNO); inet_gen_error(port, Arg::Gds(isc_net_lookup_err) << Arg::Gds(isc_service_unknown) << Arg::Str(protocol) << Arg::Str("tcp")); return NULL; } // else / not hardwired gds_db translation } else { // if we have got a service-struct, get port number from there // (in case of hardwired gds_db to 3050 translation, address.sin_port was // already set above address.sin_port = service->s_port; } // else (service found) // end of modifications by luz // Allocate a port block and initialize a socket for communications port->port_handle = socket(AF_INET, SOCK_STREAM, 0); if (port->port_handle == INVALID_SOCKET) { inet_error(port, "socket", isc_net_connect_err, INET_ERRNO); disconnect(port); return NULL; } // If we're a host, just make the connection int n; if (packet) { if (! setNoNagleOption(port)) { inet_error(port, "setsockopt TCP_NODELAY", isc_net_connect_err, INET_ERRNO); disconnect(port); return NULL; } int inetErrNo = 0; for (int i = 0; i < hostAddressNumber; i++) { address.sin_addr = host_addr_arr[i]; // If host has two addresses and the first one failed, // but the second one succeeded - no need to worry n = connect(port->port_handle, (struct sockaddr*) &address, sizeof(address)); inetErrNo = INET_ERRNO; if (n != -1 && send_full(port, packet)) return port; } inet_error(port, "connect", isc_net_connect_err, inetErrNo); disconnect(port); return NULL; } // We're a server, so wait for a host to show up memcpy(&address.sin_addr, &host_addr, sizeof(address.sin_addr)); if (flag & SRVR_multi_client) { struct linger lingerInfo; lingerInfo.l_onoff = 0; lingerInfo.l_linger = 0; #ifndef WIN_NT // dimitr: on Windows, lack of SO_REUSEADDR works the same way as it was specified on POSIX, // i.e. it allows binding to a port in a TIME_WAIT/FIN_WAIT state. If this option // is turned on explicitly, then a port can be re-bound regardless of its state, // e.g. while it's listening. This is surely not what we want. int optval = TRUE; n = setsockopt(port->port_handle, SOL_SOCKET, SO_REUSEADDR, (SCHAR*) &optval, sizeof(optval)); if (n == -1) { inet_error(port, "setsockopt REUSE", isc_net_connect_listen_err, INET_ERRNO); disconnect(port); return NULL; } #endif // Get any values for SO_LINGER so that they can be reset during // disconnect. SO_LINGER should be set by default on the socket socklen_t optlen = sizeof(port->port_linger); n = getsockopt(port->port_handle, SOL_SOCKET, SO_LINGER, (SCHAR *) & port->port_linger, &optlen); if (n != 0) // getsockopt failed port->port_linger.l_onoff = 0; n = setsockopt(port->port_handle, SOL_SOCKET, SO_LINGER, (SCHAR *) & lingerInfo, sizeof(lingerInfo)); if (n == -1) { inet_error(port, "setsockopt LINGER", isc_net_connect_listen_err, INET_ERRNO); disconnect(port); return NULL; } if (! setNoNagleOption(port)) { inet_error(port, "setsockopt TCP_NODELAY", isc_net_connect_listen_err, INET_ERRNO); disconnect(port); return NULL; } } n = bind(port->port_handle, (struct sockaddr*) &address, sizeof(address)); if (n == -1) { // On Linux platform, when the server dies the system holds a port // for some time. for (int retry = 0; INET_ERRNO == INET_ADDR_IN_USE && retry < INET_RETRY_CALL; retry++) { sleep(10); n = bind(port->port_handle, (struct sockaddr *) &address, sizeof(address)); if (n == 0) break; } } if (n == -1) { inet_error(port, "bind", isc_net_connect_listen_err, INET_ERRNO); disconnect(port); return NULL; } n = listen(port->port_handle, 5); if (n == -1) { inet_error(port, "listen", isc_net_connect_listen_err, INET_ERRNO); return NULL; } inet_ports->registerPort(port); if (flag & SRVR_multi_client) { // Prevent the generation of dummy keepalive packets on the connect port. port->port_dummy_packet_interval = 0; port->port_dummy_timeout = 0; port->port_server_flags |= (SRVR_server | SRVR_multi_client); return port; } while (true) { socklen_t l = sizeof(address); SOCKET s = accept(port->port_handle, (struct sockaddr*) &address, &l); const int inetErrNo = INET_ERRNO; if (s == INVALID_SOCKET) { if (!INET_shutting_down) { inet_error(port, "accept", isc_net_connect_err, inetErrNo); disconnect(port); } return NULL; } #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 | SRVR_debug; port->port_flags |= PORT_server; return port; } #ifdef WIN_NT Firebird::MutexLockGuard forkGuard(forkMutex); if (!forkThreadStarted) { forkThreadStarted = true; forkEvent = CreateEvent(NULL, FALSE, FALSE, NULL); forkSockets = new SocketsArray(*getDefaultMemoryPool()); gds__thread_start(forkThread, (void*) flag, THREAD_medium, 0, 0); } forkSockets->add(s); SetEvent(forkEvent); #else Firebird::MutexLockGuard guard(waitThreadMutex); if (! procCount++) { gds__thread_start(waitThread, 0, THREAD_medium, 0, 0); } SOCLOSE(s); #endif } #ifdef WIN_NT Firebird::MutexLockGuard forkGuard(forkMutex); if (forkThreadStarted) { SetEvent(forkEvent); CloseHandle(forkEvent); delete forkSockets; forkSockets = NULL; } #endif } rem_port* INET_reconnect(SOCKET handle, ISC_STATUS* status_vector) { /************************************** * * I N E T _ r e c o n n e c t * ************************************** * * Functional description * A communications link has been established by another * process. We have inheritted the handle. Set up * a port block. * **************************************/ rem_port* const port = alloc_port(NULL); port->port_status_vector = status_vector; status_vector[0] = isc_arg_gds; status_vector[1] = 0; status_vector[2] = isc_arg_end; port->port_handle = handle; port->port_flags |= PORT_server; port->port_server_flags |= SRVR_server; int n = 0, optval = TRUE; n = setsockopt(port->port_handle, SOL_SOCKET, SO_KEEPALIVE, (SCHAR*) &optval, sizeof(optval)); if (n == -1) { gds__log("inet server err: setting KEEPALIVE socket option \n"); } if (! setNoNagleOption(port)) { gds__log("inet server err: setting NODELAY socket option \n"); } return port; } rem_port* INET_server(SOCKET sock) { /************************************** * * I N E T _ s e r v e r * ************************************** * * Functional description * We have been spawned by a master server with a connection * established. Set up port block with the appropriate socket. * **************************************/ int n = 0; rem_port* const port = alloc_port(NULL); port->port_flags |= PORT_server; port->port_server_flags |= SRVR_server; port->port_handle = sock; int optval = 1; n = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (SCHAR*) &optval, sizeof(optval)); if (n == -1) { gds__log("inet server err: setting KEEPALIVE socket option \n"); } if (! setNoNagleOption(port)) { gds__log("inet server err: setting NODELAY socket option \n"); } return port; } void INET_set_clients( int count) { /************************************** * * I N E T _ s e t _ c l i e n t s * ************************************** * * Functional description * Set maxinum number of clients served before * starting new server * **************************************/ INET_max_clients = (count && count < MAXCLIENTS) ? count : MAXCLIENTS; } static bool accept_connection(rem_port* port, const P_CNCT* cnct) { /************************************** * * a c c e p t _ c o n n e c t i o n * ************************************** * * Functional description * Accept an incoming request for connection. This is purely a lower * level handshaking function, and does not constitute the server * response for protocol selection. * **************************************/ // Default account to "guest" (in theory all packets contain a name) Firebird::string name("guest"), password; // Pick up account and password, if given Firebird::ClumpletReader id(Firebird::ClumpletReader::UnTagged, cnct->p_cnct_user_id.cstr_address, cnct->p_cnct_user_id.cstr_length); SLONG eff_gid = -1, eff_uid = -1; bool user_verification = false; for (id.rewind(); !id.isEof(); id.moveNext()) { switch (id.getClumpTag()) { case CNCT_user: id.getString(name); break; case CNCT_passwd: id.getString(password); break; case CNCT_group: { const int length = id.getClumpLength(); if (length != 0) { eff_gid = 0; memcpy(&eff_gid, id.getBytes(), length); eff_gid = ntohl(eff_gid); } break; } // this case indicates that the client has requested that // we force the user name/password to be verified against // the security database case CNCT_user_verification: user_verification = true; break; } } // See if user exists. If not, reject connection if (user_verification) { eff_gid = eff_uid = -1; // port->port_flags |= PORT_not_trusted; // never tested } #ifndef WIN_NT else { if (!check_host(port)) { return false; } } { // scope // If the environment variable ISC_INET_SERVER_HOME is set, // change the home directory to the specified directory. // Note that this will overrule the normal setting of // the current directory to the effective user's home directory. // This feature was added primarily for testing via remote // loopback - but does seem to be of good general use, so // is activated for the release version. // 1995-February-27 David Schnepper Firebird::PathName home; if (fb_utils::readenv("ISC_INET_SERVER_HOME", home)) { if (chdir(home.c_str())) { gds__log("inet_server: unable to cd to %s errno %d\n", home.c_str(), INET_ERRNO); // We continue after the error } } } // end scope #endif // !WIN_NT // store FULL user identity in port_user_name for security purposes Firebird::string temp; temp.printf("%s.%ld.%ld", name.c_str(), eff_gid, eff_uid); port->port_user_name = REMOTE_make_string(temp.c_str()); port->port_protocol_str = REMOTE_make_string("TCPv4"); struct sockaddr_in address; socklen_t l = sizeof(address); memset(&address, 0, sizeof(address)); int status = getpeername(port->port_handle, (struct sockaddr *) &address, &l); if (status == 0) { Firebird::string addr_str; const UCHAR* ip = (UCHAR*) &address.sin_addr; addr_str.printf( "%d.%d.%d.%d", static_cast(ip[0]), static_cast(ip[1]), static_cast(ip[2]), static_cast(ip[3]) ); port->port_address_str = REMOTE_make_string(addr_str.c_str()); } return true; } static rem_port* alloc_port(rem_port* const parent) { /************************************** * * a l l o c _ p o r t * ************************************** * * Functional description * Allocate a port block, link it in to parent (if there is a parent), * and initialize input and output XDR streams. * **************************************/ if (!INET_initialized) { Firebird::MutexLockGuard guard(init_mutex); if (!INET_initialized) { #ifdef WIN_NT static WSADATA wsadata; const WORD version = MAKEWORD(2, 0); const int wsaError = WSAStartup(version, &wsadata); if (wsaError) { if (parent) inet_error(parent, "WSAStartup", isc_net_init_error, wsaError); else { gds__log("INET/alloc_port: WSAStartup failed, error code = %d", wsaError); } return NULL; } gds__register_cleanup(wsaExitHandler, 0); #endif INET_remote_buffer = Config::getTcpRemoteBufferSize(); if (INET_remote_buffer < MAX_DATA_LW || INET_remote_buffer > MAX_DATA_HW) { INET_remote_buffer = DEF_MAX_DATA; } #ifdef DEBUG gds__log(" Info: Remote Buffer Size set to %ld", INET_remote_buffer); #endif fb_shutdown_callback(0, cleanup_ports, fb_shut_postproviders, 0); INET_initialized = true; // This should go AFTER 'INET_initialized = true' to avoid recursion inet_async_receive = alloc_port(0); inet_async_receive->port_flags |= PORT_server; } } rem_port* const port = new rem_port(rem_port::INET, INET_remote_buffer * 2); REMOTE_get_timeout_params(port, 0); TEXT buffer[BUFFER_SMALL]; gethostname(buffer, sizeof(buffer)); port->port_host = REMOTE_make_string(buffer); port->port_connection = REMOTE_make_string(buffer); SNPRINTF(buffer, FB_NELEM(buffer), "tcp (%s)", port->port_host->str_data); port->port_version = REMOTE_make_string(buffer); port->port_accept = accept_connection; port->port_disconnect = disconnect; port->port_force_close = force_close; port->port_receive_packet = receive; port->port_select_multi = select_multi; port->port_send_packet = send_full; port->port_send_partial = send_partial; port->port_connect = aux_connect; port->port_request = aux_request; port->port_buff_size = (USHORT) INET_remote_buffer; port->port_async_receive = inet_async_receive; xdrinet_create( &port->port_send, port, &port->port_buffer[INET_remote_buffer], (USHORT) INET_remote_buffer, XDR_ENCODE); xdrinet_create( &port->port_receive, port, port->port_buffer, 0, XDR_DECODE); if (parent && !(parent->port_server_flags & SRVR_thread_per_port)) { Firebird::MutexLockGuard guard(port_mutex); port->linkParent(parent); } return port; } static rem_port* aux_connect(rem_port* port, PACKET* packet) { /************************************** * * a u x _ c o n n e c t * ************************************** * * Functional description * Try to establish an alternative connection. Somebody has already * done a successfull connect request ("packet" contains the response). * **************************************/ struct sockaddr_in address; socklen_t l = sizeof(address); // If this is a server, we're got an auxiliary connection. Accept it if (port->port_server_flags) { struct timeval timeout; timeout.tv_sec = port->port_connect_timeout; timeout.tv_usec = 0; fd_set slct_fdset; FD_ZERO(&slct_fdset); FD_SET(port->port_channel, &slct_fdset); int inetErrNo = 0; while (true) { const int count = #ifdef WIN_NT select(FD_SETSIZE, &slct_fdset, NULL, NULL, &timeout); #else select(port->port_channel + 1, &slct_fdset, NULL, NULL, &timeout); #endif inetErrNo = INET_ERRNO; if (count != -1 || !INTERRUPT_ERROR(inetErrNo)) { if (count == 1) { break; } else { const ISC_STATUS error_code = (count == 0) ? isc_net_event_connect_timeout : isc_net_event_connect_err; inet_error(port, "select", error_code, inetErrNo); SOCLOSE(port->port_channel); return NULL; } } } const SOCKET n = accept(port->port_channel, (struct sockaddr*) &address, &l); inetErrNo = INET_ERRNO; if (n == INVALID_SOCKET) { inet_error(port, "accept", isc_net_event_connect_err, inetErrNo); SOCLOSE(port->port_channel); return NULL; } SOCLOSE(port->port_channel); port->port_handle = n; port->port_flags |= PORT_async; return port; } rem_port* const new_port = alloc_port(port->port_parent); 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_flags = port->port_flags & PORT_no_oob; new_port->port_flags |= PORT_async; P_RESP* response = &packet->p_resp; // Set up new socket SOCKET n = socket(AF_INET, SOCK_STREAM, 0); if (n == INVALID_SOCKET) { inet_error(port, "socket", isc_net_event_connect_err, INET_ERRNO); return NULL; } // NJK - Determine address and port to use. // // The address returned by the server may be incorrect if it is behind a NAT box // so we must use the address that was used to connect the main socket, not the // address reported by the server. // // The port number reported by the server is used. For NAT support the port number // should be configured to be a fixed port number in the server configuration. memset(&address, 0, sizeof(address)); int status = getpeername(port->port_handle, (struct sockaddr *) &address, &l); if (status != 0) { inet_error(port, "socket", isc_net_event_connect_err, INET_ERRNO); SOCLOSE(n); return NULL; } address.sin_family = AF_INET; address.sin_port = ((struct sockaddr_in *)(response->p_resp_data.cstr_address))->sin_port; int optval = 1; setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, (SCHAR*) &optval, sizeof(optval)); status = connect(n, (struct sockaddr *) &address, sizeof(address)); const int inetErrNo = INET_ERRNO; if (status < 0) { inet_error(port, "connect", isc_net_event_connect_err, inetErrNo); SOCLOSE(n); return NULL; } new_port->port_handle = n; return new_port; } static rem_port* aux_request( rem_port* port, PACKET* packet) { /************************************** * * a u x _ r e q u e s t * ************************************** * * Functional description * A remote interface has requested the server prepare an auxiliary * connection; the server calls aux_request to set up the connection. * **************************************/ struct sockaddr_in address; // Set up new socket address.sin_family = AF_INET; in_addr bind_addr = get_bind_address(); memcpy(&address.sin_addr, &bind_addr, sizeof(address.sin_addr)); address.sin_port = htons(Config::getRemoteAuxPort()); SOCKET n = socket(AF_INET, SOCK_STREAM, 0); if (n == INVALID_SOCKET) { inet_error(port, "socket", isc_net_event_listen_err, INET_ERRNO); return NULL; } #ifndef WIN_NT // dimitr: on Windows, lack of SO_REUSEADDR works the same way as it was specified on POSIX, // i.e. it allows binding to a port in a TIME_WAIT/FIN_WAIT state. If this option // is turned on explicitly, then a port can be re-bound regardless of its state, // e.g. while it's listening. This is surely not what we want. int optval = TRUE; if (setsockopt(n, SOL_SOCKET, SO_REUSEADDR, (SCHAR*) &optval, sizeof(optval)) < 0) { inet_error(port, "setsockopt REUSE", isc_net_event_listen_err, INET_ERRNO); return NULL; } #endif if (bind(n, (struct sockaddr *) &address, sizeof(address)) < 0) { inet_error(port, "bind", isc_net_event_listen_err, INET_ERRNO); return NULL; } socklen_t length = sizeof(address); if (getsockname(n, (struct sockaddr *) &address, &length) < 0) { inet_error(port, "getsockname", isc_net_event_listen_err, INET_ERRNO); return NULL; } if (listen(n, 1) < 0) { inet_error(port, "listen", isc_net_event_listen_err, INET_ERRNO); return NULL; } rem_port* const new_port = alloc_port(port->port_parent); 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 = (int) n; new_port->port_flags = port->port_flags & PORT_no_oob; P_RESP* response = &packet->p_resp; struct sockaddr_in port_address; if (getsockname(port->port_handle, (struct sockaddr *) &port_address, &length) < 0) { inet_error(port, "getsockname", isc_net_event_listen_err, INET_ERRNO); return NULL; } memcpy(&address.sin_addr, &port_address.sin_addr, sizeof(address.sin_addr)); response->p_resp_data.cstr_length = sizeof(address); memcpy(response->p_resp_data.cstr_address, &address, sizeof(address)); return new_port; } #ifndef WIN_NT static bool check_host(rem_port* port) { /************************************** * * c h e c k _ h o s t ( n o n - V M S ) * ************************************** * * Functional description * Check the host on the other end of the socket to see if it's localhost * **************************************/ struct sockaddr_in address; socklen_t length = sizeof(address); if (getpeername(port->port_handle, (struct sockaddr*) &address, &length) == -1) return false; // If source address is in the loopback net - trust it return (ntohl(address.sin_addr.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET; } #endif // WIN_NT #if !(defined WIN_NT) static THREAD_ENTRY_DECLARE waitThread(THREAD_ENTRY_PARAM) { /************************************** * * w a i t T h r e a d * ************************************** * * Functional description * Waits for processes started by standalone classic server (avoid zombies) * **************************************/ while (procCount > 0) { int rc = wait(0); Firebird::MutexLockGuard guard(waitThreadMutex); if (rc > 0) { --procCount; } } return 0; } #endif // !defined(WIN_NT) static void disconnect(rem_port* const port) { /************************************** * * d i s c o n n e c t * ************************************** * * Functional description * Break a remote connection. * **************************************/ // SO_LINGER was turned off on the initial bind when the server was started. // This will force a reset to be sent to the client when the socket is closed. // We only want this behavior in the case of the server terminating // abnormally and not on an orderly shut down. Because of this, turn the // SO_LINGER option back on for the socket. The result of setsockopt isn't // too important at this stage since we are closing the socket anyway. This // is an attempt to return the socket to a state where a graceful shutdown can // occur. if (port->port_linger.l_onoff) { setsockopt(port->port_handle, SOL_SOCKET, SO_LINGER, (SCHAR*) &port->port_linger, sizeof(port->port_linger)); } #if defined WIN_NT if (port->port_handle && port->port_handle != INVALID_SOCKET) #else if (port->port_handle) #endif { shutdown(port->port_handle, 2); } Firebird::MutexLockGuard guard(port_mutex); port->port_state = rem_port::DISCONNECTED; if (port->port_async) { disconnect(port->port_async); port->port_async = NULL; } // If this is a sub-port, unlink it from its parent port->unlinkParent(); inet_ports->unRegisterPort(port); if (port->port_handle) { SOCLOSE(port->port_handle); } port->release(); #ifdef DEBUG if (INET_trace & TRACE_summary) { fprintf(stdout, "INET_count_send = %lu packets\n", INET_count_send); fprintf(stdout, "INET_bytes_send = %lu bytes\n", INET_bytes_send); fprintf(stdout, "INET_count_recv = %lu packets\n", INET_count_recv); fprintf(stdout, "INET_bytes_recv = %lu bytes\n", INET_bytes_recv); fflush(stdout); } #endif return; } static void force_close(rem_port* port) { /************************************** * * f o r c e _ c l o s e * ************************************** * * Functional description * Forcebly close remote connection. * **************************************/ if (port->port_state != rem_port::PENDING) return; port->port_state = rem_port::BROKEN; const SOCKET handle = port->port_handle; port->port_handle = 0; #ifdef WIN_NT if (handle && handle != INVALID_SOCKET) #else if (handle) #endif { shutdown(handle, 2); SOCLOSE(handle); } } static int cleanup_ports(const int, const int, void* /*arg*/) { /************************************** * * c l e a n u p _ p o r t s * ************************************** * * Functional description * Shutdown all active connections * to allow correct shutdown. * **************************************/ INET_shutting_down = true; inet_ports->closePorts(); return 0; } #ifdef NO_FORK static int fork() { /************************************** * * f o r k ( N O _ F O R K ) * ************************************** * * Functional description * Hmmm. * **************************************/ return 1; } #endif #ifdef WIN_NT static void wsaExitHandler(void*) { /************************************** * * w s a E x i t H a n d l e r * ************************************** * * Functional description * Cleanup WSA. * **************************************/ SleepEx(0, FALSE); // let select in other thread(s) shutdown gracefully WSACleanup(); } static int fork(SOCKET old_handle, USHORT flag) { /************************************** * * f o r k ( W I N _ N T ) * ************************************** * * Functional description * Create a child process. * **************************************/ TEXT name[MAXPATHLEN]; GetModuleFileName(NULL, name, sizeof(name)); HANDLE new_handle; if (!DuplicateHandle(GetCurrentProcess(), (HANDLE) old_handle, GetCurrentProcess(), &new_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { gds__log("INET/inet_error: fork/DuplicateHandle errno = %d", GetLastError()); return 0; } Firebird::string cmdLine; cmdLine.printf("%s -i -h %"SLONGFORMAT"@%"SLONGFORMAT, name, (SLONG) new_handle, GetCurrentProcessId()); STARTUPINFO start_crud; start_crud.cb = sizeof(STARTUPINFO); start_crud.lpReserved = NULL; start_crud.lpReserved2 = NULL; start_crud.cbReserved2 = 0; start_crud.lpDesktop = NULL; start_crud.lpTitle = NULL; start_crud.dwFlags = STARTF_FORCEOFFFEEDBACK; PROCESS_INFORMATION pi; if (CreateProcess(NULL, cmdLine.begin(), NULL, NULL, FALSE, (flag & SRVR_high_priority ? HIGH_PRIORITY_CLASS | DETACHED_PROCESS : NORMAL_PRIORITY_CLASS | DETACHED_PROCESS), NULL, NULL, &start_crud, &pi)) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); // hvlad: child process will close our handle of just accepted socket return 1; } gds__log("INET/inet_error: fork/CreateProcess errno = %d", GetLastError()); CloseHandle(new_handle); return 0; } THREAD_ENTRY_DECLARE forkThread(THREAD_ENTRY_PARAM arg) { const USHORT flag = (USHORT) arg; while (!INET_shutting_down) { if (WaitForSingleObject(forkEvent, INFINITE) != WAIT_OBJECT_0) break; while (!INET_shutting_down) { SOCKET s = 0; { // scope Firebird::MutexLockGuard forkGuard(forkMutex); if (!forkSockets || forkSockets->getCount() == 0) break; s = (*forkSockets)[0]; forkSockets->remove((size_t) 0); } fork(s, flag); SOCLOSE(s); } } return 0; } #endif namespace { in_addr config_address; class GetAddress { public: static void init() { const char* config_option = Config::getRemoteBindAddress(); if (config_option) { int n = get_host_address(config_option, &config_address, 1); if (n != 1) { // In case when config option is given with error, // bind to loopback interface only config_address.s_addr = htonl(INADDR_LOOPBACK); // log warning if (n == 0) { gds__log("Wrong RemoteBindAddress '%s' in firebird.conf - " "binding to loopback interface", config_option); } else { gds__log("Host '%s' resolves to multiple interfaces - " "binding to loopback interface", config_option); } } } else // use default to listen all { config_address.s_addr = INADDR_ANY; } } static void cleanup() { } }; } static in_addr get_bind_address() { /************************************** * * g e t _ b i n d _ a d d r e s s * ************************************** * * Functional description * Return local address to bind sockets to. * **************************************/ static Firebird::InitMutex instance; instance.init(); return config_address; } #ifdef WIN_NT // Windows does not have an inet_aton function. bool inet_aton(const char* name, in_addr* address) { address->s_addr = inet_addr(name); return address->s_addr != INADDR_NONE; } #endif static int get_host_address(const char* name, in_addr* const host_addr_arr, const int arr_size) { /************************************** * * g e t _ h o s t _ a d d r e s s * ************************************** * * Functional description * Fills array with addresses up to arr_size (must be at least 1). * Returns the required number of elements in array to be able to store * all host addresses (may be less, equal or greater than arr_size). * **************************************/ if (inet_aton(name, &host_addr_arr[0])) { return 1; } const hostent* host = gethostbyname(name); // On Windows NT/9x, gethostbyname can only accomodate // 1 call at a time. In this case it returns the error // WSAEINPROGRESS. On UNIX systems, this call may not succeed // because of a temporary error. In this case, it returns // h_error set to TRY_AGAIN. When these errors occur, // retry the operation a few times. // NOTE: This still does not guarantee success, but helps. if (!host) { for (int retry = 0; H_ERRNO == INET_RETRY_ERRNO && retry < INET_RETRY_CALL; retry++) { if ( (host = gethostbyname(name)) ) break; } } // We can't work with other types for now. Maybe AF_NETBIOS for MS, too? if (host && host->h_addrtype == AF_INET) { const in_addr* const* list = reinterpret_cast(host->h_addr_list); int i = 0; while (list[i] != NULL) { if (i < arr_size) { host_addr_arr[i] = *list[i]; } ++i; } return i; } // give up return 0; } //____________________________________________________________ // // Copy an array of p_cnct::p_cnct_repeat. // static void copy_p_cnct_repeat_array( p_cnct::p_cnct_repeat* pDest, const p_cnct::p_cnct_repeat* pSource, size_t nEntries) { for (size_t i = 0; i < nEntries; ++i) { pDest[i] = pSource[i]; } } static rem_port* receive( rem_port* main_port, PACKET * packet) { /************************************** * * r e c e i v e * ************************************** * * Functional description * Receive a message from a port or clients of a port. If the process * is a server and a connection request comes in, generate a new port * block for the client. * **************************************/ // loop as long as we are receiving dummy packets, just // throwing them away--note that if we are a server we won't // be receiving them, but it is better to check for them at // this level rather than try to catch them in all places where // this routine is called do { if (!xdr_protocol(&main_port->port_receive, packet)) { packet->p_operation = main_port->port_flags & PORT_partial_data ? op_partial : op_exit; main_port->port_flags &= ~PORT_partial_data; if (packet->p_operation == op_exit) { main_port->port_state = rem_port::BROKEN; } break; } #ifdef DEBUG { static ULONG op_rec_count = 0; op_rec_count++; if (INET_trace & TRACE_operations) { fprintf(stdout, "%04lu: OP Recd %5lu opcode %d\n", inet_debug_timer(), op_rec_count, packet->p_operation); fflush(stdout); } } #endif } while (packet->p_operation == op_dummy); return main_port; } static bool select_multi(rem_port* main_port, UCHAR* buffer, SSHORT bufsize, SSHORT* length, RemPortPtr& port) { /************************************** * * s e l e c t _ m u l t i * ************************************** * * Functional description * Receive an IP packet from a port or clients of a port. * Used only by the multiclient server on main server's port. * If a connection request comes in, generate a new port * block for the client. * **************************************/ for (;;) { select_port(main_port, &INET_select, port); if (port == main_port && (port->port_server_flags & SRVR_multi_client)) { if (INET_shutting_down) { if (main_port->port_state != rem_port::BROKEN) { main_port->port_state = rem_port::BROKEN; const SOCKET s = main_port->port_handle; shutdown(s, 2); SOCLOSE(s); } } else if (port = select_accept(main_port)) { if (!packet_receive(port, buffer, bufsize, length)) { *length = 0; } return (*length) ? true : false; } continue; } if (port) { if (port->port_dummy_timeout < 0) { port->port_dummy_timeout = port->port_dummy_packet_interval; if (port->port_flags & PORT_async || port->port_protocol < PROTOCOL_VERSION8) { continue; } *length = 0; return true; } if (!packet_receive(port, buffer, bufsize, length)) { if (port->port_flags & PORT_disconnect) { continue; } *length = 0; } return (*length) ? true : false; } if (!select_wait(main_port, &INET_select)) { port = NULL; return false; } } } static rem_port* select_accept( rem_port* main_port) { /************************************** * * s e l e c t _ a c c e p t * ************************************** * * Functional description * Accept a new connection request. * **************************************/ struct sockaddr_in address; rem_port* const port = alloc_port(main_port); socklen_t l = sizeof(address); inet_ports->registerPort(port); port->port_handle = accept(main_port->port_handle, (struct sockaddr*) &address, &l); if (port->port_handle == INVALID_SOCKET) { inet_error(port, "accept", isc_net_connect_err, INET_ERRNO); disconnect(port); return 0; } int optval = 1; setsockopt(port->port_handle, SOL_SOCKET, SO_KEEPALIVE, (SCHAR*) &optval, sizeof(optval)); port->port_flags |= PORT_server; if (main_port->port_server_flags & SRVR_thread_per_port) { port->port_server_flags = (SRVR_server | SRVR_inet | SRVR_thread_per_port); return port; } return 0; } static void select_port(rem_port* main_port, slct_t* selct, RemPortPtr& port) { /************************************** * * s e l e c t _ p o r t * ************************************** * * Functional description * Select a descriptor that is ready to read * and return the port block. Additionally, * check if a port's keepalive timer has * expired and return the port block so that * a keepalive packet can be queued. Return * NULL if none are active. * **************************************/ Firebird::MutexLockGuard guard(port_mutex); for (port = main_port; port; port = port->port_next) { const SOCKET n = port->port_handle; #ifdef WIN_NT const int ok = FD_ISSET(n, &selct->slct_fdset); #else if (n < 0 || n >= FD_SETSIZE) { return; } const int ok = n < selct->slct_width && FD_ISSET(n, &selct->slct_fdset); #endif if (ok) { port->port_dummy_timeout = port->port_dummy_packet_interval; FD_CLR(n, &selct->slct_fdset); --selct->slct_count; return; } if (port->port_dummy_timeout < 0) { return; } } } static bool select_wait( rem_port* main_port, slct_t* selct) { /************************************** * * s e l e c t _ w a i t * ************************************** * * Functional description * Select interesting descriptors from * port blocks and wait for something * to read from them. * **************************************/ struct timeval timeout; bool checkPorts = false; for (;;) { selct->slct_count = selct->slct_width = 0; FD_ZERO(&selct->slct_fdset); bool found = false; // Use the time interval between select() calls to expire // keepalive timers on all ports. time_t delta_time; if (selct->slct_time) { delta_time = time(NULL) - selct->slct_time; selct->slct_time += delta_time; } else { delta_time = 0; selct->slct_time = time(NULL); } { // port_mutex scope Firebird::MutexLockGuard guard(port_mutex); for (rem_port* port = main_port; port; port = port->port_next) { if (port->port_state == rem_port::PENDING) { // Adjust down the port's keepalive timer. if (port->port_dummy_packet_interval) { port->port_dummy_timeout -= delta_time; } if (checkPorts) { // select() returned EBADF\WSAENOTSOCK - we have a broken socket // in current fdset. Search and return it to caller to close // broken connection correctly struct linger lngr; socklen_t optlen = sizeof(lngr); const bool badSocket = #ifdef WIN_NT false; #else (port->port_handle < 0 || port->port_handle >= FD_SETSIZE); #endif if (badSocket || getsockopt(port->port_handle, SOL_SOCKET, SO_LINGER, (SCHAR*) &lngr, &optlen) != 0) { if (badSocket || INET_ERRNO == NOTASOCKET) { // not a socket, strange ! gds__log("INET/select_wait: found \"not a socket\" socket : %u", (unsigned) port->port_handle); // this will lead to receive() which will break bad connection selct->slct_count = selct->slct_width = 0; FD_ZERO(&selct->slct_fdset); if (!badSocket) { FD_SET(port->port_handle, &selct->slct_fdset); #ifdef WIN_NT ++selct->slct_width; #else selct->slct_width = port->port_handle + 1; #endif } return true; } } } // if process is shuting down - don't listen on main port if (!INET_shutting_down || port != main_port) { FD_SET(port->port_handle, &selct->slct_fdset); #ifdef WIN_NT ++selct->slct_width; #else selct->slct_width = MAX(selct->slct_width, port->port_handle + 1); #endif found = true; } } } checkPorts = false; } // port_mutex scope if (!found) { if (!INET_shutting_down && (main_port->port_server_flags & SRVR_multi_client)) { gds__log("INET/select_wait: client rundown complete, server exiting"); } return false; } for (;;) { // Before waiting for incoming packet, check for server shutdown if (tryStopMainThread && tryStopMainThread()) { // this is not server port any more main_port->port_server_flags &= ~SRVR_multi_client; return false; } // Some platforms change the timeout in the select call. // Reset timeout for each iteration to avoid problems. timeout.tv_sec = SELECT_TIMEOUT; timeout.tv_usec = 0; #ifdef WIN_NT selct->slct_count = select(FD_SETSIZE, &selct->slct_fdset, NULL, NULL, &timeout); #else selct->slct_count = select(selct->slct_width, &selct->slct_fdset, NULL, NULL, &timeout); #endif const int inetErrNo = INET_ERRNO; //if (INET_shutting_down) { // return false; //} if (selct->slct_count != -1) { // if selct->slct_count is zero it means that we timed out of // select with nothing to read or accept, so clear the fd_set // bit as this value is undefined on some platforms (eg. HP-UX), // when the select call times out. Once these bits are cleared // they can be used in select_port() if (selct->slct_count == 0) { Firebird::MutexLockGuard guard(port_mutex); for (rem_port* port = main_port; port; port = port->port_next) { FD_CLR(port->port_handle, &selct->slct_fdset); } } return true; } if (INTERRUPT_ERROR(inetErrNo)) continue; if (inetErrNo == NOTASOCKET) { checkPorts = true; break; } gds__log("INET/select_wait: select failed, errno = %d", inetErrNo); return false; } // for (;;) } } static int send_full( rem_port* port, PACKET * packet) { /************************************** * * s e n d _ f u l l * ************************************** * * Functional description * Send a packet across a port to another process. * **************************************/ if (!xdr_protocol(&port->port_send, packet)) return FALSE; #ifdef DEBUG { // scope static ULONG op_sent_count = 0; op_sent_count++; if (INET_trace & TRACE_operations) { fprintf(stdout, "%05lu: OP Sent %5lu opcode %d\n", inet_debug_timer(), op_sent_count, packet->p_operation); fflush(stdout); } } // end scope #endif return inet_write(&port->port_send /*, TRUE*/); } static int send_partial( rem_port* port, PACKET * packet) { /************************************** * * s e n d _ p a r t i a l * ************************************** * * Functional description * Send a packet across a port to another process. * **************************************/ #ifdef DEBUG { // scope static ULONG op_sentp_count = 0; op_sentp_count++; if (INET_trace & TRACE_operations) { fprintf(stdout, "%05lu: OP Sent %5lu opcode %d (partial)\n", inet_debug_timer(), op_sentp_count, packet->p_operation); fflush(stdout); } } // end scope #endif return xdr_protocol(&port->port_send, packet); } static int xdrinet_create(XDR* xdrs, rem_port* port, UCHAR* buffer, USHORT length, enum xdr_op x_op) { /************************************** * * x d r i n e t _ c r e a t e * ************************************** * * Functional description * Initialize an XDR stream for Apollo mailboxes. * **************************************/ xdrs->x_public = (caddr_t) port; xdrs->x_base = xdrs->x_private = reinterpret_cast(buffer); xdrs->x_handy = length; xdrs->x_ops = (xdr_t::xdr_ops*) &inet_ops; xdrs->x_op = x_op; return TRUE; } #ifdef HAVE_SETITIMER static void alarm_handler( int x) { /************************************** * * a l a r m _ h a n d l e r * ************************************** * * Functional description * Handle an alarm clock interrupt. If we were waiting on * a semaphone, zap it. * **************************************/ } #endif static XDR_INT inet_destroy( XDR*) { /************************************** * * i n e t _ d e s t r o y * ************************************** * * Functional description * Destroy a stream. A no-op. * **************************************/ return (XDR_INT) 0; } static void inet_gen_error(rem_port* port, const Arg::StatusVector& v) { /************************************** * * i n e t _ g e n _ e r r o r * ************************************** * * Functional description * An error has occurred. Mark the port as broken. * Format the status vector if there is one and * save the status vector strings in a permanent place. * **************************************/ port->port_state = rem_port::BROKEN; const char* node_name = port->port_connection ? port->port_connection->str_data : "(unknown)"; Arg::Gds error(isc_network_error); error << Arg::Str(node_name) << v; ISC_STATUS* status_vector = NULL; if (port->port_context != NULL) { status_vector = port->port_context->rdb_status_vector; } if (status_vector == NULL) { status_vector = port->port_status_vector; } if (status_vector != NULL) { error.copyTo(status_vector); REMOTE_save_status_strings(status_vector); } } static bool_t inet_getbytes( XDR* xdrs, SCHAR* buff, u_int count) { /************************************** * * i n e t _ g e t b y t e s * ************************************** * * Functional description * Get a bunch of bytes from a memory stream if it fits. * **************************************/ #ifdef REM_SERVER const rem_port* port = (rem_port*) xdrs->x_public; if ((port->port_flags & PORT_server) && !(port->port_server_flags & SRVR_debug)) { return REMOTE_getbytes(xdrs, buff, count); } #endif SLONG bytecount = count; // Use memcpy to optimize bulk transfers. while (bytecount > (SLONG) sizeof(ISC_QUAD)) { if (xdrs->x_handy >= bytecount) { memcpy(buff, xdrs->x_private, bytecount); xdrs->x_private += bytecount; xdrs->x_handy -= bytecount; return TRUE; } if (xdrs->x_handy > 0) { memcpy(buff, xdrs->x_private, xdrs->x_handy); xdrs->x_private += xdrs->x_handy; buff += xdrs->x_handy; bytecount -= xdrs->x_handy; xdrs->x_handy = 0; } if (!inet_read(xdrs)) return FALSE; } // Scalar values and bulk transfer remainder fall thru // to be moved byte-by-byte to avoid memcpy setup costs. if (!bytecount) return TRUE; if (xdrs->x_handy >= bytecount) { xdrs->x_handy -= bytecount; while (bytecount--) *buff++ = *xdrs->x_private++; return TRUE; } while (--bytecount >= 0) { if (!xdrs->x_handy && !inet_read(xdrs)) return FALSE; *buff++ = *xdrs->x_private++; --xdrs->x_handy; } return TRUE; } static bool_t inet_getlong( XDR* xdrs, SLONG* lp) { /************************************** * * i n e t _ g e t l o n g * ************************************** * * Functional description * Fetch a longword into a memory stream if it fits. * **************************************/ SLONG l; if (!(*xdrs->x_ops->x_getbytes) (xdrs, reinterpret_cast(&l), 4)) return FALSE; *lp = ntohl(l); return TRUE; } static u_int inet_getpostn( XDR* xdrs) { /************************************** * * i n e t _ g e t p o s t n * ************************************** * * Functional description * Get the current position (which is also current length) from stream. * **************************************/ return (u_int) (xdrs->x_private - xdrs->x_base); } static caddr_t inet_inline( XDR* xdrs, u_int bytecount) { /************************************** * * i n e t _ i n l i n e * ************************************** * * Functional description * Return a pointer to somewhere in the buffer. * **************************************/ if (bytecount > (u_int) xdrs->x_handy) return FALSE; return xdrs->x_base + bytecount; } static void inet_error(rem_port* port, const TEXT* function, ISC_STATUS operation, int status) { /************************************** * * i n e t _ e r r o r * ************************************** * * Functional description * An I/O error has occurred. Call * inet_gen_error with the appropriate args * to format the status vector if any. * **************************************/ if (status) { if (port->port_state != rem_port::BROKEN) { gds__log("INET/inet_error: %s errno = %d", function, status); } inet_gen_error(port, Arg::Gds(operation) << SYS_ERR(status)); } else { // No status value, just format the basic arguments. inet_gen_error(port, Arg::Gds(operation)); } } static bool_t inet_putbytes( XDR* xdrs, const SCHAR* buff, u_int count) { /************************************** * * i n e t _ p u t b y t e s * ************************************** * * Functional description * Put a bunch of bytes to a memory stream if it fits. * **************************************/ SLONG bytecount = count; // Use memcpy to optimize bulk transfers. while (bytecount > (SLONG) sizeof(ISC_QUAD)) { if (xdrs->x_handy >= bytecount) { memcpy(xdrs->x_private, buff, bytecount); xdrs->x_private += bytecount; xdrs->x_handy -= bytecount; return TRUE; } if (xdrs->x_handy > 0) { memcpy(xdrs->x_private, buff, xdrs->x_handy); xdrs->x_private += xdrs->x_handy; buff += xdrs->x_handy; bytecount -= xdrs->x_handy; xdrs->x_handy = 0; } if (!inet_write(xdrs /*, 0*/)) return FALSE; } // Scalar values and bulk transfer remainder fall thru // to be moved byte-by-byte to avoid memcpy setup costs. if (!bytecount) return TRUE; if (xdrs->x_handy >= bytecount) { xdrs->x_handy -= bytecount; while (bytecount--) *xdrs->x_private++ = *buff++; return TRUE; } while (--bytecount >= 0) { if (xdrs->x_handy <= 0 && !inet_write(xdrs /*, 0*/)) return FALSE; --xdrs->x_handy; *xdrs->x_private++ = *buff++; } return TRUE; } static bool_t inet_putlong( XDR* xdrs, const SLONG* lp) { /************************************** * * i n e t _ p u t l o n g * ************************************** * * Functional description * Fetch a longword into a memory stream if it fits. * **************************************/ const SLONG l = htonl(*lp); return (*xdrs->x_ops->x_putbytes) (xdrs, reinterpret_cast(&l), 4); } static bool_t inet_read( XDR* xdrs) { /************************************** * * i n e t _ r e a d * ************************************** * * Functional description * Read a buffer full of data. If we receive a bad packet, * send the moral equivalent of a NAK and retry. ACK all * partial packets. Don't ACK the last packet -- the next * message sent will handle this. * **************************************/ rem_port* port = (rem_port*) xdrs->x_public; char* p = xdrs->x_base; const char* const end = p + INET_remote_buffer; // If buffer is not completely empty, slide down what's left if (xdrs->x_handy > 0) { memmove(p, xdrs->x_private, xdrs->x_handy); p += xdrs->x_handy; } while (true) { SSHORT length = end - p; if (!packet_receive(port, reinterpret_cast(p), length, &length)) { return FALSE; } if (length >= 0) { p += length; break; } p -= length; if (!packet_send(port, 0, 0)) return FALSE; } xdrs->x_handy = (int) ((SCHAR *) p - xdrs->x_base); xdrs->x_private = xdrs->x_base; return TRUE; } static bool_t inet_setpostn( XDR* xdrs, u_int bytecount) { /************************************** * * i n e t _ s e t p o s t n * ************************************** * * Functional description * Set the current position (which is also current length) from stream. * **************************************/ if (bytecount > (u_int) xdrs->x_handy) return FALSE; xdrs->x_private = xdrs->x_base + bytecount; return TRUE; } static rem_port* inet_try_connect(PACKET* packet, Rdb* rdb, const Firebird::PathName& file_name, const TEXT* node_name, ISC_STATUS* status_vector, Firebird::ClumpletReader& dpb) { /************************************** * * i n e t _ t r y _ c o n n e c t * ************************************** * * Functional description * Given a packet with formatted protocol infomation, * set header information and try the connection. * * If a connection is established, return a port block, otherwise * return NULL. * **************************************/ P_CNCT* cnct = &packet->p_cnct; packet->p_operation = op_connect; cnct->p_cnct_operation = op_attach; cnct->p_cnct_cversion = CONNECT_VERSION2; cnct->p_cnct_client = ARCHITECTURE; cnct->p_cnct_file.cstr_length = file_name.length(); cnct->p_cnct_file.cstr_address = reinterpret_cast(file_name.c_str()); // If we can't talk to a server, punt. Let somebody else generate // an error. status_vector will have the network error info. rem_port* port = INET_connect(node_name, packet, status_vector, FALSE, &dpb); if (!port) { delete rdb; return NULL; } // Get response packet from server. rdb->rdb_port = port; port->port_context = rdb; if (!port->receive(packet)) { inet_error(port, "receive in try_connect", isc_net_connect_err, INET_ERRNO); disconnect(port); delete rdb; return NULL; } return port; } static bool_t inet_write( XDR* xdrs /*, bool_t end_flag*/) { /************************************** * * i n e t _ w r i t e * ************************************** * * Functional description * Write a buffer full of data. * Obsolete: If the end_flag isn't set, indicate * that the buffer is a fragment, and reset the XDR for another buffer * load. * **************************************/ // Encode the data portion of the packet rem_port* port = (rem_port*) xdrs->x_public; const char* p = xdrs->x_base; SSHORT length = xdrs->x_private - p; // Send data in manageable hunks. If a packet is partial, indicate // that with a negative length. A positive length marks the end. //p = xdrs->x_base; redundant while (length) { const SSHORT l = (SSHORT) MIN(length, INET_remote_buffer); length -= l; if (!packet_send(port, p, (SSHORT) (length ? -l : l))) return FALSE; p += l; } xdrs->x_private = xdrs->x_base; xdrs->x_handy = INET_remote_buffer; return TRUE; #ifdef PIGGYBACK // CVC: Screwed logic here: if I initialize l2 to zero, nothing useful executes. SCHAR aux_buffer[BUFFER_SIZE]; SSHORT l2 = 0; #error Assign l2 some meaningful value before running this. // If the other end has not piggy-backed the next packet, we're done. if (!l2) return TRUE; // We've got a piggy-backed response. If the packet is partial, // send an ACK for part we did receive. char* p2 = aux_buffer; while (l2 < 0) { if (!packet_send(port, 0, 0)) return FALSE; p2 -= l2; length = aux_buffer + sizeof(aux_buffer) - p2; if (!packet_receive(port, p2, length, &l2)) { p2 += l2; continue; } } length = p2 - aux_buffer + l2; // Now we're got a encode glump ready to stuff into the read buffer. // Unfortunately, if we just add it to the read buffer, we will shortly // overflow the buffer. To avoid this, "scrumpf down" the active bits // in the read buffer, then add out stuff at the end. xdrs = &port->port_receive; p2 = xdrs->x_base; if (xdrs->x_handy && p2 != xdrs->x_private) { memmove(p2, xdrs->x_private, xdrs->x_handy); } p2 += xdrs->x_handy; xdrs->x_private = xdrs->x_base; // xdrs->x_handy += JAP_decode (aux_buffer, length, p2); return TRUE; #endif } #ifdef DEBUG static void packet_print(const TEXT* string, const UCHAR* packet, int length, ULONG counter) { /************************************** * * p a c k e t _ p r i n t * ************************************** * * Functional description * Print a summary of packet. * **************************************/ int sum = 0; for (int l = length; l > 0; --l) sum += *packet++; fprintf(stdout, "%05lu: PKT %s\t(%lu): length = %4d, checksum = %d\n", inet_debug_timer(), string, counter, length, sum); fflush(stdout); } #endif static bool packet_receive(rem_port* port, UCHAR* buffer, SSHORT buffer_length, SSHORT* length) { /************************************** * * p a c k e t _ r e c e i v e * ************************************** * * Functional description * Receive a packet and pass on it's goodness. If it's good, * return true and the reported length of the packet, and update * the receive sequence number. If it's bad, return false. If it's * a duplicate message, just ignore it. * **************************************/ if (port->port_flags & PORT_disconnect) { return false; } timeval timeout; timeout.tv_usec = 0; timeval* time_ptr = NULL; if (port->port_protocol == 0) { // If the protocol is 0 we are still in the process of establishing // a connection. Add a time out to the wait. timeout.tv_sec = port->port_connect_timeout; time_ptr = &timeout; } else if (port->port_protocol >= PROTOCOL_VERSION8 && port->port_dummy_packet_interval > 0) { // Set the time interval for sending dummy packets to the client timeout.tv_sec = port->port_dummy_packet_interval; time_ptr = &timeout; } // On Linux systems (and possibly others too) select will eventually // change timout values so save it here for later reuse. // Thanks to Brad Pepers who reported this bug FSG 3 MAY 2001 const timeval savetime = timeout; const SOCKET ph = port->port_handle; // Unsed to send a dummy packet, but too big to be defined in the loop. PACKET packet; int n = 0; int inetErrNo; for (;;) { // Implement an error-detection protocol to ensure that the client // is still there. Use the select() call with a timeout to wait on // the connection for an incoming packet. If none comes within a // suitable time interval, write a dummy packet on the connection. // If the client is not there, an error will be returned on the write. // If the client is there, the dummy packet will be ignored by all // InterBase clients V4 or greater. This protocol will detect when // clients are lost abnormally through reboot or network disconnect. // Don't send op_dummy packets on aux port; the server won't // read them because it only writes to aux ports. if ( !(port->port_flags & PORT_async) ) { fd_set slct_fdset; FD_ZERO(&slct_fdset); FD_SET(ph, &slct_fdset); int slct_count; for (;;) { #if (defined WIN_NT) slct_count = select(FD_SETSIZE, &slct_fdset, NULL, NULL, time_ptr); #else slct_count = select(port->port_handle + 1, &slct_fdset, NULL, NULL, time_ptr); #endif inetErrNo = INET_ERRNO; // restore original timeout value FSG 3 MAY 2001 timeout = savetime; if (slct_count != -1 || !INTERRUPT_ERROR(inetErrNo)) { break; } } if (slct_count == -1) { if (!(port->port_flags & PORT_disconnect)) { inet_error(port, "select in packet_receive", isc_net_read_err, inetErrNo); } return false; } if (!slct_count && port->port_protocol >= PROTOCOL_VERSION8) { #ifdef DEBUG if (INET_trace & TRACE_operations) { fprintf(stdout, "%05lu: OP Sent: op_dummy\n", inet_debug_timer()); fflush(stdout); } #endif packet.p_operation = op_dummy; if (!send_full(port, &packet)) { return false; } continue; } if (!slct_count && port->port_protocol == 0) { return false; } } n = recv(port->port_handle, reinterpret_cast(buffer), buffer_length, 0); inetErrNo = INET_ERRNO; if (n != -1 || !INTERRUPT_ERROR(inetErrNo)) break; } if (n <= 0 && (port->port_flags & PORT_disconnect)) { return false; } if (n == -1) { inet_error(port, "read", isc_net_read_err, inetErrNo); return false; } if (!n) { inet_error(port, "read end_of_file", isc_net_read_err, inetErrNo); return false; } #ifdef DEBUG { // scope INET_count_recv++; INET_bytes_recv += n; if (INET_trace & TRACE_packets) packet_print("receive", buffer, n, INET_count_recv); INET_force_error--; if (INET_force_error == 0) { INET_force_error = 1; inet_error(port, "simulated error - read", isc_net_read_err, 0); return false; } } // end scope #endif *length = n; return true; } static bool packet_send( rem_port* port, const SCHAR* buffer, SSHORT buffer_length) { /************************************** * * p a c k e t _ s e n d * ************************************** * * Functional description * Send some data on it's way. * **************************************/ const char* data = buffer; SSHORT length = buffer_length; while (length) { #ifdef DEBUG if (INET_trace & TRACE_operations) { fprintf(stdout, "Before Send\n"); fflush(stdout); } #endif SSHORT n = send(port->port_handle, data, length, FB_SEND_FLAGS); #ifdef DEBUG if (INET_trace & TRACE_operations) { fprintf(stdout, "After Send n is %d\n", n); fflush(stdout); } #endif if (n == length) { break; } if (n == -1) { if (INTERRUPT_ERROR(INET_ERRNO)) { continue; } inet_error(port, "send", isc_net_write_err, INET_ERRNO); return false; } data += n; length -= n; } #ifdef HAVE_SETITIMER struct itimerval internal_timer, client_timer; struct sigaction internal_handler, client_handler; #endif // HAVE_SETITIMER if ((port->port_flags & PORT_async) && !(port->port_flags & PORT_no_oob)) { int count = 0; SSHORT n; int inetErrNo = 0; const char* b = buffer; while ((n = send(port->port_handle, b, 1, MSG_OOB | FB_SEND_FLAGS)) == -1 && (INET_ERRNO == ENOBUFS || INTERRUPT_ERROR(INET_ERRNO))) { inetErrNo = INET_ERRNO; if (count++ > 20) { break; } #ifndef HAVE_SETITIMER #ifdef WIN_NT SleepEx(50, TRUE); #else sleep(1); #endif } // end of while() loop for systems without setitimer. #else // HAVE_SETITIMER if (count == 1) { // Wait in a loop until the lock becomes available internal_timer.it_interval.tv_sec = 0; internal_timer.it_interval.tv_usec = 0; internal_timer.it_value.tv_sec = 0; internal_timer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &internal_timer, &client_timer); internal_handler.sa_handler = alarm_handler; sigemptyset(&internal_handler.sa_mask); internal_handler.sa_flags = SA_RESTART; sigaction(SIGALRM, &internal_handler, &client_handler); } internal_timer.it_value.tv_sec = 0; internal_timer.it_value.tv_usec = 50000; setitimer(ITIMER_REAL, &internal_timer, NULL); pause(); } // end of while() loop for systems with setitimer if (count) { // Restore user's outstanding alarm request and handler internal_timer.it_value.tv_sec = 0; internal_timer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &internal_timer, NULL); sigaction(SIGALRM, &client_handler, NULL); setitimer(ITIMER_REAL, &client_timer, NULL); } #endif // HAVE_SETITIMER if (n == -1) { inet_error(port, "send/oob", isc_net_write_err, inetErrNo); return false; } } #ifdef DEBUG { // scope INET_count_send++; INET_bytes_send += buffer_length; if (INET_trace & TRACE_packets) packet_print("send", (const UCHAR*) buffer, buffer_length, INET_count_send); INET_force_error--; if (INET_force_error == 0) { INET_force_error = 1; inet_error(port, "simulated error - send", isc_net_write_err, 0); return false; } } // end scope #endif return true; } static bool setNoNagleOption(rem_port* port) { /************************************** * * s e t N o N a g l e O p t i o n * ************************************** * * Functional description * Set TCP_NODELAY, return false * in case of unexpected error * **************************************/ if (Config::getTcpNoNagle()) { int optval = TRUE; int n = setsockopt(port->port_handle, IPPROTO_TCP, TCP_NODELAY, (SCHAR*) &optval, sizeof(optval)); if (n == -1) { return false; } } return true; } void setStopMainThread(FPTR_INT func) { /************************************** * * s e t S t o p M a i n T h r e a d * ************************************** * * Functional description * Set function called by main thread * in order to check for shutdown. * **************************************/ tryStopMainThread = func; }