Logo Search packages:      
Sourcecode: firebird1.5 version File versions

wnet.cpp

/*
 *    PROGRAM:    JRD Remote Interface/Server
 *    MODULE:           wnet.c
 *    DESCRIPTION:      Windows Net 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): ______________________________________.
 */

#ifdef DEBUG
/* define WNET_trace to 0 (zero) for no packet debugging */
#define WNET_trace      1
#endif

#include "firebird.h"
#include "../jrd/ib_stdio.h"
#include <string.h>
#include "../remote/remote.h"
#include "../jrd/gds.h"
#include "../jrd/thd.h"
#include "../jrd/iberr.h"

#include "../utilities/install_nt.h"

#include "../remote/proto_proto.h"
#include "../remote/remot_proto.h"
#include "../remote/wnet_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/isc_f_proto.h"
#include "../jrd/sch_proto.h"
#include "../common/config/config.h"

#include <stdarg.h>

#include <windows.h>
#ifdef TEXT
#undef TEXT
#endif
#define TEXT            SCHAR

#define ERRNO           GetLastError()

#ifndef SYS_ERR
#define SYS_ERR         gds_arg_win32
#endif

#ifndef MAX_DATA
#define MAX_DATA  2048
#endif

#define BUFFER_SIZE     MAX_DATA
#define MAX_SEQUENCE    256

#ifndef MAXHOSTLEN
#define MAXHOSTLEN      64
#endif

#define PIPE_PREFIX "pipe" // win32-specific
#define SERVER_PIPE_SUFFIX "server"
#define EVENT_PIPE_SUFFIX "event"

extern int xdrmem_create();

static int        accept_connection(PORT, P_CNCT *);
static PORT       alloc_port(PORT);
static PORT       aux_connect(PORT, PACKET *, XDR_INT(*)(void));
static PORT       aux_request(PORT, PACKET *);
static void       cleanup_port(PORT);
static void       disconnect(PORT);
static void       exit_handler(PORT);
static STR        make_pipe_name(TEXT *, TEXT *, TEXT *);
static PORT       receive(PORT, PACKET *);
static int        send_full(PORT, PACKET *);
static int        send_partial(PORT, PACKET *);
static int        xdrwnet_create(XDR *, PORT, UCHAR *, USHORT, enum xdr_op);
static bool_t     xdrwnet_endofrecord(XDR *, int);
static int        wnet_destroy(XDR *);
static int        wnet_error(PORT, TEXT *, ISC_STATUS, int);
static void       wnet_gen_error(PORT, ISC_STATUS, ...);
static bool_t     wnet_getbytes(XDR *, SCHAR *, u_int);
static bool_t     wnet_getlong(XDR *, SLONG *);
static u_int      wnet_getpostn(XDR *);
static caddr_t    wnet_inline(XDR *, u_int);
static bool_t     wnet_putlong(XDR *, SLONG *);
static bool_t     wnet_putbytes(XDR *, SCHAR *, u_int);
static bool_t     wnet_read(XDR *);
static bool_t     wnet_setpostn(XDR *, u_int);
static bool_t     wnet_write(XDR *, int);
#ifdef DEBUG
static void       packet_print(TEXT *, UCHAR *, int);
#endif
static int        packet_receive(PORT, UCHAR *, SSHORT, SSHORT *);
static int        packet_send(PORT, SCHAR *, SSHORT);
static void       wnet_copy(SCHAR *, SCHAR *, int);
static void       wnet_make_file_name(TEXT *, DWORD);

static xdr_t::xdr_ops wnet_ops =
{
      wnet_getlong,
      wnet_putlong,
      wnet_getbytes,
      wnet_putbytes,
      wnet_getpostn,
      wnet_setpostn,
      wnet_inline,
      wnet_destroy
};

#ifndef MAX_PTYPE
#define MAX_PTYPE ptype_out_of_band
#endif


PORT WNET_analyze(      TEXT* file_name,
                              USHORT*     file_length,
                              ISC_STATUS* status_vector,
                              TEXT* node_name,
                              TEXT* user_string,
                              USHORT      uv_flag)
{
/**************************************
 *
 *    W N E T _ a n a l y z e
 *
 **************************************
 *
 * Functional description
 *    Determine whether the file name has a "\\nodename".
 *    If so, establish an external connection to the node.
 *
 *    If a connection is established, return a port block, otherwise
 *    return NULL.
 *
 **************************************/
      RDB rdb;
      PACKET *packet;
      P_CNCT *cnct;
      SSHORT user_length;
      UCHAR *p, user_id[200];
      TEXT buffer[64];

      *file_length = strlen(file_name);

/* We need to establish a connection to a remote server.  Allocate the necessary
   blocks and get ready to go. */

      rdb = (RDB) ALLOC(type_rdb);
      packet = &rdb->rdb_packet;

/* Pick up some user identification information */

      user_id[0] = CNCT_user;
      p = user_id + 2;
      ISC_get_user(reinterpret_cast < SCHAR * >(p), 0, 0, 0, 0, 0, user_string);
      user_id[1] = (UCHAR) strlen((SCHAR *) p);

      for (; *p; p++)
            if (*p >= 'A' && *p <= 'Z')
                  *p = *p - 'A' + 'a';

      *p++ = CNCT_host;
      p++;
      ISC_get_host(reinterpret_cast < SCHAR * >(p), 64);
      p[-1] = (UCHAR) strlen((SCHAR *) p);

      for (; *p; p++)
            if (*p >= 'A' && *p <= 'Z')
                  *p = *p - 'A' + 'a';

      if (uv_flag) {
            *p++ = CNCT_user_verification;
            *p++ = 0;
      }

      user_length = p - user_id;

/* Establish connection to server */

      cnct = &packet->p_cnct;
      packet->p_operation = op_connect;
      cnct->p_cnct_operation = op_attach;
      cnct->p_cnct_cversion = CONNECT_VERSION2;
      cnct->p_cnct_client = ARCHITECTURE;
      cnct->p_cnct_file.cstr_length = *file_length;
      cnct->p_cnct_file.cstr_address = (UCHAR *) file_name;

/* Note: prior to V3.1E a recievers could not in truth handle more
   then 5 protocol descriptions; however, this restriction does not 
   apply to Windows since it was created in 4.0 */

/* If we want user verification, we can't speak anything less than version 7 */

      cnct->p_cnct_user_id.cstr_length = user_length;
      cnct->p_cnct_user_id.cstr_address = user_id;

      static const p_cnct::p_cnct_repeat protocols_to_try1[] =
      {
            REMOTE_PROTOCOL(PROTOCOL_VERSION7, ptype_rpc, MAX_PTYPE, 1),
            REMOTE_PROTOCOL(PROTOCOL_VERSION8, ptype_rpc, MAX_PTYPE, 2),
            REMOTE_PROTOCOL(PROTOCOL_VERSION10, ptype_rpc, MAX_PTYPE, 3)
#ifdef SCROLLABLE_CURSORS
            ,
            REMOTE_PROTOCOL(PROTOCOL_SCROLLABLE_CURSORS, ptype_rpc, MAX_PTYPE, 4)
#endif
      };
      cnct->p_cnct_count = FB_NELEM(protocols_to_try1);

      for (size_t i = 0; i < cnct->p_cnct_count; i++) {
            cnct->p_cnct_versions[i] = protocols_to_try1[i];
      }

/* If we can't talk to a server, punt. Let somebody else generate an error. */

      PORT port = WNET_connect(node_name, packet, status_vector, FALSE);
      if (!port) {
            ALLR_release(rdb);
            return NULL;
      }

/* Get response packet from server. */

      rdb->rdb_port = port;
      port->port_context = rdb;
      port->receive(packet);

      if (packet->p_operation == op_reject && !uv_flag) {
            disconnect(port);
            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_length;
            cnct->p_cnct_file.cstr_address = (UCHAR *) file_name;

            /* try again with next set of known protocols */

            cnct->p_cnct_user_id.cstr_length = user_length;
            cnct->p_cnct_user_id.cstr_address = user_id;

            static const p_cnct::p_cnct_repeat protocols_to_try2[] =
            {
                  REMOTE_PROTOCOL(PROTOCOL_VERSION4, ptype_rpc, ptype_batch_send, 1),
                  REMOTE_PROTOCOL(PROTOCOL_VERSION6, ptype_rpc, ptype_batch_send, 2),
            };
            cnct->p_cnct_count = FB_NELEM(protocols_to_try2);

            for (size_t i = 0; i < cnct->p_cnct_count; i++) {
                  cnct->p_cnct_versions[i] = protocols_to_try2[i];
            }

            port = WNET_connect(node_name, packet, status_vector, FALSE);
            if (!port) {
                  ALLR_release(rdb);
                  return NULL;
            }

            /* Get response packet from server. */

            rdb->rdb_port = port;
            port->port_context = rdb;
            port->receive(packet);
      }

      if (packet->p_operation == op_reject && !uv_flag) {
            disconnect(port);
            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_length;
            cnct->p_cnct_file.cstr_address = (UCHAR *) file_name;

            /* try again with next set of known protocols */

            cnct->p_cnct_user_id.cstr_length = user_length;
            cnct->p_cnct_user_id.cstr_address = user_id;

            static const p_cnct::p_cnct_repeat protocols_to_try3[] =
            {
                  REMOTE_PROTOCOL(PROTOCOL_VERSION3, ptype_rpc, ptype_batch_send, 1)
            };
            cnct->p_cnct_count = FB_NELEM(protocols_to_try3);

            for (size_t i = 0; i < cnct->p_cnct_count; i++) {
                  cnct->p_cnct_versions[i] = protocols_to_try3[i];
            }

            port = WNET_connect(node_name, packet, status_vector, FALSE);
            if (!port) {
                  ALLR_release(rdb);
                  return NULL;
            }

            /* Get response packet from server. */

            rdb->rdb_port = port;
            port->port_context = rdb;
            port->receive(packet);
      }

      if (packet->p_operation != op_accept) {
            *status_vector++ = gds_arg_gds;
            *status_vector++ = gds_connect_reject;
            *status_vector++ = 0;
            disconnect(port);
            return NULL;
      }

      port->port_protocol = packet->p_acpt.p_acpt_version;

/* once we've decided on a protocol, concatenate the version 
   string to reflect it...  */

      sprintf(buffer, "%s/P%d", port->port_version->str_data,
                  port->port_protocol);
      ALLR_free(port->port_version);
      port->port_version = REMOTE_make_string(buffer);

      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;

      return port;
}


PORT WNET_connect(TEXT*       name,
                          PACKET*   packet,
                          ISC_STATUS*     status_vector,
                          USHORT    flag)
{
/**************************************
 *
 *    W 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.
 *
 **************************************/

      ISC_STATUS status;
      TEXT command_line[MAXPATHLEN + 32], *p;
      USHORT ret;

      PORT port = alloc_port(0);
      port->port_status_vector = status_vector;
      status_vector[0] = gds_arg_gds;
      status_vector[1] = 0;
      status_vector[2] = gds_arg_end;

      if (port->port_connection) {
            ALLR_free(port->port_connection);
      }
      port->port_connection = make_pipe_name(name, SERVER_PIPE_SUFFIX, 0);

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

      if (packet)
      {
            THREAD_EXIT;
            while (TRUE) {
                  port->port_handle = CreateFile(port->port_connection->str_data,
                                                               GENERIC_WRITE | GENERIC_READ,
                                                               0, NULL, OPEN_EXISTING, 0, NULL);
                  if (port->port_handle != INVALID_HANDLE_VALUE) {
                        break;
                  }
                  if ((status = GetLastError()) != ERROR_PIPE_BUSY) {
                        THREAD_ENTER;
                        wnet_error(port, "CreateFile", isc_net_connect_err, status);
                        disconnect(port);
                        return NULL;
                  }
                  WaitNamedPipe(port->port_connection->str_data, 3000L);
            }
            THREAD_ENTER;
            send_full(port, packet);
            return port;
      }

#ifndef REQUESTER
/* We're a server, so wait for a host to show up */

      LPSECURITY_ATTRIBUTES security_attr;
      security_attr = ISC_get_security_desc();
      THREAD_EXIT;
      command_line[0] = 0;

      while (TRUE)
      {
            port->port_handle =
                  CreateNamedPipe(port->port_connection->str_data,
                                          PIPE_ACCESS_DUPLEX,
                                          PIPE_WAIT | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
                                          PIPE_UNLIMITED_INSTANCES,
                                          MAX_DATA,
                                          MAX_DATA,
                                          0,
                                          security_attr);
            if (port->port_handle == INVALID_HANDLE_VALUE ||
                  GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
            {
                  // TMN: The check for GetLastError() is redundant.
                  // This code should NEVER be called if not running on NT,
                  // since Win9x does not support the server side of named pipes!
                  THREAD_ENTER;
                  wnet_error(port, "CreateNamedPipe", isc_net_connect_listen_err,
                                 ERRNO);
                  disconnect(port);
                  return NULL;
            }

            if (!ConnectNamedPipe(port->port_handle, 0) &&
                  GetLastError() != ERROR_PIPE_CONNECTED)
            {
                  THREAD_ENTER;
                  wnet_error(port, "ConnectNamedPipe", isc_net_connect_err, ERRNO);
                  disconnect(port);
                  return NULL;
            }

            if (flag & (SRVR_debug | SRVR_multi_client))
            {
                  THREAD_ENTER;
                  port->port_server_flags |= SRVR_server;
                  if (flag & SRVR_multi_client)
                  {
                        port->port_server_flags |= SRVR_multi_client;
                  }
                  gds__register_cleanup(reinterpret_cast <
                                                  void (*)(void *) >(exit_handler), port);
                  return port;
            }

            if (!command_line[0])
            {

#ifdef CMDLINE_VIA_SERVICE_MANAGER

                  SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
                  SC_HANDLE service = 0;
                  if (manager) {
                        service = OpenService(  manager,
                                                            REMOTE_SERVICE,
                                                            SERVICE_QUERY_CONFIG);
                  }

                  if (manager && service)
                  {
                        LPQUERY_SERVICE_CONFIG config;
                        SCHAR buffer[1024];
                        DWORD config_len;

                        config = (LPQUERY_SERVICE_CONFIG) buffer;
                        if (!QueryServiceConfig
                              (service, config, sizeof(buffer), &config_len)) {
                              THREAD_ENTER;
                              config = (LPQUERY_SERVICE_CONFIG) ALLR_alloc(config_len);
                              /* NOMEM: handled by ALLR_alloc, FREE: in this block */
                              QueryServiceConfig(service, config, config_len,
                                                         &config_len);
                        }
                        sprintf(command_line, "%s -s", config->lpBinaryPathName);
                        if ((SCHAR *) config != buffer) {
                              ALLR_free(config);
                              THREAD_EXIT;
                        }
                        CloseServiceHandle(service);
                  }
                  else
                  {
                        strcpy(command_line, GetCommandLine());
                  }
                  CloseServiceHandle(manager);
#else
                  strcpy(command_line, GetCommandLine());
#endif
                  p = command_line + strlen(command_line);
            }

            sprintf(p, " -s -w -h %"SLONGFORMAT, (SLONG) port->port_handle);
            STARTUPINFO           start_crud;
            PROCESS_INFORMATION   pi;
            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;
            ret = CreateProcess(NULL,
                                          command_line,
                                          NULL,
                                          NULL,
                                          TRUE,
                                          (flag & SRVR_high_priority ?
                                           HIGH_PRIORITY_CLASS | DETACHED_PROCESS :
                                           NORMAL_PRIORITY_CLASS | DETACHED_PROCESS),
                                          NULL, NULL, &start_crud, &pi);
            if (ret) {
                  CloseHandle(pi.hThread);
                  CloseHandle(pi.hProcess);
            }
            CloseHandle(port->port_handle);
      }
#endif /* REQUESTER */
}


PORT WNET_reconnect(HANDLE handle, TEXT * name, ISC_STATUS * status_vector)
{
/**************************************
 *
 *    W 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.
 *
 **************************************/
      PORT port;

      port = alloc_port(0);
      port->port_status_vector = status_vector;
      status_vector[0] = gds_arg_gds;
      status_vector[1] = 0;
      status_vector[2] = gds_arg_end;

      if (port->port_connection)
            ALLR_free(port->port_connection);
      port->port_connection = make_pipe_name(name, SERVER_PIPE_SUFFIX, 0);

      port->port_handle = handle;
      port->port_server_flags |= SRVR_server;

      return port;
}


PORT WNET_server(void *handle)
{
/**************************************
 *
 *    W 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.
 *
 **************************************/
      PORT port;

      port = alloc_port(0);
      port->port_server_flags |= SRVR_server;
      port->port_handle = (HANDLE) handle;

      return port;
}


static int accept_connection( PORT port, P_CNCT * cnct)
{
/**************************************
 *
 *    a c c e p t _ c o n n e c t i o n
 *
 **************************************
 *
 * Functional description
 *    Accept an incoming request for connection.  This is purely a lower
 *    level handshaking function, and does not constitute the server
 *    response for protocol selection.
 *
 **************************************/
      TEXT name[64], password[64], *id, *end, *p;
      STR string;
      int i, length, l;
      BOOLEAN revert_flag;
      TEXT uname[128];
      SLONG name_len;
#ifndef REQUESTER
      int user_verification;
#endif

/* Default account to "guest" (in theory all packets contain a name) */

      strcpy(name, "guest");
      password[0] = 0;

/* Pick up account and password, if given */

      id = (TEXT *) cnct->p_cnct_user_id.cstr_address;
      end = id + cnct->p_cnct_user_id.cstr_length;

#ifndef REQUESTER
      user_verification = 0;
#endif
      while (id < end)
            switch (*id++) {
            case CNCT_user:
                  length = l = *id++;
                  port->port_user_name = string =
                        (STR) ALLOCV(type_str, (int) length);
                  string->str_length = length;
                  if (length) {
                        p = (TEXT *) string->str_data;
                        do
                              *p++ = *id++;
                        while (--l);
                  }
                  strncpy(name, string->str_data, length);
                  name[length] = (TEXT) 0;
                  break;

            case CNCT_passwd:
                  p = password;
                  if ((length = *id++) != 0)
                        do
                              *p++ = *id++;
                        while (--length);
                  *p = 0;
                  break;

            case CNCT_user_verification:
#ifndef REQUESTER
                  user_verification = 1;
#endif
                  id++;
                  break;

            default:
                  id += *id + 1;
            }

#ifndef REQUESTER
/* See if user exists.  If not, reject connection */

      if (revert_flag = ImpersonateNamedPipeClient(port->port_handle)) {
            port->port_flags |= PORT_impersonate;
            name_len = 128;

            if (GetUserName(uname, reinterpret_cast < DWORD * >(&name_len))) {
                  for (i = 0; i < name_len; i++)
                        uname[i] = LOWWER7(uname[i]);

                  uname[name_len] = 0;

                  if ((!user_verification) && strcmp(name, uname)) {
                        port->port_flags &= ~PORT_impersonate;
                        RevertToSelf();
                        return FALSE;
                  }
            }
      }

#endif /* REQUESTER */
      return TRUE;
}


static PORT alloc_port( PORT parent)
{
/**************************************
 *
 *    a l l o c _ p o r t
 *
 **************************************
 *
 * Functional description
 *    Allocate a port block, link it in to parent (if there is a parent),
 *    and initialize input and output XDR streams.
 *
 **************************************/
      PORT port;
      TEXT buffer[64];

      port = (PORT) ALLOCV(type_port, BUFFER_SIZE * 2);
      port->port_type = port_pipe;
      port->port_state = state_pending;

      ISC_get_host(buffer, sizeof(buffer));
      port->port_host = REMOTE_make_string(buffer);
      port->port_connection = REMOTE_make_string(buffer);
      sprintf(buffer, "WNet (%s)", port->port_host->str_data);
      port->port_version = REMOTE_make_string(buffer);

      if (parent) {
            port->port_parent = parent;
            port->port_next = parent->port_clients;
            parent->port_clients = parent->port_next = port;
            port->port_handle = parent->port_handle;
            port->port_server = parent->port_server;
            port->port_server_flags = parent->port_server_flags;
            if (port->port_connection)
                  ALLR_free(port->port_connection);
            port->port_connection =
                  REMOTE_make_string(parent->port_connection->str_data);
      }

      port->port_accept = accept_connection;
      port->port_disconnect = disconnect;
      port->port_receive_packet = receive;
      port->port_send_packet = send_full;
      port->port_send_partial = send_partial;
      port->port_connect =
            reinterpret_cast < PORT(*)(PORT, PACKET *, void (*)()) >(aux_connect);
      port->port_request = aux_request;
      port->port_buff_size = BUFFER_SIZE;

      xdrwnet_create(&port->port_send, port,
                           &port->port_buffer[BUFFER_SIZE], BUFFER_SIZE, XDR_ENCODE);

      xdrwnet_create(&port->port_receive, port, port->port_buffer, 0,
                           XDR_DECODE);

      return port;
}


static PORT aux_connect( PORT port, PACKET * packet, XDR_INT(*ast) (void))
{
/**************************************
 *
 *    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).
 *
 **************************************/
      PORT new_port;
      ISC_STATUS status;
      TEXT *p, str_pid[32];
      P_RESP *response;

#ifndef REQUESTER
/* If this is a server, we're got an auxiliary connection.  Accept it */

      if (port->port_server_flags) {
            if (!ConnectNamedPipe(port->port_handle, 0) &&
                  GetLastError() != ERROR_PIPE_CONNECTED) {
                  wnet_error(port, "ConnectNamedPipe", isc_net_event_connect_err,
                                 ERRNO);
                  disconnect(port);
                  return NULL;
            }

            port->port_flags |= PORT_async;
            return port;
      }
#endif /* REQUESTER */

/* The server will be sending its process id in the packet to
 * create a unique pipe name.
 */

      response = &packet->p_resp;

      if (response->p_resp_data.cstr_length) {
            wnet_copy(reinterpret_cast <
                          char *>(response->p_resp_data.cstr_address), str_pid,
                          response->p_resp_data.cstr_length);
            str_pid[response->p_resp_data.cstr_length] = 0;
            p = str_pid;
      }
      else {
            p = 0;
      }

      port->port_async = new_port = alloc_port(port->port_parent);
      new_port->port_flags |= PORT_async;
      new_port->port_connection =
            make_pipe_name(port->port_connection->str_data, EVENT_PIPE_SUFFIX, p);

      THREAD_EXIT;
      while (TRUE) {
            new_port->port_handle =
                  CreateFile(new_port->port_connection->str_data, GENERIC_READ, 0,
                                 NULL, OPEN_EXISTING, 0, NULL);
            if (new_port->port_handle != INVALID_HANDLE_VALUE)
                  break;
            if ((status = GetLastError()) != ERROR_PIPE_BUSY) {
                  THREAD_ENTER;
                  return (PORT) wnet_error(new_port, "CreateFile",
                                                       isc_net_event_connect_err, status);
            }
            WaitNamedPipe(new_port->port_connection->str_data, 3000L);
      }

      THREAD_ENTER;

      new_port->port_flags = port->port_flags & PORT_no_oob;

      return new_port;
}


static PORT aux_request( PORT port, PACKET * packet)
{
/**************************************
 *
 *    a u x _ r e q u e s t
 *
 **************************************
 *
 * Functional description
 *    A remote interface has requested the server prepare an auxiliary 
 *    connection; the server calls aux_request to set up the connection.
 *    Send the servers process id on the packet.  If at a later time
 *    a multi client server is used, there may be a need to
 *    generate a unique id based on connection.
 *
 **************************************/
      PORT new_port;
      P_RESP *response;
      DWORD server_pid;
      TEXT str_pid[32];
#ifndef REQUESTER
      LPSECURITY_ATTRIBUTES security_attr;
#endif /* REQUESTER */

#ifndef REQUESTER
      server_pid = GetCurrentProcessId();
      port->port_async = new_port = alloc_port(port->port_parent);
      new_port->port_server_flags = port->port_server_flags;
      new_port->port_flags = port->port_flags & PORT_no_oob;

      wnet_make_file_name(str_pid, server_pid);
      new_port->port_connection =
            make_pipe_name(port->port_connection->str_data, EVENT_PIPE_SUFFIX, str_pid);

      security_attr = ISC_get_security_desc();
      THREAD_EXIT;
      new_port->port_handle =
            CreateNamedPipe(new_port->port_connection->str_data,
                                    PIPE_ACCESS_DUPLEX,
                                    PIPE_WAIT | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
                                    PIPE_UNLIMITED_INSTANCES,
                                    MAX_DATA,
                                    MAX_DATA,
                                    0,
                                    security_attr);
      THREAD_ENTER;
      if (new_port->port_handle == INVALID_HANDLE_VALUE) {
            wnet_error(new_port, "CreateNamedPipe", isc_net_event_listen_err,
                           ERRNO);
            disconnect(new_port);
            return NULL;
      }

      response = &packet->p_resp;
      response->p_resp_data.cstr_length = strlen(str_pid);
      wnet_copy(str_pid,
                    reinterpret_cast < char *>(response->p_resp_data.cstr_address),
                    response->p_resp_data.cstr_length);

#endif /* REQUESTER */

      return new_port;
}


static void disconnect(PORT port)
{
/**************************************
 *
 *    d i s c o n n e c t
 *
 **************************************
 *
 * Functional description
 *    Break a remote connection.
 *
 **************************************/

/* If this is a sub-port, unlink it from it's parent */

      PORT parent = port->port_parent;
      if (parent)
      {
            if (port->port_async)
            {
                  disconnect(port->port_async);
                  port->port_async = NULL;
            }
            for (PORT* ptr = &parent->port_clients; *ptr; ptr = &(*ptr)->port_next)
            {
                  if (*ptr == port)
                  {
                        *ptr = port->port_next;
                        if (ptr == &parent->port_clients) {
                              parent->port_next = *ptr;
                        }
                        break;
                  }
            }
      }
      else if (port->port_async)
      {
/* If we're MULTI_THREAD then we cannot free the port because another
 * thread might be using it.  If we're SUPERSERVER we must free the
 * port to avoid a memory leak.  What we really need to know is if we
 * have multi-threaded events, but this is transport specific.
 */
#if     (defined (MULTI_THREAD) && !defined (SUPERSERVER))
            port->port_async->port_flags |= PORT_disconnect;
#else
            disconnect(port->port_async);
            port->port_async = NULL;
#endif
      }

#ifndef REQUESTER
      if (port->port_server_flags & SRVR_server)
      {
            FlushFileBuffers(port->port_handle);
            DisconnectNamedPipe(port->port_handle);
            if (port->port_flags & PORT_impersonate)
            {
                  RevertToSelf();
                  port->port_flags &= ~PORT_impersonate;
            }
      }
#endif /* REQUESTER */
      if (port->port_handle) {
            CloseHandle(port->port_handle);
            port->port_handle = 0;
      }
      gds__unregister_cleanup(reinterpret_cast<void (*)(void*)>(exit_handler),
                              port);
      cleanup_port(port);
}


static void cleanup_port( PORT port)
{
/**************************************
 *
 *      c l e a n u p _ p o r t
 *
 **************************************
 *
 * Functional description
 *      Walk through the port structure freeing
 *      allocated memory and then free the port.
 *
 **************************************/

      if (port->port_version)
            ALLR_free((UCHAR *) port->port_version);

      if (port->port_connection)
            ALLR_free((UCHAR *) port->port_connection);

      if (port->port_user_name)
            ALLR_free((UCHAR *) port->port_user_name);

      if (port->port_host)
            ALLR_free((UCHAR *) port->port_host);

      if (port->port_object_vector)
            ALLR_free((UCHAR *) port->port_object_vector);

#ifdef DEBUG_XDR_MEMORY
      if (port->port_packet_vector)
            ALLR_free((UCHAR *) port->port_packet_vector);
#endif

      ALLR_release((UCHAR *) port);
      return;
}


static void exit_handler( PORT main_port)
{
/**************************************
 *
 *    e x i t _ h a n d l e r
 *
 **************************************
 *
 * Functional description
 *    Shutdown all active connections
 *    to allow restart.
 *
 **************************************/
      PORT port;

      for (port = main_port; port; port = port->port_next)
            CloseHandle(port->port_handle);
}


static STR make_pipe_name(
                                      TEXT * connect_name,
                                      TEXT * suffix_name,
                                      TEXT * str_pid)
{
/**************************************
 *
 *    m a k e _ p i p e _ n a m e
 *
 **************************************
 *
 * Functional description
 *    Construct a name for the pipe connection.
 *    Figure out whether we need a remote node name,
 *    and construct the pipe name accordingly.
 *    If a server pid != 0, append it to pipe name  as <>/<pid>
 *
 **************************************/
      TEXT buffer[128], *p, *q, *protocol;
      TEXT pid[32];

      p = connect_name;
      q = buffer;

      if (!p || *p++ != '\\' || *p++ != '\\')
            p = ".";

      *q++ = '\\';
      *q++ = '\\';
      while (*p && *p != '\\' && *p != '@')
            *q++ = *p++;

      if (!*p)
            protocol = const_cast<TEXT*>(Config::getRemoteServiceName());
      else if (*p == '@')
            protocol = p + 1;
      else {
            while (*p)
                  if (*p++ == '\\')
                        protocol = p;
      }

      *q++ = '\\';
      strcpy(q, PIPE_PREFIX);
      q += strlen(PIPE_PREFIX);
      *q++ = '\\';
      const char *pipe_name = Config::getRemotePipeName();
      strcpy(q, pipe_name);
      q += strlen(pipe_name);
      *q++ = '\\';
      strcpy(q, suffix_name);
      q += strlen(suffix_name);
      *q++ = '\\';
      strcpy(q, protocol);

      if (str_pid) {
            sprintf(pid, "\\%s", str_pid);
            strcat(buffer, pid);
      }

      return REMOTE_make_string(buffer);
}


static PORT receive( PORT main_port, PACKET * packet)
{
/**************************************
 *
 *    r e c e i v e
 *
 **************************************
 *
 * Functional description
 *    Receive a message from a port or clients of a port.  If the process
 *    is a server and a connection request comes in, generate a new port
 *    block for the client.
 *
 **************************************/

      if (!xdr_protocol(&main_port->port_receive, packet))
            packet->p_operation = op_exit;

      return main_port;
}


static int send_full( PORT port, PACKET * packet)
{
/**************************************
 *
 *    s e n d _ f u l l
 *
 **************************************
 *
 * Functional description
 *    Send a packet across a port to another process.
 *
 **************************************/

      if (!xdr_protocol(&port->port_send, packet))
            return FALSE;

      return xdrwnet_endofrecord(&port->port_send, TRUE);
}


static int send_partial( PORT port, PACKET * packet)
{
/**************************************
 *
 *    s e n d _ p a r t i a l
 *
 **************************************
 *
 * Functional description
 *    Send a packet across a port to another process.
 *
 **************************************/

      return xdr_protocol(&port->port_send, packet);
}


static int xdrwnet_create(
                                      XDR * xdrs,
                                      PORT port,
                                      UCHAR * buffer, USHORT length, enum xdr_op x_op)
{
/**************************************
 *
 *    x d r w 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 = (SCHAR *) buffer;
      xdrs->x_handy = length;
      xdrs->x_ops = &wnet_ops;
      xdrs->x_op = x_op;

      return TRUE;
}


static bool_t xdrwnet_endofrecord( XDR * xdrs, bool_t flushnow)
{
/**************************************
 *
 *    x d r w n e t _ e n d o f r e c o r d
 *
 **************************************
 *
 * Functional description
 *    Write out the rest of a record.
 *
 **************************************/

      return wnet_write(xdrs, flushnow);
}


static int wnet_destroy( XDR * xdrs)
{
/**************************************
 *
 *    w n e t _ d e s t r o y
 *
 **************************************
 *
 * Functional description
 *    Destroy a stream.  A no-op.
 *
 **************************************/

      return 0;
}


static int wnet_error(
                                PORT port,
                                TEXT * function, ISC_STATUS operation, int status)
{
/**************************************
 *
 *    w n e t _ e r r o r
 *
 **************************************
 *
 * Functional description
 *    An I/O error has occurred.  If a status vector is present,
 *    generate an error return.  In any case, return NULL, which
 *    is used to indicate and error.
 *
 **************************************/
      TEXT msg[64];
      TEXT node_name[MAXPATHLEN];
      TEXT *p;

      strcpy(node_name, ((SCHAR *) port->port_connection->str_data) + 2);
      p = strchr(node_name, '\\');
      if (p != NULL)
            *p = '\0';

      if (status) {
            wnet_gen_error(port, isc_network_error,
                                 gds_arg_string, (ISC_STATUS) node_name,
                                 isc_arg_gds, operation,
                                 SYS_ERR, status,
                                 0);
            if (status != ERROR_CALL_NOT_IMPLEMENTED) {
                  sprintf(msg, "WNET/wnet_error: %s errno = %d", function, status);
                  gds__log(msg, 0, 0, 0, 0);
            }
      }
      else {
            wnet_gen_error(port, isc_network_error,
                                 gds_arg_string, (ISC_STATUS) node_name,
                                 isc_arg_gds, operation, 0);
      }

      return 0;
}


static void wnet_gen_error( PORT port, ISC_STATUS status, ...)
{
/**************************************
 *
 *    w 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.
 *
 **************************************/
      ISC_STATUS *status_vector;

      port->port_flags |= PORT_broken;
      port->port_state = state_broken;

      status_vector = NULL;
      if (port->port_context != NULL)
            status_vector = port->port_context->rdb_status_vector;
      if (status_vector == NULL)
            status_vector = port->port_status_vector;
      if (status_vector != NULL) {
            STUFF_STATUS(status_vector, status);
            REMOTE_save_status_strings(status_vector);
      }
}


static bool_t wnet_getbytes( XDR * xdrs, SCHAR * buff, u_int count)
{
/**************************************
 *
 *    w 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.
 *
 **************************************/
      SLONG bytecount = count;

/* Use memcpy to optimize bulk transfers. */

      while (bytecount > (SLONG) sizeof(GDS_QUAD)) {
            if (xdrs->x_handy >= bytecount) {
                  memcpy(buff, xdrs->x_private, bytecount);
                  xdrs->x_private += bytecount;
                  xdrs->x_handy -= bytecount;
                  return TRUE;
            }
            else {
                  if (xdrs->x_handy > 0) {
                        memcpy(buff, xdrs->x_private, xdrs->x_handy);
                        xdrs->x_private += xdrs->x_handy;
                        buff += xdrs->x_handy;
                        bytecount -= xdrs->x_handy;
                        xdrs->x_handy = 0;
                  }
                  if (!wnet_read(xdrs))
                        return FALSE;
            }
      }

/* Scalar values and bulk transfer remainder fall thru
   to be moved byte-by-byte to avoid memcpy setup costs. */

      if (!bytecount)
            return TRUE;

      if (xdrs->x_handy >= bytecount) {
            xdrs->x_handy -= bytecount;
            do
                  *buff++ = *xdrs->x_private++;
            while (--bytecount);
            return TRUE;
      }

      while (--bytecount >= 0) {
            if (!xdrs->x_handy && !wnet_read(xdrs))
                  return FALSE;
            *buff++ = *xdrs->x_private++;
            --xdrs->x_handy;
      }

      return TRUE;
}


static bool_t wnet_getlong( XDR * xdrs, SLONG * lp)
{
/**************************************
 *
 *    w n e t _ g e t l o n g
 *
 **************************************
 *
 * Functional description
 *    Fetch a longword into a memory stream if it fits.
 *
 **************************************/
      SLONG l;

      if (!(*xdrs->x_ops->x_getbytes) (xdrs, reinterpret_cast < char *>(&l), 4))
            return FALSE;

      *lp = ntohl(l);

      return TRUE;
}


static u_int wnet_getpostn( XDR * xdrs)
{
/**************************************
 *
 *    w n e t _ g e t p o s t n
 *
 **************************************
 *
 * Functional description
 *    Get the current position (which is also current length) from stream.
 *
 **************************************/

      return (u_int) (xdrs->x_private - xdrs->x_base);
}


static caddr_t wnet_inline( XDR * xdrs, u_int bytecount)
{
/**************************************
 *
 *    w 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 bool_t wnet_putbytes( XDR * xdrs, SCHAR * buff, u_int count)
{
/**************************************
 *
 *    w n e t _ p u t b y t e s
 *
 **************************************
 *
 * Functional description
 *    Put a bunch of bytes to a memory stream if it fits.
 *
 **************************************/
      SLONG bytecount = count;

/* Use memcpy to optimize bulk transfers. */

      while (bytecount > (SLONG) sizeof(GDS_QUAD)) {
            if (xdrs->x_handy >= bytecount) {
                  memcpy(xdrs->x_private, buff, bytecount);
                  xdrs->x_private += bytecount;
                  xdrs->x_handy -= bytecount;
                  return TRUE;
            }
            else {
                  if (xdrs->x_handy > 0) {
                        memcpy(xdrs->x_private, buff, xdrs->x_handy);
                        xdrs->x_private += xdrs->x_handy;
                        buff += xdrs->x_handy;
                        bytecount -= xdrs->x_handy;
                        xdrs->x_handy = 0;
                  }
                  if (!wnet_write(xdrs, 0))
                        return FALSE;
            }
      }

/* Scalar values and bulk transfer remainder fall thru
   to be moved byte-by-byte to avoid memcpy setup costs. */

      if (!bytecount)
            return TRUE;

      if (xdrs->x_handy >= bytecount) {
            xdrs->x_handy -= bytecount;
            do
                  *xdrs->x_private++ = *buff++;
            while (--bytecount);
            return TRUE;
      }

      while (--bytecount >= 0) {
            if (xdrs->x_handy <= 0 && !wnet_write(xdrs, 0))
                  return FALSE;
            --xdrs->x_handy;
            *xdrs->x_private++ = *buff++;
      }

      return TRUE;
}


static bool_t wnet_putlong( XDR * xdrs, SLONG * lp)
{
/**************************************
 *
 *    w n e t _ p u t l o n g
 *
 **************************************
 *
 * Functional description
 *    Fetch a longword into a memory stream if it fits.
 *
 **************************************/
      SLONG l;

      l = htonl(*lp);
      return (*xdrs->x_ops->x_putbytes) (xdrs,
                                                         reinterpret_cast < char *>(AOF32L(l)),
                                                         4);
}


static bool_t wnet_read( XDR * xdrs)
{
/**************************************
 *
 *    w 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.
 *
 **************************************/
      PORT port;
      SSHORT length;
      SCHAR *p, *end;

      port = (PORT) xdrs->x_public;
      p = xdrs->x_base;
      end = p + BUFFER_SIZE;

/* If buffer is not completely empty, slide down what what's left */

      if (xdrs->x_handy > 0) {
            memmove(p, xdrs->x_private, xdrs->x_handy);
            p += xdrs->x_handy;
      }

/* If an ACK is pending, do an ACK.  The alternative is deadlock. */

/*
if (port->port_flags & PORT_pend_ack)
    if (!packet_send (port, 0, 0))
      return FALSE;
*/

      while (TRUE) {
            length = end - p;
            if (!packet_receive
                  (port, reinterpret_cast < UCHAR * >(p), length, &length)) {
                  return FALSE;
      /***
      if (!packet_send (port, 0, 0))
          return FALSE;
      continue;
      ***/
            }
            if (length >= 0) {
                  p += length;
                  break;
            }
            p -= length;
            if (!packet_send(port, 0, 0))
                  return FALSE;
      }

      port->port_flags |= PORT_pend_ack;
      xdrs->x_handy = (int) ((SCHAR *) p - xdrs->x_base);
      xdrs->x_private = xdrs->x_base;

      return TRUE;
}


static bool_t wnet_setpostn( XDR * xdrs, u_int bytecount)
{
/**************************************
 *
 *    w 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 bool_t wnet_write( XDR * xdrs, bool_t end_flag)
{
/**************************************
 *
 *    w n e t _ w r i t e
 *
 **************************************
 *
 * Functional description
 *    Write a buffer fulll of data.  If the end_flag isn't set, indicate
 *    that the buffer is a fragment, and reset the XDR for another buffer
 *    load.
 *
 **************************************/
      SCHAR *p;
      PORT port;
      SSHORT l, length;

/* Encode the data portion of the packet */

      port = (PORT) xdrs->x_public;
      p = xdrs->x_base;
      length = xdrs->x_private - p;

/* Send data in manageable hunks.  If a packet is partial, indicate
   that with a negative length.  A positive length marks the end. */

      p = xdrs->x_base;

      while (length) {
            port->port_misc1 = (port->port_misc1 + 1) % MAX_SEQUENCE;
            l = MIN(length, MAX_DATA);
            length -= l;
            if (!packet_send(port, p, (SSHORT) (length ? -l : l)))
                  return FALSE;
            p += l;
      }

      xdrs->x_private = xdrs->x_base;
      xdrs->x_handy = BUFFER_SIZE;

      return TRUE;
}


#ifdef DEBUG
static void packet_print( TEXT * string, UCHAR * packet, int length)
{
/**************************************
 *
 *    p a c k e t _ p r i n t
 *
 **************************************
 *
 * Functional description
 *    Print a summary of packet.
 *
 **************************************/
      int sum = 0;
      int l = length;

      if (l) {
            do {
                  sum += *packet++;
            } while (--l);
      }

      ib_printf("%s\t: length = %d, checksum = %d\n", string, length, sum);
}
#endif


static int packet_receive(
                                      PORT port,
                                      UCHAR * buffer,
                                      SSHORT buffer_length, SSHORT * length)
{
/**************************************
 *
 *    p a c k e t _ r e c e i v e
 *
 **************************************
 *
 * Functional description
 *    Receive a packet and pass on it's goodness.  If it's good,
 *    return TRUE and the reported length of the packet, and update
 *    the receive sequence number.  If it's bad, return FALSE.  If it's
 *    a duplicate message, just ignore it.
 *
 **************************************/
      DWORD n = 0;
      USHORT status;

      THREAD_EXIT;
      status = ReadFile(port->port_handle, buffer, buffer_length, &n, NULL);
      THREAD_ENTER;
      if (!status && GetLastError() != ERROR_BROKEN_PIPE)
            return wnet_error(port, "ReadFile", isc_net_read_err, ERRNO);
      if (!n)
            return wnet_error(port, "ReadFile end-of-file", isc_net_read_err,
                                      ERRNO);

#ifdef DEBUG
      if (WNET_trace)
            packet_print("receive", buffer, n);
#endif

      *length = (SSHORT) n;

      return TRUE;
}


static int packet_send( PORT port, SCHAR * buffer, SSHORT buffer_length)
{
/**************************************
 *
 *    p a c k e t _ s e n d
 *
 **************************************
 *
 * Functional description
 *    Send some data on it's way.  
 *
 **************************************/
      SCHAR *data;
      DWORD n, length;
      USHORT status;

      data = buffer;
      length = buffer_length;

      THREAD_EXIT;
      status = WriteFile(port->port_handle, data, length, &n, NULL);
      THREAD_ENTER;
      if (!status)
            return wnet_error(port, "WriteFile", isc_net_write_err, ERRNO);
      if (n != length)
            return wnet_error(port, "WriteFile truncated", isc_net_write_err,
                                      ERRNO);

#ifdef DEBUG
      if (WNET_trace)
            packet_print("send", (UCHAR*)buffer, buffer_length);
#endif

      port->port_flags &= ~PORT_pend_ack;

      return TRUE;
}


static void wnet_copy( SCHAR * from, SCHAR * to, int length)
{
/**************************************
 *
 *      w n e t _ c o p y
 *
 **************************************
 *
 * Functional description
 *      Copy a number of bytes;
 *
 **************************************/

      if (length)
            do
                  *to++ = *from++;
            while ((--length) != 0);
}


static void wnet_make_file_name( TEXT * name, DWORD number)
{
/**************************************
 *
 *      w n e t _ m a k e _ f i l e _ n a m e
 *
 **************************************
 *
 * Functional description
 *      Create a file name out of a number making sure
 *    the Windows <8>.<3> limitations are handled.
 *
 **************************************/
      TEXT *p, *q, temp[32];
      USHORT len, length;

      sprintf(temp, "%lu", number);

      if ((length = strlen(temp)) < 8) {
            strcpy(name, temp);
            return;
      }

      p = name;
      q = temp;

      while (length) {
            len = (length > 8) ? 8 : length;
            length -= len;
            do
                  *p++ = *q++;
            while ((--len) != 0);

            if (length)
                  *p++ = '\\';
      }
      *p++ = 0;
}

Generated by  Doxygen 1.6.0   Back to index