8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 18:03:04 +01:00
firebird-mirror/src/remote/inet.cpp

3456 lines
85 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Remote Interface/Server
* MODULE: inet.cpp
2001-05-23 15:26:42 +02:00
* 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-16 03:21:35 +01:00
*
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "EPSON" port
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "XENIX" port
2002-02-16 04:27:33 +01:00
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "IMP" port
2002-02-16 04:33:53 +01:00
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "NCR3000" port
2002-02-16 03:21:35 +01:00
*
* 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-29 03:45:09 +01:00
* 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port
*
2002-10-30 07:40:58 +01:00
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
* 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
2002-10-31 06:33:35 +01:00
* 2002.10.30 Sean Leyne - Code Cleanup, removed obsolete "SUN3_3" port
* 2005.04.01 Konstantin Kuznetsov - allow setting NoNagle option in Classic
*
2001-05-23 15:26:42 +02:00
*/
2004-12-17 07:22:37 +01:00
#include "firebird.h"
2004-04-29 00:36:29 +02:00
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
2001-05-23 15:26:42 +02:00
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "../jrd/common.h"
#include "../jrd/file_params.h"
#include <stdarg.h>
#include "../common/classes/timestamp.h"
#include "../common/classes/init.h"
2001-05-23 15:26:42 +02:00
2008-04-29 10:15:36 +02:00
#if !defined(WIN_NT)
#include "../jrd/ThreadStart.h"
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
2001-07-12 07:46:06 +02:00
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
2001-12-24 03:51:06 +01:00
#ifdef HAVE_GRP_H
#include <grp.h>
2001-12-24 03:51:06 +01:00
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> /* for socket() */
2001-07-12 07:46:06 +02:00
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
#define FD_SETSIZE 1024
#endif
2008-01-16 08:55:28 +01:00
#ifndef WIN_NT
#include <netinet/tcp.h>
#include <netinet/in.h>
2001-05-23 15:26:42 +02:00
#include <netdb.h>
2003-03-12 12:22:24 +01:00
#include <arpa/inet.h>
2008-04-29 10:15:36 +02:00
#include <sys/wait.h>
2008-01-16 08:55:28 +01:00
#endif // !WIN_NT
2001-05-23 15:26:42 +02:00
2007-11-12 16:18:49 +01:00
#if (defined DARWIN || defined HPUX)
2004-03-07 08:58:55 +01:00
extern "C" int innetgr(const char*, const char*, const char*, const char*);
2001-07-12 07:46:06 +02:00
#endif
2004-05-17 12:22:34 +02:00
const int INET_RETRY_CALL = 5;
2001-05-23 15:26:42 +02:00
#include "../remote/remote.h"
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/iberr.h"
#include "../common/thd.h"
2001-05-23 15:26:42 +02:00
#include "../remote/inet_proto.h"
#include "../remote/proto_proto.h"
#include "../remote/remot_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/os/isc_i_proto.h"
2001-05-23 15:26:42 +02:00
2002-12-06 13:34:43 +01:00
#include "../common/config/config.h"
2006-01-16 09:47:20 +01:00
#include "../common/utils_proto.h"
2004-12-26 14:48:01 +01:00
#include "../common/classes/ClumpletWriter.h"
2002-12-06 13:34:43 +01:00
2007-11-12 16:18:49 +01:00
#if (defined HPUX || defined SCO_UNIX)
2001-05-23 15:26:42 +02:00
extern int h_errno;
#endif
2004-05-17 12:22:34 +02:00
const char* PROXY_FILE = "/etc/gds_proxy";
const char* HOSTS_FILE = "/etc/hosts.equiv";
const USHORT MAX_PTYPE = ptype_lazy_send;
2004-05-17 12:22:34 +02:00
const char* GDS_HOSTS_FILE = "/etc/gds_hosts.equiv";
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
2004-05-19 00:01:53 +02:00
2001-05-23 15:26:42 +02:00
#include <fcntl.h>
#include <process.h>
#include <signal.h>
2003-07-15 04:43:36 +02:00
#include "../utilities/install/install_nt.h"
2001-05-23 15:26:42 +02:00
#define SOCLOSE closesocket
#define INET_RETRY_ERRNO WSAEINPROGRESS
#define INET_ADDR_IN_USE WSAEADDRINUSE
#define sleep(seconds) Sleep ((seconds) * 1000)
2007-06-06 10:08:36 +02:00
const int NOTASOCKET = WSAENOTSOCK;
2003-02-10 06:42:22 +01:00
2004-05-19 00:01:53 +02:00
#else // WIN_NT
2001-05-23 15:26:42 +02:00
#ifndef SOCKET
#define SOCKET int
#endif
#ifndef SOCLOSE
#define SOCLOSE close
#endif
#ifndef INET_ADDR_IN_USE
#define INET_ADDR_IN_USE EADDRINUSE
#endif
#ifndef INET_RETRY_ERRNO
#define INET_RETRY_ERRNO TRY_AGAIN
#endif
2007-06-06 10:08:36 +02:00
const int NOTASOCKET = EBADF;
2001-05-23 15:26:42 +02:00
2004-05-19 00:01:53 +02:00
#endif // WIN_NT
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
2001-05-23 15:26:42 +02:00
#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
2004-05-19 00:01:53 +02:00
//
//#define DEBUG 1
//
2001-05-23 15:26:42 +02:00
#ifdef DEBUG
#ifdef HAVE_SYS_TIMEB_H
# include <sys/timeb.h>
#endif
2004-05-18 18:32:07 +02:00
const int TRACE_packets = 1 << 0; /* bit 0 */
const int TRACE_operations = 1 << 1; /* bit 1 */
const int TRACE_summary = 1 << 2; /* bit 2 */
2001-05-23 15:26:42 +02:00
static int INET_trace = TRACE_summary | TRACE_packets | TRACE_operations;
2001-05-23 15:26:42 +02:00
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(void)
{
/**************************************
*
* 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;
2003-10-31 12:33:45 +01:00
GETTIMEOFDAY(&tv);
return (tv.tv_sec * 1000 + tv.tv_usec - INET_start_time);
#else
2001-05-23 15:26:42 +02:00
struct timeb now;
ftime(&now);
2001-05-23 15:26:42 +02:00
return (now.time * 1000 + now.millitm - INET_start_time);
#endif /* HAVE_GETTIMEOFDAY */
2001-05-23 15:26:42 +02:00
}
#endif /* DEBUG */
2001-05-23 15:26:42 +02:00
2004-05-17 12:22:34 +02:00
const SLONG MAX_DATA_LW = 1448; /* Low Water mark */
const SLONG MAX_DATA_HW = 32768; /* High Water mark */
const SLONG DEF_MAX_DATA = 8192;
2001-05-23 15:26:42 +02:00
2004-05-17 12:22:34 +02:00
const int MAXHOSTLEN = 64;
2001-05-23 15:26:42 +02:00
2004-05-17 12:22:34 +02:00
const int SELECT_TIMEOUT = 60; /* Dispatch thread select timeout (sec) */
2001-05-23 15:26:42 +02:00
2004-06-09 20:23:27 +02:00
typedef struct slct
2001-05-23 15:26:42 +02:00
{
int slct_width;
int slct_count;
2008-04-20 16:35:23 +02:00
time_t slct_time;
2001-05-23 15:26:42 +02:00
fd_set slct_fdset;
2004-06-09 20:23:27 +02:00
} SLCT;
2001-05-23 15:26:42 +02:00
2008-04-15 11:31:35 +02:00
static int accept_connection(rem_port*, const P_CNCT*);
#ifdef HAVE_SETITIMER
2001-05-23 15:26:42 +02:00
static void alarm_handler(int);
#endif
static rem_port* alloc_port(rem_port*);
static rem_port* aux_connect(rem_port*, PACKET*, t_event_ast);
static rem_port* aux_request(rem_port*, PACKET*);
#if !defined(WIN_NT)
2004-03-07 08:58:55 +01:00
static int check_host(rem_port*, TEXT*, const TEXT*, const struct passwd*);
static bool check_proxy(rem_port*, TEXT*, Firebird::string&);
2008-04-29 10:15:36 +02:00
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*);
2001-05-23 15:26:42 +02:00
static void exit_handler(void *);
#ifdef NO_FORK
static int fork(void);
#endif
#ifdef WIN_NT
static int fork(SOCKET, USHORT);
#endif
2003-03-19 14:14:09 +01:00
static in_addr get_bind_address();
2008-01-16 08:55:28 +01:00
static in_addr get_host_address(const Firebird::string& name,
in_addr* const host_addr_arr,
const int arr_size);
2001-05-23 15:26:42 +02:00
static void copy_p_cnct_repeat_array( p_cnct::p_cnct_repeat* pDest,
const p_cnct::p_cnct_repeat* pSource,
size_t nEntries);
static int inet_destroy(XDR *);
static void inet_gen_error(rem_port*, ISC_STATUS, ...);
2001-05-23 15:26:42 +02:00
static bool_t inet_getbytes(XDR *, SCHAR *, u_int);
static bool_t inet_getlong(XDR *, SLONG *);
static u_int inet_getpostn(XDR *);
#if !(defined WIN_NT)
static void inet_handler(void* _port);
#endif
static caddr_t inet_inline(XDR *, u_int);
static void inet_error(rem_port*, const TEXT*, ISC_STATUS, int);
static bool_t inet_putlong(XDR*, const SLONG*);
static bool_t inet_putbytes(XDR*, const SCHAR*, u_int);
2001-05-23 15:26:42 +02:00
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*,
2003-04-10 12:31:28 +02:00
ISC_STATUS*,
Firebird::ClumpletReader&);
2001-05-23 15:26:42 +02:00
static bool_t inet_write(XDR *, int);
#if !(defined WIN_NT)
2004-03-07 08:58:55 +01:00
static int parse_hosts(const TEXT*, const TEXT*, const TEXT*);
static int parse_line(const TEXT*, const TEXT*, const TEXT*, const TEXT*);
#endif
2001-05-23 15:26:42 +02:00
#ifdef DEBUG
2006-03-07 06:44:13 +01:00
static void packet_print(const TEXT*, const UCHAR*, int, ULONG);
2001-05-23 15:26:42 +02:00
#endif
static bool_t packet_receive(rem_port*, UCHAR*, SSHORT, SSHORT*);
static bool_t packet_send(rem_port*, const SCHAR*, SSHORT);
static rem_port* receive(rem_port*, PACKET *);
static rem_port* select_accept(rem_port*);
2001-05-23 15:26:42 +02:00
static void select_port(rem_port*, SLCT*, RemPortPtr&);
2008-04-20 16:02:51 +02:00
static bool select_multi(rem_port*, UCHAR* buffer, SSHORT bufsize, SSHORT* length, RemPortPtr&);
static int select_wait(rem_port*, SLCT *);
static int send_full(rem_port*, PACKET *);
static int send_partial(rem_port*, PACKET *);
2001-05-23 15:26:42 +02:00
static void unhook_port(rem_port*, rem_port*);
static int xdrinet_create(XDR *, rem_port*, UCHAR *, USHORT, enum xdr_op);
2006-05-24 16:08:06 +02:00
static bool setNoNagleOption(rem_port*);
2008-02-06 18:26:35 +01:00
static FPTR_VOID tryStopMainThread = 0;
static int shut_preproviders();
2008-02-06 18:26:35 +01:00
2001-05-23 15:26:42 +02:00
static XDR::xdr_ops inet_ops =
{
inet_getlong,
inet_putlong,
inet_getbytes,
inet_putbytes,
2001-05-23 15:26:42 +02:00
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)
2004-12-25 10:44:03 +01:00
#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)))
2008-01-16 08:55:28 +01:00
#define FD_ZERO(p) memset(p, 0, sizeof(*(p)))
2001-05-23 15:26:42 +02:00
#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 bool INET_initialized = false;
static bool INET_shutting_down = false;
static SLCT INET_select = { 0, 0, 0 };
static int INET_max_clients;
2008-04-15 02:57:19 +02:00
static rem_port* inet_async_receive = NULL;
static Firebird::GlobalPtr<Firebird::Mutex> port_mutex;
2001-05-23 15:26:42 +02:00
rem_port* INET_analyze(const Firebird::PathName& file_name,
2003-04-10 12:31:28 +02:00
ISC_STATUS* status_vector,
const TEXT* node_name,
const TEXT* user_string,
2003-12-03 09:19:24 +01:00
bool uv_flag,
Firebird::ClumpletReader &dpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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;
2001-05-23 15:26:42 +02:00
/* Pick up some user identification information */
Firebird::ClumpletWriter user_id(Firebird::ClumpletReader::UnTagged, MAX_DPB_SIZE);
Firebird::string buffer;
int eff_gid;
int eff_uid;
ISC_get_user(&buffer, &eff_uid, &eff_gid, user_string);
user_id.insertString(CNCT_user, buffer);
ISC_get_host(buffer);
buffer.lower();
user_id.insertString(CNCT_host, buffer);
2001-05-23 15:26:42 +02:00
if ((eff_uid == -1) || uv_flag) {
2004-12-26 14:48:01 +01:00
user_id.insertTag(CNCT_user_verification);
2001-05-23 15:26:42 +02:00
}
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);
2004-12-26 14:48:01 +01:00
user_id.insertBytes(CNCT_group,
2008-01-16 08:55:28 +01:00
reinterpret_cast<UCHAR*>(&eff_gid), sizeof(SLONG));
2001-05-23 15:26:42 +02:00
}
/* Establish connection to server */
/* Note: prior to V3.1E a recievers could not in truth handle more
then 5 protocol descriptions, so we try them in chunks of 5 or less */
/* If we want user verification, we can't speak anything less than version 7 */
P_CNCT* cnct = &packet->p_cnct;
2004-12-26 14:48:01 +01:00
cnct->p_cnct_user_id.cstr_length = user_id.getBufferLength();
cnct->p_cnct_user_id.cstr_address = user_id.getBuffer();
2001-05-23 15:26:42 +02:00
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)
2001-05-23 15:26:42 +02:00
#ifdef SCROLLABLE_CURSORS
,
2006-12-08 19:38:15 +01:00
REMOTE_PROTOCOL(PROTOCOL_SCROLLABLE_CURSORS, ptype_rpc, MAX_PTYPE, 99)
2001-05-23 15:26:42 +02:00
#endif
};
cnct->p_cnct_count = FB_NELEM(protocols_to_try1);
2001-05-23 15:26:42 +02:00
copy_p_cnct_repeat_array(cnct->p_cnct_versions,
protocols_to_try1,
cnct->p_cnct_count);
2001-05-23 15:26:42 +02:00
/* Try connection using first set of protocols. punt if error */
rem_port* port = inet_try_connect(packet, rdb, file_name,
node_name, status_vector, dpb);
2001-05-23 15:26:42 +02:00
if (!port) {
return NULL;
}
if (packet->p_operation == op_reject && !uv_flag)
{
disconnect(port);
/* try again with next set of known protocols */
2004-12-26 14:48:01 +01:00
cnct->p_cnct_user_id.cstr_length = user_id.getBufferLength();
cnct->p_cnct_user_id.cstr_address = user_id.getBuffer();
2001-05-23 15:26:42 +02:00
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)
2001-05-23 15:26:42 +02:00
};
cnct->p_cnct_count = FB_NELEM(protocols_to_try2);
2001-05-23 15:26:42 +02:00
copy_p_cnct_repeat_array(cnct->p_cnct_versions,
protocols_to_try2,
cnct->p_cnct_count);
2001-05-23 15:26:42 +02:00
port = inet_try_connect(packet, rdb, file_name,
node_name, status_vector, dpb);
2005-12-23 09:52:31 +01:00
if (!port) {
2001-05-23 15:26:42 +02:00
return NULL;
2005-12-23 09:52:31 +01:00
}
2001-05-23 15:26:42 +02:00
}
if (packet->p_operation == op_reject && !uv_flag)
{
disconnect(port);
/* try again with next set of known protocols */
2004-12-26 14:48:01 +01:00
cnct->p_cnct_user_id.cstr_length = user_id.getBufferLength();
cnct->p_cnct_user_id.cstr_address = user_id.getBuffer();
2001-05-23 15:26:42 +02:00
static const p_cnct::p_cnct_repeat protocols_to_try3[] =
{
REMOTE_PROTOCOL(PROTOCOL_VERSION3, ptype_rpc, ptype_batch_send, 1),
REMOTE_PROTOCOL(PROTOCOL_VERSION4, ptype_rpc, ptype_batch_send, 2)
2001-05-23 15:26:42 +02:00
};
cnct->p_cnct_count = FB_NELEM(protocols_to_try3);
2001-05-23 15:26:42 +02:00
copy_p_cnct_repeat_array(cnct->p_cnct_versions,
protocols_to_try3,
cnct->p_cnct_count);
2001-05-23 15:26:42 +02:00
port = inet_try_connect(packet, rdb, file_name,
node_name, status_vector, dpb);
2001-05-23 15:26:42 +02:00
if (!port) {
return NULL;
}
}
if (packet->p_operation != op_accept)
{
2003-11-08 17:40:17 +01:00
*status_vector++ = isc_arg_gds;
*status_vector++ = isc_connect_reject;
2001-05-23 15:26:42 +02:00
*status_vector++ = 0;
disconnect(port);
delete rdb;
2001-05-23 15:26:42 +02:00
return NULL;
}
port->port_protocol = packet->p_acpt.p_acpt_version;
2002-02-16 03:21:35 +01:00
/* once we've decided on a protocol, concatenate the version
2001-05-23 15:26:42 +02:00
string to reflect it... */
Firebird::string temp;
2006-12-08 19:38:15 +01:00
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());
2001-05-23 15:26:42 +02:00
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;
}
2001-05-23 15:26:42 +02:00
return port;
}
rem_port* INET_connect(const TEXT* name,
PACKET* packet,
ISC_STATUS* status_vector,
USHORT flag, Firebird::ClumpletReader* dpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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) {
2004-04-29 00:36:29 +02:00
fprintf(stdout, "INET_connect\n");
fflush(stdout);
}
2001-05-23 15:26:42 +02:00
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);
2001-05-23 15:26:42 +02:00
}
}
#endif
rem_port* port = alloc_port(0);
2001-05-23 15:26:42 +02:00
port->port_status_vector = status_vector;
REMOTE_get_timeout_params(port, dpb);
2003-11-08 17:40:17 +01:00
status_vector[0] = isc_arg_gds;
2001-05-23 15:26:42 +02:00
status_vector[1] = 0;
2003-11-08 17:40:17 +01:00
status_vector[2] = isc_arg_end;
2001-05-23 15:26:42 +02:00
Firebird::string host;
Firebird::string protocol;
2001-05-23 15:26:42 +02:00
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);
2001-05-23 15:26:42 +02:00
}
}
if (host.hasData()) {
delete port->port_connection;
port->port_connection = REMOTE_make_string(host.c_str());
2001-05-23 15:26:42 +02:00
}
else {
host = port->port_host->str_data;
2001-05-23 15:26:42 +02:00
}
if (protocol.isEmpty()) {
2004-05-12 21:39:17 +02:00
const unsigned short port2 = Config::getRemoteServicePort();
if (port2) {
protocol.printf("%hu", port2);
2003-12-11 09:26:46 +01:00
}
else {
protocol = Config::getRemoteServiceName();
2003-12-11 09:26:46 +01:00
}
}
2001-05-23 15:26:42 +02:00
/* Set up Inter-Net socket address */
struct sockaddr_in address;
2008-01-16 08:55:28 +01:00
memset(&address, 0, sizeof(address));
2001-05-23 15:26:42 +02:00
/* U N I X style sockets */
2003-03-19 14:14:09 +01:00
address.sin_family = AF_INET;
2001-05-23 15:26:42 +02:00
2008-01-16 08:55:28 +01:00
// define maximum numbers of addresses for a host that we can handle
const int MAX_HOST_ADDRESS_NUMBER = 8;
2003-03-19 14:14:09 +01:00
in_addr host_addr;
2008-01-16 08:55:28 +01:00
in_addr host_addr_arr[MAX_HOST_ADDRESS_NUMBER];
2001-05-23 15:26:42 +02:00
2003-03-19 14:14:09 +01:00
if (packet) {
// client connection
2008-01-16 08:55:28 +01:00
host_addr = get_host_address(host, host_addr_arr, MAX_HOST_ADDRESS_NUMBER);
2001-05-23 15:26:42 +02:00
if (host_addr.s_addr == INADDR_NONE)
{
2008-03-17 10:08:48 +01:00
gds__log("INET/INET_connect: gethostbyname (%s) failed, error code = %d",
host.c_str(), H_ERRNO);
2003-03-19 14:14:09 +01:00
inet_gen_error(port,
isc_network_error,
isc_arg_string,
port->port_connection->str_data,
isc_arg_gds,
isc_net_lookup_err, isc_arg_gds, isc_host_unknown, 0);
2001-05-23 15:26:42 +02:00
2003-03-19 14:14:09 +01:00
disconnect(port);
return NULL;
}
2001-05-23 15:26:42 +02:00
}
else {
2003-03-19 14:14:09 +01:00
// server connection
host_addr = get_bind_address();
2001-05-23 15:26:42 +02:00
}
const struct servent* service = getservbyname(protocol.c_str(), "tcp");
2001-05-23 15:26:42 +02:00
#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) {
if (H_ERRNO == INET_RETRY_ERRNO) {
for (int retry = 0; retry < INET_RETRY_CALL; retry++) {
if ( (service = getservbyname(protocol.c_str(), "tcp")) )
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
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) {
2001-05-23 15:26:42 +02:00
/* apply hardwired translation */
2003-06-25 09:39:04 +02:00
address.sin_port = htons(FB_SERVICE_PORT);
2001-05-23 15:26:42 +02:00
}
/* 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()));
2001-05-23 15:26:42 +02:00
}
if (address.sin_port == 0)
{
2001-05-23 15:26:42 +02:00
/* end of modification by FSG */
/* this is the original code */
2008-03-17 10:08:48 +01:00
gds__log("INET/INET_connect: getservbyname failed, error code = %d", H_ERRNO);
2001-05-23 15:26:42 +02:00
inet_gen_error(port,
isc_network_error,
isc_arg_string,
port->port_connection->str_data,
isc_arg_gds,
isc_net_lookup_err,
isc_arg_gds,
isc_service_unknown,
isc_arg_string,
protocol.c_str(),
isc_arg_string, "tcp", 0);
2001-05-23 15:26:42 +02:00
return NULL;
} /* else / not hardwired gds_db translation */
}
else {
/* if we have got a service-struct, get port number from there
* (in case of hardwired gds_db to 3050 translation, address.sin_port was
* already set above */
address.sin_port = service->s_port;
} /* else (service found) */
/* end of modifications by luz */
/* Allocate a port block and initialize a socket for communications */
port->port_handle = (HANDLE) socket(AF_INET, SOCK_STREAM, 0);
if ((SOCKET) port->port_handle == INVALID_SOCKET)
{
inet_error(port, "socket", isc_net_connect_err, INET_ERRNO);
2001-05-23 15:26:42 +02:00
disconnect(port);
return NULL;
}
/* If we're a host, just make the connection */
int n;
if (packet)
{
if (! setNoNagleOption(port)) {
inet_error(port, "setsockopt TCP_NODELAY",
isc_net_connect_err, INET_ERRNO);
disconnect(port);
return NULL;
}
2008-01-16 08:55:28 +01:00
int inetErrNo = 0;
for (int i = 0; i < MAX_HOST_ADDRESS_NUMBER; i++)
{
address.sin_addr = host_addr_arr[i];
if (address.sin_addr.s_addr == 0L)
break; // all addresses tried and failed - get out and print the last error
// If host has two addresses and the first one failed,
// but the second one succeeded - no need to worry
n = connect((SOCKET) port->port_handle,
2001-05-23 15:26:42 +02:00
(struct sockaddr *) &address, sizeof(address));
2008-01-16 08:55:28 +01:00
inetErrNo = INET_ERRNO;
2008-01-16 08:55:28 +01:00
if (n != -1 && send_full(port, packet))
return port;
2001-05-23 15:26:42 +02:00
}
2008-01-16 08:55:28 +01:00
inet_error(port, "connect", isc_net_connect_err, inetErrNo);
disconnect(port);
return NULL;
2001-05-23 15:26:42 +02:00
}
2008-01-16 08:55:28 +01:00
// We're a server, so wait for a host to show up
memcpy(&address.sin_addr, &host_addr, sizeof(address.sin_addr));
2001-05-23 15:26:42 +02:00
if (flag & SRVR_multi_client) {
struct linger lingerInfo;
lingerInfo.l_onoff = 0;
lingerInfo.l_linger = 0;
2003-12-03 09:19:24 +01:00
int optval = TRUE;
2001-05-23 15:26:42 +02:00
n = setsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_REUSEADDR,
2003-12-03 09:19:24 +01:00
(SCHAR*) &optval, sizeof(optval));
2001-05-23 15:26:42 +02:00
if (n == -1) {
inet_error(port, "setsockopt REUSE", isc_net_connect_listen_err,
INET_ERRNO);
2001-05-23 15:26:42 +02:00
disconnect(port);
return NULL;
}
/* Get any values for SO_LINGER so that they can be reset during
* disconnect. SO_LINGER should be set by default on the socket
*/
2006-03-07 06:44:13 +01:00
socklen_t optlen = sizeof(port->port_linger);
2001-05-23 15:26:42 +02:00
n = getsockopt((SOCKET) 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((SOCKET) port->port_handle, SOL_SOCKET, SO_LINGER,
(SCHAR *) & lingerInfo, sizeof(lingerInfo));
if (n == -1) {
inet_error(port, "setsockopt LINGER", isc_net_connect_listen_err,
INET_ERRNO);
2001-05-23 15:26:42 +02:00
disconnect(port);
return NULL;
}
2006-05-24 16:08:06 +02:00
if (! setNoNagleOption(port)) {
inet_error(port, "setsockopt TCP_NODELAY",
isc_net_connect_listen_err, INET_ERRNO);
disconnect(port);
return NULL;
2001-05-23 15:26:42 +02:00
}
}
n = bind((SOCKET) 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. */
if (INET_ERRNO == INET_ADDR_IN_USE) {
for (int retry = 0; retry < INET_RETRY_CALL; retry++) {
2001-05-23 15:26:42 +02:00
sleep(10);
2008-04-04 04:14:17 +02:00
n = bind((SOCKET) port->port_handle, (struct sockaddr *) &address, sizeof(address));
2001-05-23 15:26:42 +02:00
if (n == 0)
break;
}
}
}
if (n == -1) {
inet_error(port, "bind", isc_net_connect_listen_err, INET_ERRNO);
disconnect(port);
return NULL;
2001-05-23 15:26:42 +02:00
}
n = listen((SOCKET) port->port_handle, 5);
if (n == -1) {
inet_error(port, "listen", isc_net_connect_listen_err, INET_ERRNO);
2001-05-23 15:26:42 +02:00
return NULL;
}
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);
2001-05-23 15:26:42 +02:00
gds__register_cleanup(exit_handler, (void *) port);
return port;
}
while (true) {
socklen_t l = sizeof(address);
SOCKET s = accept((SOCKET) port->port_handle,
2001-05-23 15:26:42 +02:00
(struct sockaddr *) &address, &l);
const int inetErrNo = INET_ERRNO;
2001-05-23 15:26:42 +02:00
if (s == INVALID_SOCKET) {
inet_error(port, "accept", isc_net_connect_err, inetErrNo);
2001-05-23 15:26:42 +02:00
disconnect(port);
return NULL;
}
#ifdef WIN_NT
2008-04-29 10:15:36 +02:00
if ((flag & SRVR_debug) || !fork(s, flag)) {
2001-05-23 15:26:42 +02:00
#else
2008-04-29 10:15:36 +02:00
if ((flag & SRVR_debug) || !fork()) {
2001-05-23 15:26:42 +02:00
#endif
SOCLOSE((SOCKET) port->port_handle);
port->port_handle = (HANDLE) s;
port->port_server_flags |= SRVR_server;
2008-04-29 10:15:36 +02:00
port->port_flags |= PORT_server;
2001-05-23 15:26:42 +02:00
return port;
}
2008-04-29 10:15:36 +02:00
#ifndef WIN_NT
Firebird::MutexLockGuard guard(waitThreadMutex);
if (! procCount++) {
gds__thread_start(waitThread, 0, THREAD_medium, 0, 0);
}
#endif
2001-05-23 15:26:42 +02:00
SOCLOSE(s);
}
}
rem_port* INET_reconnect(HANDLE handle, ISC_STATUS* status_vector)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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* port = alloc_port(0);
2001-05-23 15:26:42 +02:00
port->port_status_vector = status_vector;
2003-11-08 17:40:17 +01:00
status_vector[0] = isc_arg_gds;
2001-05-23 15:26:42 +02:00
status_vector[1] = 0;
2003-11-08 17:40:17 +01:00
status_vector[2] = isc_arg_end;
2001-05-23 15:26:42 +02:00
port->port_handle = handle;
port->port_flags |= PORT_server;
2001-05-23 15:26:42 +02:00
port->port_server_flags |= SRVR_server;
int n = 0, optval = TRUE;
n = setsockopt((SOCKET) port->port_handle, SOL_SOCKET,
SO_KEEPALIVE, (SCHAR*) &optval, sizeof(optval));
if (n == -1) {
gds__log("inet server err: setting KEEPALIVE socket option \n");
}
2006-05-24 16:08:06 +02:00
if (! setNoNagleOption(port)) {
gds__log("inet server err: setting NODELAY socket option \n");
}
2001-05-23 15:26:42 +02:00
return port;
}
rem_port* INET_server(int sock)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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* port = alloc_port(0);
port->port_flags |= PORT_server;
2001-05-23 15:26:42 +02:00
port->port_server_flags |= SRVR_server;
port->port_handle = (HANDLE) sock;
int optval = 1;
n = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
(SCHAR *) & optval, sizeof(optval));
2005-04-05 08:47:17 +02:00
if (n == -1) {
gds__log("inet server err: setting KEEPALIVE socket option \n");
}
2006-05-24 16:08:06 +02:00
if (! setNoNagleOption(port)) {
gds__log("inet server err: setting NODELAY socket option \n");
}
2001-05-23 15:26:42 +02:00
return port;
}
void INET_set_clients( int count)
{
/**************************************
*
* I N E T _ s e t _ c l i e n t s
*
**************************************
*
* Functional description
* Set maxinum number of clients served before
* starting new server
*
**************************************/
INET_max_clients = (count && count < MAXCLIENTS) ? count : MAXCLIENTS;
}
2008-04-18 03:37:44 +02:00
static int accept_connection(rem_port* port, const P_CNCT* cnct)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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) */
2004-12-26 14:48:01 +01:00
Firebird::string name("guest"), password;
2001-05-23 15:26:42 +02:00
/* Pick up account and password, if given */
Firebird::ClumpletReader id(Firebird::ClumpletReader::UnTagged, cnct->p_cnct_user_id.cstr_address,
2004-12-26 14:48:01 +01:00
cnct->p_cnct_user_id.cstr_length);
SLONG eff_gid = -1, eff_uid = -1;
2003-09-11 20:59:34 +02:00
bool user_verification = false;
2004-12-26 14:48:01 +01:00
for (id.rewind(); !id.isEof(); id.moveNext())
2001-05-23 15:26:42 +02:00
{
2004-12-26 14:48:01 +01:00
switch (id.getClumpTag())
2001-05-23 15:26:42 +02:00
{
case CNCT_user:
2003-12-03 09:19:24 +01:00
{
2004-12-26 14:48:01 +01:00
id.getString(name);
2003-12-03 09:19:24 +01:00
break;
2001-05-23 15:26:42 +02:00
}
case CNCT_passwd:
2003-12-03 09:19:24 +01:00
{
2004-12-26 14:48:01 +01:00
id.getString(password);
2003-12-03 09:19:24 +01:00
break;
}
2001-05-23 15:26:42 +02:00
case CNCT_group:
2003-12-03 09:19:24 +01:00
{
2004-12-26 14:48:01 +01:00
int length = id.getClumpLength();
2003-12-03 09:19:24 +01:00
if (length != 0) {
2004-12-26 14:48:01 +01:00
eff_gid = 0;
2006-02-23 07:52:25 +01:00
memcpy(&eff_gid, id.getBytes(), length);
2004-12-26 14:48:01 +01:00
eff_gid = ntohl(eff_gid);
2003-12-03 09:19:24 +01:00
}
break;
}
2001-05-23 15:26:42 +02:00
/* 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:
2003-09-11 20:59:34 +02:00
user_verification = true;
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
}
2008-01-16 08:55:28 +01:00
#ifndef WIN_NT
2001-05-23 15:26:42 +02:00
else
{
/* Security check should be against remote name passed - so do
check_host first */
TEXT host[MAXHOSTLEN];
2005-01-27 14:19:16 +01:00
const struct passwd* passwd = getpwnam( name.c_str());
const int trusted = check_host(port, host, name.c_str(), passwd);
2001-05-23 15:26:42 +02:00
if (!trusted) {
return FALSE;
}
if (trusted == -1) {
eff_gid = eff_uid = -1;
port->port_flags |= PORT_not_trusted; // never tested
2001-05-23 15:26:42 +02:00
}
else
{
if (check_proxy(port, host, name))
2005-01-27 14:19:16 +01:00
passwd = getpwnam(name.c_str());
2005-12-23 09:52:31 +01:00
if (!passwd) {
2001-05-23 15:26:42 +02:00
return FALSE;
2005-12-23 09:52:31 +01:00
}
#ifndef HAVE_INITGROUPS
2001-05-23 15:26:42 +02:00
eff_gid = passwd->pw_gid;
#else
SLONG gids[BUFFER_TINY];
2001-05-23 15:26:42 +02:00
initgroups(passwd->pw_name, passwd->pw_gid);
if (eff_gid != -1) {
const int gid_count = getgroups(FB_NELEM(gids), (gid_t*)gids);
2003-12-03 09:19:24 +01:00
int i;
2001-07-12 07:46:06 +02:00
for (i = 0; i < gid_count; ++i) {
2001-05-23 15:26:42 +02:00
if (gids[i] == eff_gid) {
break;
}
}
if ((i == gid_count) && passwd) {
eff_gid = passwd->pw_gid;
}
}
else
{
eff_gid = passwd->pw_gid;
}
#endif /* HAVE_INITGROUPS */
2001-05-23 15:26:42 +02:00
eff_uid = passwd->pw_uid;
/* if not multi-client: set uid, gid and home directory */
if (!port->port_parent) {
if (!eff_gid || setregid(passwd->pw_gid, eff_gid) == -1) {
setregid(passwd->pw_gid, passwd->pw_gid);
}
if (!setreuid(passwd->pw_uid, passwd->pw_uid)) {
chdir(passwd->pw_dir);
2001-05-23 15:26:42 +02:00
}
}
}
}
{
2002-02-16 03:21:35 +01:00
/* If the environment varible ISC_INET_SERVER_HOME is set,
2001-05-23 15:26:42 +02:00
* 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 activiated for the production product.
2002-02-16 03:21:35 +01:00
* 1995-February-27 David Schnepper
2001-05-23 15:26:42 +02:00
*/
Firebird::PathName home;
if (fb_utils::readenv("ISC_INET_SERVER_HOME", home))
{
if (chdir(home.c_str())) {
2008-03-17 10:08:48 +01:00
gds__log("inet_server: unable to cd to %s errno %d\n",
home.c_str(), INET_ERRNO);
2001-05-23 15:26:42 +02:00
/* We continue after the error */
}
}
}
2002-10-30 07:40:58 +01:00
#endif /* !WIN_NT */
2001-05-23 15:26:42 +02:00
/* store FULL user identity in port_user_name for security purposes */
Firebird::string temp;
2005-01-13 08:31:17 +01:00
temp.printf("%s.%ld.%ld", name.c_str(), eff_gid, eff_uid);
port->port_user_name = REMOTE_make_string(temp.c_str());
2001-05-23 15:26:42 +02:00
port->port_protocol_str = REMOTE_make_string("TCPv4");
struct sockaddr_in address;
socklen_t l = sizeof(address);
2008-01-16 08:55:28 +01:00
memset(&address, 0, sizeof(address));
int status = getpeername((SOCKET) port->port_handle, (struct sockaddr *) &address, &l);
if (status == 0) {
Firebird::string addr_str;
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());
}
2001-05-23 15:26:42 +02:00
return TRUE;
}
2003-03-11 06:53:55 +01:00
static rem_port* alloc_port( rem_port* parent)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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.
*
**************************************/
2003-03-11 06:53:55 +01:00
if (!INET_initialized)
{
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
static WSADATA wsadata;
const WORD version = MAKEWORD(2, 0);
const int wsaError = WSAStartup(version, &wsadata);
if (wsaError) {
2001-05-23 15:26:42 +02:00
if (parent)
inet_error(parent, "WSAStartup", isc_net_init_error, wsaError);
2001-05-23 15:26:42 +02:00
else {
2008-03-17 10:08:48 +01:00
gds__log("INET/alloc_port: WSAStartup failed, error code = %d", wsaError);
2001-05-23 15:26:42 +02:00
}
return NULL;
}
gds__register_cleanup(exit_handler, 0);
#endif
2002-12-06 13:34:43 +01:00
INET_remote_buffer = Config::getTcpRemoteBufferSize();
2003-03-11 06:53:55 +01:00
if (INET_remote_buffer < MAX_DATA_LW ||
INET_remote_buffer > MAX_DATA_HW)
{
INET_remote_buffer = DEF_MAX_DATA;
}
2001-05-23 15:26:42 +02:00
#ifdef DEBUG
2008-03-17 10:08:48 +01:00
gds__log(" Info: Remote Buffer Size set to %ld", INET_remote_buffer);
2001-05-23 15:26:42 +02:00
#endif
fb_shutdown_callback(0, shut_preproviders, fb_shut_preproviders);
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;
2001-05-23 15:26:42 +02:00
}
rem_port* port = new rem_port(rem_port::INET, INET_remote_buffer * 2);
REMOTE_get_timeout_params(port, 0);
2001-05-23 15:26:42 +02:00
2008-03-21 03:42:24 +01:00
TEXT buffer[BUFFER_SMALL];
2002-11-14 07:48:09 +01:00
gethostname(buffer, sizeof(buffer));
2008-03-21 03:42:24 +01:00
2001-05-23 15:26:42 +02:00
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);
2001-05-23 15:26:42 +02:00
port->port_version = REMOTE_make_string(buffer);
port->port_accept = accept_connection;
port->port_disconnect = disconnect;
port->port_receive_packet = receive;
2006-03-03 17:20:42 +01:00
port->port_select_multi = select_multi;
2001-05-23 15:26:42 +02:00
port->port_send_packet = send_full;
port->port_send_partial = send_partial;
port->port_connect = aux_connect;
2001-05-23 15:26:42 +02:00
port->port_request = aux_request;
port->port_buff_size = (USHORT) INET_remote_buffer;
port->port_async_receive = inet_async_receive;
2001-05-23 15:26:42 +02:00
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);
}
2006-03-03 17:20:42 +01:00
2001-05-23 15:26:42 +02:00
return port;
}
static rem_port* aux_connect(rem_port* port, PACKET* packet, t_event_ast ast)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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);
2001-05-23 15:26:42 +02:00
/* If this is a server, we're got an auxiliary connection. Accept it */
if (port->port_server_flags) {
SOCKET n = accept(port->port_channel, (struct sockaddr *) &address, &l);
const int inetErrNo = INET_ERRNO;
2001-05-23 15:26:42 +02:00
if (n == INVALID_SOCKET) {
inet_error(port, "accept", isc_net_event_connect_err, inetErrNo);
2001-05-23 15:26:42 +02:00
SOCLOSE(port->port_channel);
return NULL;
}
SOCLOSE(port->port_channel);
port->port_handle = (HANDLE) n;
port->port_flags |= PORT_async;
return port;
}
rem_port* new_port = alloc_port(port->port_parent);
port->port_async = new_port;
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
new_port->port_flags |= PORT_async;
P_RESP* response = &packet->p_resp;
2001-05-23 15:26:42 +02:00
/* Set up new socket */
SOCKET n = socket(AF_INET, SOCK_STREAM, 0);
if (n == INVALID_SOCKET) {
inet_error(port, "socket", isc_net_event_connect_err, INET_ERRNO);
2001-05-23 15:26:42 +02:00
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.
*/
2008-01-16 08:55:28 +01:00
memset(&address, 0, sizeof(address));
int status = getpeername((SOCKET) port->port_handle, (struct sockaddr *) &address, &l);
if (status != 0) {
inet_error(port, "socket", isc_net_event_connect_err, INET_ERRNO);
SOCLOSE(n);
return NULL;
}
2001-05-23 15:26:42 +02:00
address.sin_family = AF_INET;
address.sin_port = ((struct sockaddr_in *)(response->p_resp_data.cstr_address))->sin_port;
2001-05-23 15:26:42 +02:00
2008-03-26 15:20:43 +01:00
int optval = 1;
setsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_KEEPALIVE,
(SCHAR*) &optval, sizeof(optval));
status = connect(n, (struct sockaddr *) &address, sizeof(address));
const int inetErrNo = INET_ERRNO;
2001-05-23 15:26:42 +02:00
if (status < 0) {
inet_error(port, "connect", isc_net_event_connect_err, inetErrNo);
2001-05-23 15:26:42 +02:00
SOCLOSE(n);
return NULL;
}
2002-11-12 14:15:13 +01:00
#ifdef SIOCSPGRP
2001-05-23 15:26:42 +02:00
if (ast)
{
int arg;
2002-11-12 14:15:13 +01:00
#ifdef HAVE_GETPGRP
2001-05-23 15:26:42 +02:00
arg = getpgrp();
#else
arg = getpid();
#endif
if (ioctl(n, SIOCSPGRP, &arg) < 0) {
inet_error(port, "ioctl/SIOCSPGRP", isc_net_event_connect_err,
INET_ERRNO);
2001-05-23 15:26:42 +02:00
SOCLOSE(port->port_channel);
return NULL;
}
new_port->port_ast = ast;
ISC_signal(SIGURG, inet_handler, new_port);
2001-05-23 15:26:42 +02:00
}
2002-11-12 14:15:13 +01:00
#endif /* SIOCSPGRP */
2001-05-23 15:26:42 +02:00
new_port->port_handle = (HANDLE) n;
return new_port;
}
static rem_port* aux_request( rem_port* port, PACKET* packet)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* a u x _ r e q u e s t
*
**************************************
*
* Functional description
2002-02-16 03:21:35 +01:00
* A remote interface has requested the server prepare an auxiliary
2001-05-23 15:26:42 +02:00
* connection; the server calls aux_request to set up the connection.
*
**************************************/
struct sockaddr_in address;
2001-05-23 15:26:42 +02:00
/* Set up new socket */
address.sin_family = AF_INET;
2003-03-19 14:14:09 +01:00
in_addr bind_addr = get_bind_address();
2008-01-16 08:55:28 +01:00
memcpy(&address.sin_addr, &bind_addr, sizeof(address.sin_addr));
address.sin_port = htons(Config::getRemoteAuxPort());
SOCKET n = socket(AF_INET, SOCK_STREAM, 0);
if (n == INVALID_SOCKET) {
inet_error(port, "socket", isc_net_event_listen_err, INET_ERRNO);
2001-05-23 15:26:42 +02:00
return NULL;
}
int optval = TRUE;
2006-10-25 17:09:13 +02:00
int ret = setsockopt(n, SOL_SOCKET, SO_REUSEADDR,
(SCHAR *) &optval, sizeof(optval));
if (ret == -1) {
inet_error(port, "setsockopt REUSE", isc_net_event_listen_err, INET_ERRNO);
return NULL;
}
2001-05-23 15:26:42 +02:00
if (bind(n, (struct sockaddr *) &address, sizeof(address)) < 0) {
inet_error(port, "bind", isc_net_event_listen_err, INET_ERRNO);
2001-05-23 15:26:42 +02:00
return NULL;
}
socklen_t length = sizeof(address);
2001-05-23 15:26:42 +02:00
if (getsockname(n, (struct sockaddr *) &address, &length) < 0) {
inet_error(port, "getsockname", isc_net_event_listen_err, INET_ERRNO);
2001-05-23 15:26:42 +02:00
return NULL;
}
if (listen(n, 1) < 0) {
inet_error(port, "listen", isc_net_event_listen_err, INET_ERRNO);
2001-05-23 15:26:42 +02:00
return NULL;
}
rem_port* new_port = alloc_port(port->port_parent);
port->port_async = new_port;
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
struct sockaddr_in port_address;
if (getsockname((SOCKET) port->port_handle, (struct sockaddr *) &port_address, &length) < 0) {
inet_error(port, "getsockname", isc_net_event_listen_err, INET_ERRNO);
2001-05-23 15:26:42 +02:00
return NULL;
}
2008-01-16 08:55:28 +01:00
memcpy(&address.sin_addr, &port_address.sin_addr, sizeof(address.sin_addr));
2001-05-23 15:26:42 +02:00
response->p_resp_data.cstr_length = sizeof(address);
2008-01-16 08:55:28 +01:00
memcpy(response->p_resp_data.cstr_address, &address, sizeof(address));
2001-05-23 15:26:42 +02:00
return new_port;
}
#ifndef WIN_NT
2001-05-23 15:26:42 +02:00
static int check_host(
rem_port* port,
2004-03-07 08:58:55 +01:00
TEXT* host_name,
const TEXT* user_name, const struct passwd *passwd)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c h e c k _ h o s t ( n o n - V M S )
*
**************************************
*
* Functional description
2007-03-24 18:44:09 +01:00
* Check the host on the other end of the socket to see if
2001-05-23 15:26:42 +02:00
* it's an equivalent host.
* NB.: First check the ~/.rhosts then the HOSTS_FILE - both have
* the same line formats (see parse_line)
*
**************************************/
struct sockaddr_in address;
socklen_t length = sizeof(address);
2001-05-23 15:26:42 +02:00
2001-07-12 07:46:06 +02:00
if (getpeername((int) port->port_handle, (struct sockaddr*)&address, &length) == -1)
2001-05-23 15:26:42 +02:00
return 0;
// If source address is in the loopback net - trust it
if((ntohl(address.sin_addr.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
return 1;
2008-01-16 08:55:28 +01:00
const struct hostent* host = gethostbyaddr((SCHAR*) &address.sin_addr,
sizeof(address.sin_addr), address.sin_family);
if (!host)
2003-09-11 20:59:34 +02:00
{
return 0;
}
2001-05-23 15:26:42 +02:00
int result = -1;
2001-05-23 15:26:42 +02:00
strcpy(host_name, host->h_name);
TEXT user[BUFFER_TINY], rhosts[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
if (passwd) {
strcpy(user, passwd->pw_name);
strcpy(rhosts, passwd->pw_dir);
strcat(rhosts, "/.rhosts");
result = parse_hosts(rhosts, host_name, user);
}
else
strcpy(user, user_name);
if (result == -1) {
2004-04-29 00:36:29 +02:00
FILE* fp = fopen(GDS_HOSTS_FILE, "r");
const TEXT* hosts_file = fp ? (TEXT*)GDS_HOSTS_FILE : (TEXT*)HOSTS_FILE;
2001-05-23 15:26:42 +02:00
if (fp)
2004-04-29 00:36:29 +02:00
fclose(fp);
2001-05-23 15:26:42 +02:00
2003-09-13 11:15:06 +02:00
result = parse_hosts(hosts_file, host_name, user);
if (result == -1)
2005-12-23 09:52:31 +01:00
result = 0;
2001-05-23 15:26:42 +02:00
}
return result;
}
2003-12-03 09:19:24 +01:00
#endif // !defined(WIN_NT)
2001-05-23 15:26:42 +02:00
#if !(defined WIN_NT)
static bool check_proxy(rem_port* port,
TEXT* host_name,
Firebird::string& user_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c h e c k _ p r o x y
*
**************************************
*
* Functional description
* Lookup <host_name, user_name> in proxy file. If found,
* change user_name.
*
**************************************/
2003-09-11 20:59:34 +02:00
TEXT proxy_file[MAXPATHLEN];
TEXT source_user[BUFFER_TINY];
2003-09-11 20:59:34 +02:00
TEXT source_host[MAXHOSTLEN];
TEXT target_user[BUFFER_TINY];
TEXT line[BUFFER_SMALL];
2001-05-23 15:26:42 +02:00
strcpy(proxy_file, PROXY_FILE);
2004-04-29 00:36:29 +02:00
FILE* proxy = fopen(proxy_file, "r");
if (!proxy)
2003-09-11 20:59:34 +02:00
return false;
2001-05-23 15:26:42 +02:00
/* Read lines, scan, and compare */
2003-09-11 20:59:34 +02:00
bool result = false;
2001-05-23 15:26:42 +02:00
for (;;) {
int c;
TEXT* p = line;
2004-04-29 00:36:29 +02:00
while (((c = getc(proxy)) != 0) && c != EOF && c != '\n')
{
2001-05-23 15:26:42 +02:00
*p++ = c;
}
2001-05-23 15:26:42 +02:00
*p = 0;
if (sscanf(line, " %[^:]:%s%s", source_host, source_user, target_user)
>= 3)
2006-04-09 08:46:28 +02:00
{
2001-05-23 15:26:42 +02:00
if ((!strcmp(source_host, host_name) || !strcmp(source_host, "*"))
&& (!strcmp(source_user, user_name.c_str())
|| !strcmp(source_user, "*")))
{
delete port->port_user_name;
port->port_user_name = REMOTE_make_string(target_user);
user_name = target_user;
2003-09-11 20:59:34 +02:00
result = true;
2001-05-23 15:26:42 +02:00
break;
}
2006-04-09 08:46:28 +02:00
}
2001-05-23 15:26:42 +02:00
if (c == EOF)
break;
}
2004-04-29 00:36:29 +02:00
fclose(proxy);
2001-05-23 15:26:42 +02:00
return result;
}
2008-04-29 10:15:36 +02:00
#endif // !defined(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)
2001-05-23 15:26:42 +02:00
static void disconnect( rem_port* port)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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 terminiating
* 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((SOCKET) port->port_handle, SOL_SOCKET, SO_LINGER,
2001-05-23 15:26:42 +02:00
(SCHAR *) & port->port_linger,
sizeof(port->port_linger));
}
#if defined WIN_NT
if (port->port_handle && (SOCKET) port->port_handle != INVALID_SOCKET) {
shutdown((int) port->port_handle, 2);
}
#else /* WIN_NT */
if (port->port_handle) {
shutdown((int) port->port_handle, 2);
}
#endif /* WIN_NT */
2008-01-16 08:55:28 +01:00
#ifndef WIN_NT
2001-05-23 15:26:42 +02:00
if (port->port_ast) {
ISC_signal_cancel(SIGURG, inet_handler, port);
2001-05-23 15:26:42 +02:00
}
#endif
/* If this is a sub-port, unlink it from it's parent */
2008-04-21 11:53:28 +02:00
Firebird::MutexLockGuard guard(port_mutex);
port->port_state = rem_port::DISCONNECTED;
rem_port* parent = port->port_parent;
if (parent != NULL) {
2001-05-23 15:26:42 +02:00
if (port->port_async) {
disconnect(port->port_async);
port->port_async = NULL;
}
unhook_port(port, parent);
2001-05-23 15:26:42 +02:00
}
else if (port->port_async) {
port->port_async->port_flags |= PORT_disconnect;
}
if (port->port_handle) {
SOCLOSE((SOCKET) port->port_handle);
}
gds__unregister_cleanup(exit_handler, (void *) port);
port->release();
2001-05-23 15:26:42 +02:00
#ifdef DEBUG
if (INET_trace & TRACE_summary) {
2004-04-29 00:36:29 +02:00
fprintf(stdout, "INET_count_send = %lu packets\n",
2001-05-23 15:26:42 +02:00
INET_count_send);
2004-04-29 00:36:29 +02:00
fprintf(stdout, "INET_bytes_send = %lu bytes\n",
2001-05-23 15:26:42 +02:00
INET_bytes_send);
2004-04-29 00:36:29 +02:00
fprintf(stdout, "INET_count_recv = %lu packets\n",
2001-05-23 15:26:42 +02:00
INET_count_recv);
2004-04-29 00:36:29 +02:00
fprintf(stdout, "INET_bytes_recv = %lu bytes\n",
2001-05-23 15:26:42 +02:00
INET_bytes_recv);
2004-04-29 00:36:29 +02:00
fflush(stdout);
2001-05-23 15:26:42 +02:00
}
#endif
return;
}
static void exit_handler( void *arg)
{
/**************************************
*
* e x i t _ h a n d l e r
*
**************************************
*
* Functional description
* Shutdown all active connections
* to allow restart.
*
**************************************/
rem_port* main_port = (rem_port*) arg;
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
if (!main_port)
{
2005-03-10 11:23:52 +01:00
SleepEx(0, FALSE); // let select in other thread(s) shutdown gracefully
2001-05-23 15:26:42 +02:00
WSACleanup();
return;
}
#endif
for (rem_port* port = main_port; port; port = port->port_next)
if (port->port_state != rem_port::BROKEN)
{
port->port_state = rem_port::BROKEN;
shutdown((int) port->port_handle, 2);
SOCLOSE((SOCKET) port->port_handle);
}
2001-05-23 15:26:42 +02:00
}
#ifdef NO_FORK
static int fork(void)
{
/**************************************
*
* f o r k ( N O _ F O R K )
*
**************************************
*
* Functional description
* Hmmm.
*
**************************************/
return 1;
}
#endif
#ifdef WIN_NT
static int fork( SOCKET old_handle, USHORT flag)
{
/**************************************
*
* f o r k ( W I N _ N T )
*
**************************************
*
* Functional description
* Create a child process.
*
**************************************/
2006-11-28 11:21:21 +01:00
TEXT name[MAXPATHLEN];
GetModuleFileName(NULL, name, sizeof(name));
2001-05-23 15:26:42 +02:00
HANDLE new_handle;
2001-05-23 15:26:42 +02:00
DuplicateHandle(GetCurrentProcess(), (HANDLE) old_handle,
GetCurrentProcess(), &new_handle, 0, TRUE,
DUPLICATE_SAME_ACCESS);
2006-11-28 11:21:21 +01:00
Firebird::string cmdLine;
cmdLine.printf("%s -i -h %"SLONGFORMAT, name, (SLONG) new_handle);
2003-12-03 09:19:24 +01:00
STARTUPINFO start_crud;
2001-05-23 15:26:42 +02:00
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;
2006-11-28 11:21:21 +01:00
if (CreateProcess(NULL, cmdLine.begin(), NULL, NULL, TRUE,
(flag & SRVR_high_priority ?
HIGH_PRIORITY_CLASS | DETACHED_PROCESS :
NORMAL_PRIORITY_CLASS | DETACHED_PROCESS),
NULL, NULL, &start_crud, &pi))
2003-12-03 09:19:24 +01:00
{
2001-05-23 15:26:42 +02:00
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
CloseHandle(new_handle);
return 1;
}
#endif
2003-03-19 14:14:09 +01:00
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.
*
**************************************/
2003-03-19 14:14:09 +01:00
in_addr config_address;
const char* config_option = Config::getRemoteBindAddress();
2003-03-19 14:14:09 +01:00
config_address.s_addr =
(config_option) ? inet_addr(config_option) : INADDR_NONE;
if (config_address.s_addr == INADDR_NONE) {
config_address.s_addr = INADDR_ANY;
}
return config_address;
}
2008-01-16 08:55:28 +01:00
static in_addr get_host_address(const Firebird::string& name,
in_addr* const host_addr_arr,
const int arr_size)
{
/**************************************
*
2003-03-19 14:14:09 +01:00
* g e t _ h o s t _ a d d r e s s
*
**************************************
*
* Functional description
2008-01-16 08:55:28 +01:00
* Fills array with addresses up to arr_size (must be at least 2)
* Return first address from the list.
*
**************************************/
2003-03-19 14:14:09 +01:00
2008-01-16 08:55:28 +01:00
host_addr_arr[0].s_addr = inet_addr(name.c_str());
host_addr_arr[1].s_addr = 0L;
2003-03-19 14:14:09 +01:00
2008-01-16 08:55:28 +01:00
if (host_addr_arr[0].s_addr == INADDR_NONE)
{
const hostent* host = gethostbyname(name.c_str());
2003-03-19 14:14:09 +01:00
/* 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) {
if (H_ERRNO == INET_RETRY_ERRNO) {
for (int retry = 0; retry < INET_RETRY_CALL; retry++) {
if ( (host = gethostbyname(name.c_str())) )
2003-03-19 14:14:09 +01:00
break;
}
}
}
2008-01-16 08:55:28 +01:00
// 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);
for (int i = 0; i < arr_size; ++i)
{
if (list[i] == NULL)
{
// Mark the end of the useful entries in host_addr_arr.
host_addr_arr[i].s_addr = 0L;
break;
}
host_addr_arr[i] = *list[i];
}
2003-03-19 14:14:09 +01:00
}
}
2008-01-16 08:55:28 +01:00
return host_addr_arr[0];
}
2001-05-23 15:26:42 +02:00
//____________________________________________________________
2002-02-16 03:21:35 +01:00
//
2001-05-23 15:26:42 +02:00
// Copy an array of p_cnct::p_cnct_repeat.
2002-02-16 03:21:35 +01:00
//
2001-05-23 15:26:42 +02:00
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) {
2001-05-23 15:26:42 +02:00
pDest[i] = pSource[i];
}
}
#if !(defined WIN_NT)
2004-03-07 08:58:55 +01:00
static int parse_hosts( const TEXT* file_name, const TEXT* host_name,
const TEXT* user_name)
2001-05-23 15:26:42 +02:00
{
/*****************************************************************
*
* p a r s e _ h o s t s
*
*****************************************************************
*
* Functional description:
* Parse hosts file (.rhosts or hosts.equiv) to determine
* if user_name on host_name should be allowed access.
*
*****************************************************************/
TEXT line[BUFFER_SMALL], entry1[BUFFER_SMALL], entry2[BUFFER_SMALL];
2001-05-23 15:26:42 +02:00
int result = -1;
2004-04-29 00:36:29 +02:00
FILE* fp = fopen(file_name, "r");
2001-05-23 15:26:42 +02:00
if (fp) {
for (;;) {
entry1[0] = entry2[0] = 0;
entry1[1] = entry2[1] = 0;
int c;
TEXT* p = line;
2004-04-29 00:36:29 +02:00
while ((c = getc(fp)) != EOF && c != '\n')
2001-05-23 15:26:42 +02:00
*p++ = c;
*p = 0;
sscanf(line, "%s", entry1);
sscanf(&line[strlen(entry1)], "%s", entry2);
result = parse_line(entry1, entry2, host_name, user_name);
if (c == EOF || result > -1)
break;
}
2004-04-29 00:36:29 +02:00
fclose(fp);
2001-05-23 15:26:42 +02:00
}
return result;
}
#endif
#if !(defined WIN_NT)
2001-05-23 15:26:42 +02:00
static int parse_line(
2004-03-07 08:58:55 +01:00
const TEXT* entry1,
const TEXT* entry2,
const TEXT* host_name, const TEXT* user_name)
2001-05-23 15:26:42 +02:00
{
/*****************************************************************
*
2002-02-16 03:21:35 +01:00
* p a r s e _ l i n e
2001-05-23 15:26:42 +02:00
*
*****************************************************************
*
* Functional description:
* Parse hosts file (.rhosts or hosts.equiv) to determine
* if user_name on host_name should be allowed access.
*
2003-09-11 20:59:34 +02:00
* Returns
* 1 if user_name is allowed
* 0 if not allowed and
* -1 if there is not a host_name or a user_name
*
2001-05-23 15:26:42 +02:00
* only supporting:
* + - anybody on any machine
* -@machinegroup [2nd entry optional] - nobody on that machine
* +@machinegroup - anybody on that machine
* +@machinegroup username - this person on these machines
* +@machinegroup +@usergroup - these people on these machines
* +@machinegroup -@usergroup - anybody on these machines EXCEPT these people
* machinename - anyone on this machine
* machinename username - this person on this machine
* machinename +@usergroup - these people on this machine
* machinename -@usergroup - anybody but these people on this machine
* machinename + - anybody on this machine
*
******************************************************************/
/* if entry1 is simply a "+" - everyone's in */
if (!strcmp(entry1, "+"))
return TRUE;
/* if we don't have a host_name match, don't bother */
if (strcmp(entry1, host_name))
2006-04-09 08:46:28 +02:00
{
2008-01-16 08:55:28 +01:00
#if (defined UNIX) && !(defined NETBSD)
2006-04-09 08:46:28 +02:00
if (entry1[1] == '@')
{
2001-05-23 15:26:42 +02:00
if (!innetgr(&entry1[2], host_name, 0, 0))
return -1;
}
else
#endif
return -1;
2006-04-09 08:46:28 +02:00
}
2001-05-23 15:26:42 +02:00
/* if we have a host_name match and an exclude symbol - they're out */
if (entry1[0] == '-')
return FALSE;
/* if we matched the machine and seen a + after the machine
name they are in (eg: <machinename> + ) */
if ((entry2[0] == '+') && (strlen(entry2) == 1))
return TRUE;
/* if we don't have a second entry OR it matches the user_name - they're in */
if (!entry2[0] || !strcmp(entry2, user_name))
return TRUE;
/* if they're in the user group: + they're in, - they're out */
2008-01-16 08:55:28 +01:00
#if (defined UNIX) && !(defined NETBSD)
if (entry2[1] == '@')
{
if (innetgr(&entry2[2], 0, user_name, 0))
return entry2[0] == '+' ? TRUE : FALSE;
2001-05-23 15:26:42 +02:00
2008-01-16 08:55:28 +01:00
// if they're NOT in the user group AND we're excluding it - they're in
if (entry2[0] == '-')
return TRUE;
2001-05-23 15:26:42 +02:00
}
#endif
/* if we get here - we matched machine but not user - maybe next line ... */
return -1;
}
#endif
static rem_port* receive( rem_port* main_port, PACKET * packet)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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 */
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
}
#ifdef DEBUG
{
static ULONG op_rec_count = 0;
op_rec_count++;
if (INET_trace & TRACE_operations) {
fprintf(stdout, "%04lu: OP Recd %5lu opcode %d\n",
inet_debug_timer(),
op_rec_count, packet->p_operation);
fflush(stdout);
2001-05-23 15:26:42 +02:00
}
}
#endif
2001-05-23 15:26:42 +02:00
}
while (packet->p_operation == op_dummy);
return main_port;
2006-03-03 17:20:42 +01:00
}
2008-04-20 16:02:51 +02:00
static bool select_multi(rem_port* main_port, UCHAR* buffer, SSHORT bufsize, SSHORT* length, RemPortPtr& port)
2006-03-03 17:20:42 +01:00
{
/**************************************
*
* 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.
*
**************************************/
2008-04-20 16:02:51 +02:00
2006-03-03 17:20:42 +01:00
for (;;)
{
select_port(main_port, &INET_select, port);
if (port == main_port && (port->port_server_flags & SRVR_multi_client))
2006-03-03 17:20:42 +01:00
{
if (INET_shutting_down)
{
if (main_port->port_state != rem_port::BROKEN)
{
main_port->port_state = rem_port::BROKEN;
SOCKET s = (SOCKET) main_port->port_handle;
shutdown(s, 2);
SOCLOSE(s);
}
}
else if (port = select_accept(main_port))
2006-03-03 17:20:42 +01:00
{
if (!packet_receive(port, buffer, bufsize, length))
{
*length = 0;
}
2008-04-20 16:02:51 +02:00
return (*length) ? true : false;
2006-03-03 17:20:42 +01:00
}
2008-04-20 16:02:51 +02:00
2006-03-03 17:20:42 +01:00
continue;
}
if (port)
{
if (port->port_dummy_timeout < 0)
{
port->port_dummy_timeout = port->port_dummy_packet_interval;
2008-04-20 16:02:51 +02:00
if (port->port_flags & PORT_async || port->port_protocol < PROTOCOL_VERSION8)
{
continue;
}
2006-03-03 17:20:42 +01:00
*length = 0;
2008-04-20 16:02:51 +02:00
return true;
2006-03-03 17:20:42 +01:00
}
2006-03-03 17:20:42 +01:00
if (!packet_receive(port, buffer, bufsize, length))
{
if (port->port_flags & PORT_disconnect) {
continue;
}
2006-03-03 17:20:42 +01:00
*length = 0;
}
2008-04-20 16:02:51 +02:00
return (*length) ? true : false;
2006-03-03 17:20:42 +01:00
}
if (!select_wait(main_port, &INET_select))
{
port = NULL;
2008-04-20 16:02:51 +02:00
return false;
2006-03-03 17:20:42 +01:00
}
}
2001-05-23 15:26:42 +02:00
}
static rem_port* select_accept( rem_port* main_port)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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* port = alloc_port(main_port);
2003-12-03 09:19:24 +01:00
socklen_t l = sizeof(address);
2001-05-23 15:26:42 +02:00
port->port_handle = (HANDLE) accept((SOCKET) main_port->port_handle,
(struct sockaddr *) &address, &l);
if ((SOCKET) port->port_handle == INVALID_SOCKET) {
inet_error(port, "accept", isc_net_connect_err, INET_ERRNO);
2001-05-23 15:26:42 +02:00
disconnect(port);
return 0;
}
2003-12-03 09:19:24 +01:00
int optval = 1;
2001-05-23 15:26:42 +02:00
setsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_KEEPALIVE,
2003-12-03 09:19:24 +01:00
(SCHAR*) &optval, sizeof(optval));
2001-05-23 15:26:42 +02:00
port->port_flags |= PORT_server;
2001-05-23 15:26:42 +02:00
if (main_port->port_server_flags & SRVR_thread_per_port) {
port->port_server_flags =
(SRVR_server | SRVR_inet | SRVR_thread_per_port);
return port;
}
return 0;
}
static void select_port(rem_port* main_port, SLCT* selct, RemPortPtr& port)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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);
2001-05-23 15:26:42 +02:00
for (port = main_port; port; port = port->port_next)
{
#ifdef WIN_NT
const SOCKET n = (SOCKET) port->port_handle;
const int ok = FD_ISSET(n, &selct->slct_fdset);
#else
2003-12-03 09:19:24 +01:00
const int n = (int) port->port_handle;
if (n < 0 || n >= FD_SETSIZE) {
2008-04-17 18:25:23 +02:00
return;
}
const int ok = n < selct->slct_width && FD_ISSET(n, &selct->slct_fdset);
#endif
if (ok)
{
2001-05-23 15:26:42 +02:00
port->port_dummy_timeout = port->port_dummy_packet_interval;
FD_CLR(n, &selct->slct_fdset);
--selct->slct_count;
return;
2001-05-23 15:26:42 +02:00
}
2008-01-16 08:55:28 +01:00
if (port->port_dummy_timeout < 0) {
return;
2001-05-23 15:26:42 +02:00
}
}
}
static int select_wait( rem_port* main_port, SLCT * selct)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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;
2001-05-23 15:26:42 +02:00
for (;;)
{
selct->slct_count = selct->slct_width = 0;
FD_ZERO(&selct->slct_fdset);
2003-12-03 09:19:24 +01:00
bool found = false;
2001-05-23 15:26:42 +02:00
/* Use the time interval between select() calls to expire
keepalive timers on all ports. */
2008-04-20 16:35:23 +02:00
time_t delta_time;
2001-05-23 15:26:42 +02:00
if (selct->slct_time)
{
2008-04-20 16:35:23 +02:00
delta_time = time(NULL) - selct->slct_time;
2001-05-23 15:26:42 +02:00
selct->slct_time += delta_time;
}
else
{
delta_time = 0;
2008-04-20 16:35:23 +02:00
selct->slct_time = time(NULL);
2001-05-23 15:26:42 +02:00
}
{ // port_mutex scope
Firebird::MutexLockGuard guard(port_mutex);
for (rem_port* port = main_port; port; port = port->port_next)
2001-05-23 15:26:42 +02:00
{
if (port->port_state == rem_port::PENDING)
2001-05-23 15:26:42 +02:00
{
/* Adjust down the port's keepalive timer. */
2001-05-23 15:26:42 +02:00
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
((SOCKET) port->port_handle < 0) ||
((SOCKET) port->port_handle) >= FD_SETSIZE;
#endif
if (badSocket || getsockopt((SOCKET) port->port_handle,
SOL_SOCKET, SO_LINGER, (SCHAR*) &lngr, &optlen) != 0)
{
if (badSocket || INET_ERRNO == NOTASOCKET)
{
// not a socket, strange !
gds__log("INET/select_wait: found \"not a socket\" socket : %u", (SOCKET) port->port_handle);
// this will lead to receive() which will break bad connection
selct->slct_count = selct->slct_width = 0;
FD_ZERO(&selct->slct_fdset);
if (!badSocket)
{
FD_SET((SLONG) port->port_handle, &selct->slct_fdset);
#ifdef WIN_NT
++selct->slct_width;
#else
selct->slct_width = (int) port->port_handle + 1;
#endif
}
return true;
}
}
}
// if process is shuting down - don't listen on main port
if (!INET_shutting_down || port != main_port)
{
FD_SET((SLONG) port->port_handle, &selct->slct_fdset);
#ifdef WIN_NT
++selct->slct_width;
#else
selct->slct_width = MAX(selct->slct_width, (int) port->port_handle + 1);
#endif
found = true;
}
}
2001-05-23 15:26:42 +02:00
}
checkPorts = false;
} // port_mutex scope
2001-05-23 15:26:42 +02:00
if (!found)
{
if (!INET_shutting_down && (main_port->port_server_flags & SRVR_multi_client)) {
gds__log("INET/select_wait: client rundown complete, server exiting", 0);
}
2001-05-23 15:26:42 +02:00
return FALSE;
}
for (;;)
{
2008-02-06 18:26:35 +01:00
// Before waiting for incoming packet, check for server shutdown
if (tryStopMainThread)
{
tryStopMainThread();
}
2001-05-23 15:26:42 +02:00
// Some platforms change the timeout in the select call.
// Reset timeout for each iteration to avoid problems.
timeout.tv_sec = SELECT_TIMEOUT;
timeout.tv_usec = 0;
#ifdef WIN_NT
selct->slct_count = select(FD_SETSIZE, &selct->slct_fdset,
NULL, NULL, &timeout);
2001-05-23 15:26:42 +02:00
#else
selct->slct_count = select(selct->slct_width, &selct->slct_fdset,
NULL, NULL, &timeout);
2001-05-23 15:26:42 +02:00
#endif
const int inetErrNo = INET_ERRNO;
//if (INET_shutting_down) {
// return FALSE;
//}
2001-05-23 15:26:42 +02:00
if (selct->slct_count != -1)
{
/* if selct->slct_count is zero it means that we timed out of
select with nothing to read or accept, so clear the fd_set
bit as this value is undefined on some platforms (eg. HP-UX),
2001-05-23 15:26:42 +02:00
when the select call times out. Once these bits are cleared
they can be used in select_port() */
if (selct->slct_count == 0)
{
for (rem_port* port = main_port; port; port = port->port_next)
2001-05-23 15:26:42 +02:00
{
#ifdef WIN_NT
FD_CLR((SOCKET)port->port_handle, &selct->slct_fdset);
#else
FD_CLR(port->port_handle, &selct->slct_fdset);
#endif
}
}
return TRUE;
}
2008-01-16 08:55:28 +01:00
if (INTERRUPT_ERROR(inetErrNo))
2001-05-23 15:26:42 +02:00
continue;
2008-01-16 08:55:28 +01:00
if (inetErrNo == NOTASOCKET)
{
checkPorts = true;
break;
}
2008-01-16 08:55:28 +01:00
2008-03-17 10:08:48 +01:00
gds__log("INET/select_wait: select failed, errno = %d", inetErrNo);
2008-01-16 08:55:28 +01:00
return FALSE;
2001-05-23 15:26:42 +02:00
} // for (;;)
}
}
static int send_full( rem_port* port, PACKET * packet)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s e n d _ f u l l
*
**************************************
*
* Functional description
* Send a packet across a port to another process.
*
**************************************/
if (!xdr_protocol(&port->port_send, packet))
return FALSE;
#ifdef DEBUG
{
static ULONG op_sent_count = 0;
op_sent_count++;
if (INET_trace & TRACE_operations) {
2004-04-29 00:36:29 +02:00
fprintf(stdout, "%05lu: OP Sent %5lu opcode %d\n",
2001-05-23 15:26:42 +02:00
inet_debug_timer(),
op_sent_count, packet->p_operation);
2004-04-29 00:36:29 +02:00
fflush(stdout);
}
}
2001-05-23 15:26:42 +02:00
#endif
2006-02-04 11:37:15 +01:00
return inet_write(&port->port_send, TRUE);
2001-05-23 15:26:42 +02:00
}
static int send_partial( rem_port* port, PACKET * packet)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s e n d _ p a r t i a l
*
**************************************
*
* Functional description
* Send a packet across a port to another process.
*
**************************************/
#ifdef DEBUG
{
static ULONG op_sentp_count = 0;
op_sentp_count++;
if (INET_trace & TRACE_operations) {
2004-04-29 00:36:29 +02:00
fprintf(stdout, "%05lu: OP Sent %5lu opcode %d (partial)\n",
2001-05-23 15:26:42 +02:00
inet_debug_timer(),
op_sentp_count, packet->p_operation);
2004-04-29 00:36:29 +02:00
fflush(stdout);
}
}
2001-05-23 15:26:42 +02:00
#endif
return xdr_protocol(&port->port_send, packet);
}
static int xdrinet_create(
XDR * xdrs,
rem_port* port,
2001-05-23 15:26:42 +02:00
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);
2001-05-23 15:26:42 +02:00
xdrs->x_handy = length;
xdrs->x_ops = (xdr_t::xdr_ops *) & inet_ops;
xdrs->x_op = x_op;
return TRUE;
}
#ifdef HAVE_SETITIMER
2001-05-23 15:26:42 +02:00
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
2008-01-16 08:55:28 +01:00
2003-12-03 09:19:24 +01:00
static XDR_INT inet_destroy( XDR* xdrs)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n e t _ d e s t r o y
*
**************************************
*
* Functional description
* Destroy a stream. A no-op.
*
**************************************/
return (XDR_INT)0;
2001-05-23 15:26:42 +02:00
}
static void inet_gen_error( rem_port* port, ISC_STATUS status, ...)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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;
2001-05-23 15:26:42 +02:00
ISC_STATUS* status_vector = NULL;
2001-05-23 15:26:42 +02:00
if (port->port_context != NULL) {
status_vector = port->port_context->rdb_status_vector;
}
if (status_vector == NULL) {
status_vector = port->port_status_vector;
}
if (status_vector != NULL) {
STUFF_STATUS(status_vector, status);
REMOTE_save_status_strings(status_vector);
}
}
2007-11-19 01:21:18 +01:00
static bool_t inet_getbytes( XDR * xdrs, SCHAR * buff, u_int count)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n e t _ g e t b y t e s
*
**************************************
*
* Functional description
* Get a bunch of bytes from a memory stream if it fits.
*
**************************************/
#ifdef REM_SERVER
2008-03-13 11:41:38 +01:00
const rem_port* port = (rem_port*) xdrs->x_public;
if (port->port_flags & PORT_server) {
return REMOTE_getbytes(xdrs, buff, count);
}
#endif
2001-05-23 15:26:42 +02:00
SLONG bytecount = count;
/* Use memcpy to optimize bulk transfers. */
2008-01-16 08:55:28 +01:00
while (bytecount > (SLONG) sizeof(ISC_QUAD))
{
if (xdrs->x_handy >= bytecount)
{
2001-05-23 15:26:42 +02:00
memcpy(buff, xdrs->x_private, bytecount);
xdrs->x_private += bytecount;
xdrs->x_handy -= bytecount;
return TRUE;
}
2008-01-16 08:55:28 +01:00
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;
2001-05-23 15:26:42 +02:00
}
2008-01-16 08:55:28 +01:00
if (!inet_read(xdrs))
return FALSE;
}
2001-05-23 15:26:42 +02:00
/* 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;
2006-04-09 08:46:28 +02:00
while (bytecount--)
2001-05-23 15:26:42 +02:00
*buff++ = *xdrs->x_private++;
2006-04-09 08:46:28 +02:00
2001-05-23 15:26:42 +02:00
return TRUE;
}
while (--bytecount >= 0) {
if (!xdrs->x_handy && !inet_read(xdrs))
return FALSE;
*buff++ = *xdrs->x_private++;
--xdrs->x_handy;
}
return TRUE;
}
2008-03-13 11:41:38 +01:00
2001-05-23 15:26:42 +02:00
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;
2003-12-03 09:19:24 +01:00
if (!(*xdrs->x_ops->x_getbytes) (xdrs, reinterpret_cast<char*>(&l), 4))
2001-05-23 15:26:42 +02:00
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);
}
#if !(defined WIN_NT)
static void inet_handler(void* port_void)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n e t _ h a n d l e r
*
**************************************
*
* Functional description
* Inet_handler is called the the signal handler on receipt of
* a SIGURG signal indicating out-of-band data. Since SIGURG
* may be noisy, check to see if any IO is pending on the channel.
* If not, ignore the signal. If so, call the port specific
2001-05-23 15:26:42 +02:00
* handler to do something appropriate.
*
**************************************/
rem_port* port = static_cast<rem_port*>(port_void);
2001-05-23 15:26:42 +02:00
/* If there isn't any out of band data, this signal isn't for us */
2003-12-03 09:19:24 +01:00
SCHAR junk;
const int n = recv((SOCKET) port->port_handle, &junk, 1, MSG_OOB);
2006-04-09 08:46:28 +02:00
if (n < 0) {
2001-05-23 15:26:42 +02:00
return;
}
2003-12-03 09:19:24 +01:00
2004-03-07 08:58:55 +01:00
(*port->port_ast) (port);
2001-05-23 15:26:42 +02:00
}
#endif
2003-12-03 09:19:24 +01:00
static caddr_t inet_inline( XDR* xdrs, u_int bytecount)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n e t _ i n l i n e
*
**************************************
*
* Functional description
* Return a pointer to somewhere in the buffer.
*
**************************************/
if (bytecount > (u_int) xdrs->x_handy)
return FALSE;
return xdrs->x_base + bytecount;
}
static void inet_error(
rem_port* port,
2003-12-03 09:19:24 +01:00
const TEXT* function, ISC_STATUS operation, int status)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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) {
2008-01-16 08:55:28 +01:00
inet_gen_error(port, isc_network_error,
isc_arg_string,
(ISC_STATUS) port->port_connection->str_data,
isc_arg_gds, operation, SYS_ERR, status, 0);
2001-05-23 15:26:42 +02:00
2008-03-17 10:08:48 +01:00
gds__log("INET/inet_error: %s errno = %d", function, status);
2001-05-23 15:26:42 +02:00
}
else {
/* No status value, just format the basic arguments. */
inet_gen_error(port, isc_network_error,
2003-11-08 17:40:17 +01:00
isc_arg_string,
2003-04-10 12:31:28 +02:00
(ISC_STATUS) port->port_connection->str_data, isc_arg_gds,
2001-05-23 15:26:42 +02:00
operation, 0);
}
}
static bool_t inet_putbytes( XDR* xdrs, const SCHAR* buff, u_int count)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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. */
2008-01-16 08:55:28 +01:00
while (bytecount > (SLONG) sizeof(ISC_QUAD))
{
if (xdrs->x_handy >= bytecount)
{
2001-05-23 15:26:42 +02:00
memcpy(xdrs->x_private, buff, bytecount);
xdrs->x_private += bytecount;
xdrs->x_handy -= bytecount;
return TRUE;
}
2008-01-16 08:55:28 +01:00
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;
2001-05-23 15:26:42 +02:00
}
2008-01-16 08:55:28 +01:00
if (!inet_write(xdrs, 0))
return FALSE;
2001-05-23 15:26:42 +02:00
}
/* 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;
2006-04-09 08:46:28 +02:00
while (bytecount--)
2001-05-23 15:26:42 +02:00
*xdrs->x_private++ = *buff++;
2006-04-09 08:46:28 +02:00
2001-05-23 15:26:42 +02:00
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;
}
2003-12-03 09:19:24 +01:00
// CVC: It could be const SLONG* lp, but it should fit into xdr_ops' signature.
static bool_t inet_putlong( XDR* xdrs, const SLONG* lp)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n e t _ p u t l o n g
*
**************************************
*
* Functional description
* Fetch a longword into a memory stream if it fits.
*
**************************************/
2003-12-03 09:19:24 +01:00
const SLONG l = htonl(*lp);
2001-05-23 15:26:42 +02:00
return (*xdrs->x_ops->x_putbytes) (xdrs,
2008-01-16 08:55:28 +01:00
reinterpret_cast<const char*>(&l),
2001-05-23 15:26:42 +02:00
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;
2003-12-03 09:19:24 +01:00
char* p = xdrs->x_base;
const char* const end = p + INET_remote_buffer;
2001-05-23 15:26:42 +02:00
/* 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) {
2003-12-03 09:19:24 +01:00
SSHORT length = end - p;
2008-03-10 09:44:07 +01:00
if (!packet_receive(port, reinterpret_cast<UCHAR*>(p), length, &length))
2006-04-09 08:46:28 +02:00
{
2001-05-23 15:26:42 +02:00
return FALSE;
}
if (length >= 0) {
p += length;
break;
}
p -= length;
if (!packet_send(port, 0, 0))
return FALSE;
}
xdrs->x_handy = (int) ((SCHAR *) p - xdrs->x_base);
xdrs->x_private = xdrs->x_base;
return TRUE;
}
static bool_t inet_setpostn( XDR * xdrs, u_int bytecount)
{
/**************************************
*
* i n e t _ s e t p o s t n
*
**************************************
*
* Functional description
* Set the current position (which is also current length) from stream.
*
**************************************/
if (bytecount > (u_int) xdrs->x_handy)
return FALSE;
xdrs->x_private = xdrs->x_base + bytecount;
return TRUE;
}
static rem_port* inet_try_connect(
PACKET* packet,
Rdb* rdb,
const Firebird::PathName& file_name,
const TEXT* node_name,
ISC_STATUS* status_vector,
Firebird::ClumpletReader& dpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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.
*
**************************************/
2003-12-03 09:19:24 +01:00
P_CNCT* cnct = &packet->p_cnct;
2001-05-23 15:26:42 +02:00
packet->p_operation = op_connect;
cnct->p_cnct_operation = op_attach;
cnct->p_cnct_cversion = CONNECT_VERSION2;
cnct->p_cnct_client = ARCHITECTURE;
cnct->p_cnct_file.cstr_length = file_name.length();
cnct->p_cnct_file.cstr_address = reinterpret_cast<const UCHAR*>(file_name.c_str());
2001-05-23 15:26:42 +02:00
/* If we can't talk to a server, punt. Let somebody else generate
an error. status_vector will have the network error info. */
rem_port* port = INET_connect(node_name, packet, status_vector, FALSE, &dpb);
2001-05-23 15:26:42 +02:00
if (!port) {
delete rdb;
2001-05-23 15:26:42 +02:00
return NULL;
}
/* Get response packet from server. */
rdb->rdb_port = port;
port->port_context = rdb;
if (!port->receive(packet)) {
inet_error(port, "receive in try_connect", isc_net_connect_err,
INET_ERRNO);
2001-05-23 15:26:42 +02:00
disconnect(port);
delete rdb;
2001-05-23 15:26:42 +02:00
return NULL;
}
return port;
}
static bool_t inet_write( XDR * xdrs, bool_t end_flag)
{
/**************************************
*
* i n e t _ w r i t e
*
**************************************
*
* Functional description
* Write a buffer full of data. 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;
2003-12-03 09:19:24 +01:00
const char* p = xdrs->x_base;
SSHORT length = xdrs->x_private - p;
2001-05-23 15:26:42 +02:00
/* Send data in manageable hunks. If a packet is partial, indicate
that with a negative length. A positive length marks the end. */
2003-12-03 09:19:24 +01:00
//p = xdrs->x_base; redundant
2001-05-23 15:26:42 +02:00
while (length) {
const SSHORT l = (SSHORT) MIN(length, INET_remote_buffer);
2001-05-23 15:26:42 +02:00
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
2003-12-03 09:19:24 +01:00
// 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.
2001-05-23 15:26:42 +02:00
/* 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. */
2003-12-03 09:19:24 +01:00
char* p2 = aux_buffer;
2001-05-23 15:26:42 +02:00
while (l2 < 0) {
if (!packet_send(port, 0, 0))
return FALSE;
2003-12-03 09:19:24 +01:00
p2 -= l2;
length = aux_buffer + sizeof(aux_buffer) - p2;
if (!packet_receive(port, p2, length, &l2)) {
p2 += l2;
2001-05-23 15:26:42 +02:00
continue;
}
}
2003-12-03 09:19:24 +01:00
length = p2 - aux_buffer + l2;
2001-05-23 15:26:42 +02:00
/* 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;
2003-12-03 09:19:24 +01:00
p2 = xdrs->x_base;
2001-05-23 15:26:42 +02:00
2003-12-03 09:19:24 +01:00
if (xdrs->x_handy && p2 != xdrs->x_private) {
memmove(p2, xdrs->x_private, xdrs->x_handy);
}
2001-05-23 15:26:42 +02:00
2003-12-03 09:19:24 +01:00
p2 += xdrs->x_handy;
2001-05-23 15:26:42 +02:00
xdrs->x_private = xdrs->x_base;
/*
2003-12-03 09:19:24 +01:00
xdrs->x_handy += JAP_decode (aux_buffer, length, p2);
2001-05-23 15:26:42 +02:00
*/
return TRUE;
#endif
}
#ifdef DEBUG
static void packet_print(
const TEXT* string,
2006-03-07 06:44:13 +01:00
const UCHAR* packet, int length, ULONG counter)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p a c k e t _ p r i n t
*
**************************************
*
* Functional description
* Print a summary of packet.
*
**************************************/
int sum = 0;
2006-04-09 08:46:28 +02:00
for (int l = length; l > 0; --l)
sum += *packet++;
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
fprintf(stdout,
2006-03-07 06:44:13 +01:00
"%05lu: PKT %s\t(%lu): length = %4d, checksum = %d\n",
2001-05-23 15:26:42 +02:00
inet_debug_timer(), string, counter, length, sum);
2004-04-29 00:36:29 +02:00
fflush(stdout);
2001-05-23 15:26:42 +02:00
}
#endif
static int packet_receive(
rem_port* port,
2003-12-03 09:19:24 +01:00
UCHAR* buffer,
SSHORT buffer_length, SSHORT* length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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;
}
2003-12-03 09:19:24 +01:00
timeval timeout;
2001-05-23 15:26:42 +02:00
timeout.tv_usec = 0;
2003-12-03 09:19:24 +01:00
timeval* time_ptr = NULL;
2001-05-23 15:26:42 +02:00
if (port->port_protocol == 0)
{
2008-04-20 16:02:51 +02:00
// If the protocol is 0 we are still in the process of establishing
// a connection. Add a time out to the wait.
2001-05-23 15:26:42 +02:00
timeout.tv_sec = port->port_connect_timeout;
time_ptr = &timeout;
}
2008-04-20 16:02:51 +02:00
else if (port->port_protocol >= PROTOCOL_VERSION8 &&
2008-04-24 03:53:40 +02:00
port->port_dummy_packet_interval > 0)
2008-04-20 16:02:51 +02:00
{
// Set the time interval for sending dummy packets to the client
timeout.tv_sec = port->port_dummy_packet_interval;
time_ptr = &timeout;
}
2001-05-23 15:26:42 +02:00
2008-04-20 16:02:51 +02:00
// On Linux systems (and possibly others too) select will eventually
2001-05-23 15:26:42 +02:00
// change timout values so save it here for later reuse.
// Thanks to Brad Pepers who reported this bug FSG 3 MAY 2001
2004-06-21 04:48:47 +02:00
const timeval savetime = timeout;
2003-12-03 09:19:24 +01:00
int ph = (int) port->port_handle;
2001-05-23 15:26:42 +02:00
2003-12-03 09:19:24 +01:00
// Unsed to send a dummy packet, but too big to be defined in the loop.
PACKET packet;
2001-05-23 15:26:42 +02:00
2003-12-03 09:19:24 +01:00
int n = 0;
int inetErrNo;
2003-12-03 09:19:24 +01:00
2001-05-23 15:26:42 +02:00
for (;;)
{
/* Implement an error-detection protocol to ensure that the client
is still there. Use the select() call with a timeout to wait on
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
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) )
2001-05-23 15:26:42 +02:00
{
2003-12-03 09:19:24 +01:00
fd_set slct_fdset;
2001-05-23 15:26:42 +02:00
FD_ZERO(&slct_fdset);
FD_SET(ph, &slct_fdset);
2003-12-03 09:19:24 +01:00
int slct_count;
2001-05-23 15:26:42 +02:00
for (;;) {
2002-10-30 07:40:58 +01:00
#if (defined WIN_NT)
slct_count = select(FD_SETSIZE, &slct_fdset, NULL, NULL, time_ptr);
2001-05-23 15:26:42 +02:00
#else
slct_count = select((SOCKET) port->port_handle + 1, &slct_fdset,
NULL, NULL, time_ptr);
2001-05-23 15:26:42 +02:00
#endif
inetErrNo = INET_ERRNO;
2001-05-23 15:26:42 +02:00
// restore original timeout value FSG 3 MAY 2001
timeout = savetime;
2001-05-23 15:26:42 +02:00
if (slct_count != -1 || !INTERRUPT_ERROR(inetErrNo))
2001-05-23 15:26:42 +02:00
{
break;
}
}
if (slct_count == -1)
{
if (!(port->port_flags & PORT_disconnect)) {
inet_error(port, "select in packet_receive", isc_net_read_err, inetErrNo);
}
return FALSE;
2001-05-23 15:26:42 +02:00
}
if (!slct_count && port->port_protocol >= PROTOCOL_VERSION8)
{
#ifdef DEBUG
if (INET_trace & TRACE_operations)
{
2004-04-29 00:36:29 +02:00
fprintf(stdout, "%05lu: OP Sent: op_dummy\n",
2001-05-23 15:26:42 +02:00
inet_debug_timer());
2004-04-29 00:36:29 +02:00
fflush(stdout);
2001-05-23 15:26:42 +02:00
}
#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((SOCKET) port->port_handle, reinterpret_cast<char*>(buffer), buffer_length, 0);
inetErrNo = INET_ERRNO;
if (n != -1 || !INTERRUPT_ERROR(inetErrNo))
2001-05-23 15:26:42 +02:00
break;
}
if ((n <= 0) && (port->port_flags & PORT_disconnect)) {
return FALSE;
}
if (n == -1) {
inet_error(port, "read", isc_net_read_err, inetErrNo);
2001-05-23 15:26:42 +02:00
return FALSE;
}
2001-05-23 15:26:42 +02:00
if (!n) {
inet_error(port, "read end_of_file", isc_net_read_err, 0);
return FALSE;
}
2001-05-23 15:26:42 +02:00
#ifdef DEBUG
{
INET_count_recv++;
INET_bytes_recv += n;
if (INET_trace & TRACE_packets)
packet_print("receive", buffer, n, INET_count_recv);
INET_force_error--;
if (INET_force_error == 0) {
INET_force_error = 1;
inet_error(port, "simulated error - read",
isc_net_read_err, 0);
return FALSE;
2001-05-23 15:26:42 +02:00
}
}
#endif
*length = n;
return TRUE;
}
static bool_t packet_send( rem_port* port, const SCHAR* buffer, SSHORT buffer_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p a c k e t _ s e n d
*
**************************************
*
* Functional description
* Send some data on it's way.
2001-05-23 15:26:42 +02:00
*
**************************************/
const char* data = buffer;
2003-12-03 09:19:24 +01:00
SSHORT length = buffer_length;
2001-05-23 15:26:42 +02:00
while (length) {
#ifdef DEBUG
if (INET_trace & TRACE_operations) {
2004-04-29 00:36:29 +02:00
fprintf(stdout, "Before Send\n");
fflush(stdout);
2001-05-23 15:26:42 +02:00
}
#endif
2003-12-03 09:19:24 +01:00
SSHORT n = -1;
n = send((SOCKET) port->port_handle, data, length, FB_SEND_FLAGS);
2001-05-23 15:26:42 +02:00
#ifdef DEBUG
if (INET_trace & TRACE_operations) {
2004-04-29 00:36:29 +02:00
fprintf(stdout, "After Send n is %d\n", n);
fflush(stdout);
2001-05-23 15:26:42 +02:00
}
#endif
if (n == length) {
break;
}
2001-05-23 15:26:42 +02:00
if (n == -1)
{
if (INTERRUPT_ERROR(INET_ERRNO)) {
2001-05-23 15:26:42 +02:00
continue;
}
inet_error(port, "send", isc_net_write_err, INET_ERRNO);
return FALSE;
2001-05-23 15:26:42 +02:00
}
data += n;
length -= n;
}
2004-12-26 05:09:06 +01:00
#ifdef HAVE_SETITIMER
struct itimerval internal_timer, client_timer;
struct sigaction internal_handler, client_handler;
#endif /* HAVE_SETITIMER */
2001-05-23 15:26:42 +02:00
if ((port->port_flags & PORT_async) && !(port->port_flags & PORT_no_oob))
{
2003-12-03 09:19:24 +01:00
int count = 0;
SSHORT n;
2008-01-16 15:37:26 +01:00
int inetErrNo = 0;
const char* b = buffer;
while ((n = send((SOCKET) port->port_handle, b, 1, MSG_OOB | FB_SEND_FLAGS)) == -1 &&
(INET_ERRNO == ENOBUFS || INTERRUPT_ERROR(INET_ERRNO)))
2001-05-23 15:26:42 +02:00
{
inetErrNo = INET_ERRNO;
2001-05-23 15:26:42 +02:00
if (count++ > 20) {
break;
}
#ifndef HAVE_SETITIMER
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
SleepEx(50, TRUE);
#else
sleep(1);
#endif
} // end of while() loop for systems without setitimer.
#else /* HAVE_SETITIMER */
2001-05-23 15:26:42 +02:00
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);
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
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 */
2001-05-23 15:26:42 +02:00
if (n == -1) {
inet_error(port, "send/oob", isc_net_write_err, inetErrNo);
return FALSE;
2001-05-23 15:26:42 +02:00
}
}
#ifdef DEBUG
{
INET_count_send++;
INET_bytes_send += buffer_length;
if (INET_trace & TRACE_packets)
packet_print("send", (const UCHAR*) buffer, buffer_length, INET_count_send);
2001-05-23 15:26:42 +02:00
INET_force_error--;
if (INET_force_error == 0) {
INET_force_error = 1;
inet_error(port, "simulated error - send",
isc_net_write_err, 0);
return FALSE;
2001-05-23 15:26:42 +02:00
}
}
#endif
return TRUE;
}
static void unhook_port( rem_port* port, rem_port* parent)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* u n h o o k _ p a r e n t
*
**************************************
*
* Functional description
* Disconnect a port from its parent
* This must be done under port_mutex control.
2001-05-23 15:26:42 +02:00
*
**************************************/
for (rem_port** ptr = &parent->port_clients; *ptr; ptr = &(*ptr)->port_next) {
2001-05-23 15:26:42 +02:00
if (*ptr == port) {
*ptr = port->port_next;
if (ptr == &parent->port_clients) {
parent->port_next = *ptr;
}
break;
}
}
}
2006-05-24 16:08:06 +02:00
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((SOCKET) port->port_handle, IPPROTO_TCP,
2006-05-24 16:08:06 +02:00
TCP_NODELAY, (SCHAR*) &optval, sizeof(optval));
if (n == -1)
2006-05-24 16:08:06 +02:00
{
return false;
}
}
return true;
}
2008-02-06 18:26:35 +01:00
void setStopMainThread(FPTR_VOID 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;
}
static int shut_preproviders()
{
INET_shutting_down = true;
return 0;
}