Logo Search packages:      
Sourcecode: firebird3.0 version File versions  Download package

inet.cpp

/*
 *    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 <stdio.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "../common/common.h"
#include "../common/file_params.h"
#include <stdarg.h>

#include "../common/classes/timestamp.h"
#include "../common/classes/init.h"
#include "../common/ThreadStart.h"

#ifdef HAVE_PWD_H
#include <pwd.h>
#endif

#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#ifdef HAVE_GRP_H
#include <grp.h>
#endif

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> // for socket()
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif



#ifdef      WIN_NT
#define FD_SETSIZE 1024
#endif

#ifndef WIN_NT
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>

#if defined(HAVE_POLL_H)
#include <poll.h>
#elif defined(HAVE_SYS_SELECT_H)
#include <sys/select.h>
#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 "../yvalve/gds_proto.h"
#include "../common/isc_proto.h"
#include "../common/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 <fcntl.h>
#include <process.h>
#include <signal.h>
#include "../utilities/install/install_nt.h"

#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 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

static void SOCLOSE(SOCKET& socket)
{
      if (socket != INVALID_SOCKET)
      {
#ifdef WIN_NT
            closesocket(socket);
#else
            close(socket);
#endif
            socket = INVALID_SOCKET;
      }
}

// 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 <sys/timeb.h>
#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)

00236 class Select
{
#ifdef HAVE_POLL
private:
      static const int SEL_INIT_EVENTS = POLLIN;
      static const int SEL_CHECK_MASK = POLLIN;

      pollfd* getPollFd(int n)
      {
            pollfd* const end = slct_poll.end();
            for (pollfd* pf = slct_poll.begin(); pf < end; ++pf)
            {
                  if (n == pf->fd)
                  {
                        return pf;
                  }
            }

            return NULL;
      }

      static int compare(const void* a, const void* b)
      {
            // use C-cast here to be for sure compatible with libc
            return ((pollfd*) a)->fd - ((pollfd*) b)->fd;
      }
#endif

public:
#ifdef HAVE_POLL
      Select()
            : slct_time(0), slct_count(0), slct_poll(*getDefaultMemoryPool())
      { }
#else
      Select()
            : slct_time(0), slct_count(0), slct_width(0)
      {
            memset(&slct_fdset, 0, sizeof slct_fdset);
      }
#endif

      enum HandleState {SEL_BAD, SEL_DISCONNECTED, SEL_NO_DATA, SEL_READY};

      HandleState ok(const rem_port* port)
      {
            SOCKET n = port->port_handle;
#if defined(WIN_NT)
            return FD_ISSET(n, &slct_fdset) ? SEL_READY : SEL_NO_DATA;
#elif defined(HAVE_POLL)
            const pollfd* pf = getPollFd(n);
            if (pf)
                  return pf->events & SEL_CHECK_MASK ? SEL_READY : SEL_NO_DATA;
            return n < 0 ? (port->port_flags & PORT_disconnect ? SEL_DISCONNECTED : SEL_BAD) : SEL_NO_DATA;
#else
            if (n < 0 || n >= FD_SETSIZE)
                  return port->port_flags & PORT_disconnect ? SEL_DISCONNECTED : SEL_BAD;
            return (n < slct_width && FD_ISSET(n, &slct_fdset)) ? SEL_READY : SEL_NO_DATA;
#endif
      }

      void unset(SOCKET handle)
      {
#if defined(HAVE_POLL)
            pollfd* pf = getPollFd(handle);
            if (pf)
            {
                  pf->events = 0;
            }
#else
            FD_CLR(handle, &slct_fdset);
            --slct_count;
#endif
      }

      void set(SOCKET handle)
      {
#ifdef HAVE_POLL
            pollfd* pf = getPollFd(handle);
            if (pf)
            {
                  pf->events = SEL_INIT_EVENTS;
                  return;
            }
            pollfd f;
            f.fd = handle;
            f.events = SEL_INIT_EVENTS;
            slct_poll.push(f);
#else
            FD_SET(handle, &slct_fdset);
#ifdef WIN_NT
            ++slct_width;
#else
            slct_width = MAX(slct_width, handle + 1);
#endif // WIN_NT
#endif // HAVE_POLL
      }

      void clear()
      {
            slct_count = 0;
#if defined(HAVE_POLL)
            slct_poll.clear();
#else
            slct_width = 0;
            FD_ZERO(&slct_fdset);
#endif
      }

      int select(timeval* timeout)
      {
            bool hasRequest = false;

#ifdef HAVE_POLL
            pollfd* const end = slct_poll.end();
            for (pollfd* pf = slct_poll.begin(); pf < end; ++pf)
            {
                  pf->revents = pf->events;
                  if (pf->events & SEL_CHECK_MASK)
                        hasRequest = true;
            }

            if (!hasRequest)
            {
                  errno = NOTASOCKET;
                  return -1;
            }

            int milliseconds = timeout ? timeout->tv_sec * 1000 + timeout->tv_usec / 1000 : -1;
            slct_count = ::poll(slct_poll.begin(), slct_poll.getCount(), milliseconds);

            for (pollfd* pf = slct_poll.begin(); pf < end; ++pf)
                  pf->events = pf->revents;
#else
#ifdef WIN_NT
            slct_count = ::select(FD_SETSIZE, &slct_fdset, NULL, NULL, timeout);
#else
            slct_count = ::select(slct_width, &slct_fdset, NULL, NULL, timeout);
#endif // WIN_NT
#endif // HAVE_POLL

            return slct_count;
      }

      int getCount()
      {
            return slct_count;
      }

      time_t      slct_time;

private:
      int         slct_count;
#ifdef HAVE_POLL
      HalfStaticArray<pollfd, 8> slct_poll;
#else
      int         slct_width;
      fd_set      slct_fdset;
#endif
};

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*, const USHORT = 0);
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<Firebird::Mutex> 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<Firebird::Mutex> forkMutex;
static HANDLE forkEvent = INVALID_HANDLE_VALUE;
static bool forkThreadStarted = false;

typedef Firebird::Array<SOCKET> 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(bool, 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(bool, 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*,
                                                      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*, Select*, RemPortPtr&);
static bool       select_multi(rem_port*, UCHAR* buffer, SSHORT bufsize, SSHORT* length, RemPortPtr&);
static bool       select_wait(rem_port*, Select*);
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<Firebird::Mutex> init_mutex;
static volatile bool INET_initialized = false;
static volatile bool INET_shutting_down = false;
static Select INET_select;
static rem_port* inet_async_receive = NULL;


static Firebird::GlobalPtr<Firebird::Mutex> port_mutex;
static Firebird::GlobalPtr<PortsCleanup>  inet_ports;


rem_port* INET_analyze(const Firebird::PathName& file_name,
                              const TEXT* node_name,
                              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_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<UCHAR*>(&eff_gid), sizeof(eff_gid));
      }

      // Establish connection to server
      // 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 = (USHORT) 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),
            REMOTE_PROTOCOL(PROTOCOL_VERSION13, ptype_rpc, MAX_PTYPE, 5)
      };

      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, dpb);

      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 = (USHORT) 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, dpb);
      }

      if (packet->p_operation != op_accept)
      {
            disconnect(port);
            delete rdb;

            Arg::Gds(isc_connect_reject).raise();
      }

      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,
                                 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);
      REMOTE_get_timeout_params(port, dpb);

      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(true, port, Arg::Gds(isc_net_lookup_err) << Arg::Gds(isc_host_unknown));
            }
            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(false, port, Arg::Gds(isc_net_lookup_err) <<
                                       Arg::Gds(isc_service_unknown) << Arg::Str(protocol) << Arg::Str("tcp"));
            }                                   // 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(true, port, "socket", isc_net_connect_err, INET_ERRNO);
      }

      // If we're a host, just make the connection

    int n;

      if (packet)
      {
            if (! setNoNagleOption(port))
            {
                  inet_error(true, port, "setsockopt TCP_NODELAY", isc_net_connect_err, INET_ERRNO);
            }

            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(true, port, "connect", isc_net_connect_err, inetErrNo);
      }

      // 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(true, port, "setsockopt REUSE", isc_net_connect_listen_err, INET_ERRNO);
            }
#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(true, port, "setsockopt LINGER", isc_net_connect_listen_err, INET_ERRNO);
            }

            if (! setNoNagleOption(port))
            {
                  inet_error(true, port, "setsockopt TCP_NODELAY", isc_net_connect_listen_err, INET_ERRNO);
            }
      }

      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(true, port, "bind", isc_net_connect_listen_err, INET_ERRNO);
      }

      n = listen(port->port_handle, SOMAXCONN);

      if (n == -1)
      {
            inet_error(false, port, "listen", isc_net_connect_listen_err, INET_ERRNO);
      }

      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(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 | 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());

                  Thread::start(forkThread, (void*) flag, THREAD_medium);
            }
            forkSockets->add(s);
            SetEvent(forkEvent);
#else
            Firebird::MutexLockGuard guard(waitThreadMutex);
            if (! procCount++) {
                  Thread::start(waitThread, 0, THREAD_medium);
            }

            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)
{
/**************************************
 *
 *    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_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;
}

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 size_t 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<int>(ip[0]),
                  static_cast<int>(ip[1]),
                  static_cast<int>(ip[2]),
                  static_cast<int>(ip[3]) );
            port->port_address_str = REMOTE_make_string(addr_str.c_str());
      }

      return true;
}


static rem_port* alloc_port(rem_port* const parent, const USHORT flags)
{
/**************************************
 *
 *    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)
                  {
                        inet_error(false, parent, "WSAStartup", isc_net_init_error, wsaError);
                  }
                  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;
      port->port_flags = flags;

      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;

            Select slct;
            slct.set(port->port_channel);

            int inetErrNo = 0;

            while (true)
            {
                  slct.select(&timeout);
                  const int count = slct.getCount();
                  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;
                              int savedError = inetErrNo;
                              SOCLOSE(port->port_channel);
                              inet_error(false, port, "select", error_code, savedError);
                        }
                  }
            }

            const SOCKET n = accept(port->port_channel, (struct sockaddr*) &address, &l);
            inetErrNo = INET_ERRNO;

            if (n == INVALID_SOCKET)
            {
                  int savedError = inetErrNo;
                  SOCLOSE(port->port_channel);
                  inet_error(false, port, "accept", isc_net_event_connect_err, savedError);
            }

            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(false, 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)
      {
            int savedError = INET_ERRNO;
            SOCLOSE(n);
            inet_error(false, port, "socket", isc_net_event_connect_err, savedError);
      }
      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)
      {
            int savedError = INET_ERRNO;
            SOCLOSE(n);
            inet_error(false, port, "connect", isc_net_event_connect_err, savedError);
      }

      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(false, port, "socket", isc_net_event_listen_err, INET_ERRNO);
      }

#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(false, port, "setsockopt REUSE", isc_net_event_listen_err, INET_ERRNO);
      }
#endif

      if (bind(n, (struct sockaddr *) &address, sizeof(address)) < 0)
      {
            inet_error(false, port, "bind", isc_net_event_listen_err, INET_ERRNO);
      }

      socklen_t length = sizeof(address);

      if (getsockname(n, (struct sockaddr *) &address, &length) < 0)
      {
            inet_error(false, port, "getsockname", isc_net_event_listen_err, INET_ERRNO);
      }

      if (listen(n, 1) < 0)
      {
            inet_error(false, port, "listen", isc_net_event_listen_err, INET_ERRNO);
      }

    rem_port* const new_port = alloc_port(port->port_parent, PORT_async);
      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(false, port, "getsockname", isc_net_event_listen_err, INET_ERRNO);
      }
      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 (port->port_handle != INVALID_SOCKET)
      {
            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);

      SOCLOSE(port->port_handle);
      SOCLOSE(port->port_channel);

      port->release();

#ifdef DEBUG
      if (INET_trace & TRACE_summary)
      {
            fprintf(stdout, "INET_count_send = %u packets\n", INET_count_send);
            fprintf(stdout, "INET_bytes_send = %u bytes\n", INET_bytes_send);
            fprintf(stdout, "INET_count_recv = %u packets\n", INET_count_recv);
            fprintf(stdout, "INET_bytes_recv = %u 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;

      if (port->port_handle != INVALID_SOCKET)
      {
            shutdown(port->port_handle, 2);
            SOCLOSE(port->port_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 %"HANDLEFORMAT"@%"ULONGFORMAT, name, 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<GetAddress> 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<in_addr**>(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

#ifdef DEV_BUILD
      main_port->port_receive.x_client = !(main_port->port_flags & PORT_server);
#endif
      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, "%04u: OP Recd %5u 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;

                              shutdown(main_port->port_handle, 2);
                              SOCLOSE(main_port->port_handle);
                        }
                  }
                  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(true, port, "accept", isc_net_connect_err, INET_ERRNO);
      }

      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, Select* 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)
      {
            Select::HandleState result = selct->ok(port);
            selct->unset(port->port_handle);

            switch (result)
            {
            case Select::SEL_BAD:
                  if (port->port_state == rem_port::BROKEN)
                        continue;
                  return;

            case Select::SEL_DISCONNECTED:
                  continue;

            case Select::SEL_READY:
                  port->port_dummy_timeout = port->port_dummy_packet_interval;
                  return;

            default:
                  break;
            }

            if (port->port_dummy_timeout < 0)
                  return;
      }
}

static bool select_wait( rem_port* main_port, Select* 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->clear();
            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 &&
                              // don't wait on still listening (not connected) async port
                              !(port->port_handle == INVALID_SOCKET && (port->port_flags & PORT_async)))
                        {
                              // 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 : %"HANDLEFORMAT,
                                                             port->port_handle);

                                                // this will lead to receive() which will break bad connection
                                                selct->clear();
                                                if (!badSocket)
                                                {
                                                      selct->set(port->port_handle);
                                                }
                                                return true;
                                          }
                                    }
                              }

                              // if process is shuting down - don't listen on main port
                              if (!INET_shutting_down || port != main_port)
                              {
                                    selct->set(port->port_handle);
                                    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;

                  selct->select(&timeout);
                  const int inetErrNo = INET_ERRNO;

                  //if (INET_shutting_down) {
                  //    return false;
                  //}

                  if (selct->getCount() != -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->getCount() == 0)
                        {
                              Firebird::MutexLockGuard guard(port_mutex);
                              for (rem_port* port = main_port; port; port = port->port_next)
                              {
                                    selct->unset(port->port_handle);
                              }
                        }
                        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.
 *
 **************************************/

#ifdef DEV_BUILD
      port->port_send.x_client = !(port->port_flags & PORT_server);
#endif
      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, "%05u: OP Sent %5u 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, "%05u: OP Sent %5u opcode %d (partial)\n", inet_debug_timer(),
                        op_sentp_count, packet->p_operation);
                  fflush(stdout);
            }
      } // end scope
#endif

#ifdef DEV_BUILD
      port->port_send.x_client = !(port->port_flags & PORT_server);
#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<SCHAR*>(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(bool releasePort, 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;

      string node_name(port->port_connection ? port->port_connection->str_data : "(unknown)");

      if (releasePort)
      {
            disconnect(port);
      }

      Arg::Gds error(isc_network_error);
      error << Arg::Str(node_name) << v;
      error.raise();
}


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.
 *
 **************************************/
      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);
      }

      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<char*>(&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(bool releasePort, 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(releasePort, port, Arg::Gds(operation) << SYS_ERR(status));
      }
      else
      {
            // No status value, just format the basic arguments.
            inet_gen_error(releasePort, 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<const char*>(&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<UCHAR*>(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,
                                                  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 = (USHORT) file_name.length();
      cnct->p_cnct_file.cstr_address = reinterpret_cast<const UCHAR*>(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 = NULL;
      try
      {
            port = INET_connect(node_name, packet, FALSE, &dpb);
      }
      catch (const Exception&)
      {
            delete rdb;
            throw;
      }

      // Get response packet from server.

      rdb->rdb_port = port;
      port->port_context = rdb;
      if (!port->receive(packet))
      {
            rdb->rdb_port = NULL;
            delete rdb;
            inet_error(true, port, "receive in try_connect", isc_net_connect_err, INET_ERRNO);
      }

      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, "%05u:    PKT %s\t(%u): 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) )
            {
                  Select slct;
                  slct.set(ph);

                  int slct_count;
                  for (;;)
                  {
                        slct.select(time_ptr);
                        slct_count = slct.getCount();
                        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))
                        {
                              try
                              {
                                    inet_error(false, port, "select in packet_receive", isc_net_read_err, inetErrNo);
                              }
                              catch(const Exception&) { }
                        }
                        return false;
                  }

                  if (!slct_count && port->port_protocol >= PROTOCOL_VERSION8)
                  {
#ifdef DEBUG
                        if (INET_trace & TRACE_operations)
                        {
                              fprintf(stdout, "%05u: 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<char*>(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)
      {
            try
            {
                  inet_error(false, port, "read", isc_net_read_err, inetErrNo);
            }
            catch(const Exception&) { }
            return false;
      }

      if (!n)
      {
            port->port_state = rem_port::BROKEN;
            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;
                  try
                  {
                        inet_error(false, port, "simulated error - read", isc_net_read_err);
                  }
                  catch(const Exception&) { }
                  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;
                  }

                  try
                  {
                        inet_error(false, port, "send", isc_net_write_err, INET_ERRNO);
                  }
                  catch(const Exception&) { }
                  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)
            {
                  try
                  {
                        inet_error(false, port, "send/oob", isc_net_write_err, inetErrNo);
                  }
                  catch(const Exception&) { }
                  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;
                  try
                  {
                        inet_error(false, port, "simulated error - send", isc_net_write_err, 0);
                  }
                  catch(const Exception&) { }
                  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;
}

Generated by  Doxygen 1.6.0   Back to index