8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 03:23:03 +01:00
firebird-mirror/src/remote/spxwin.cpp

2117 lines
52 KiB
C++

/*
* PROGRAM: JRD Remote Interface/Server
* MODULE: spxwin.c
* DESCRIPTION: SPX for PC Client 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): ______________________________________.
*/
#include "firebird.h"
#include "../jrd/time.h"
#include "../jrd/common.h"
#include "../jrd/ib_stdio.h"
#include <errno.h>
#include <string.h>
#include <direct.h>
#include <memory.h>
#include <stdarg.h>
#include <dos.h>
#include "../remote/remote.h"
#include "../jrd/gds.h"
#include "../jrd/iberr.h"
#include "../jrd/thd.h"
#include "../remote/ntoh_proto.h"
#include "../remote/proto_proto.h"
#include "../remote/remot_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/isc_i_proto.h"
#include "../jrd/sch_proto.h"
#include "../remote/xdr_proto.h"
#include "../jrd/doserr.h" /* include after remote.h */
/*
* Need to undef FAILURE from common.h (#define FAILURE 1) and MIN from
* common.h. (#define MIN(a,b) (((a) < (b)) ? (a) : (b)). They get
* redefined by the netware include files, included with spxwin.h, as
* #define FAILURE -1 and #define MIN(a,b) ((a) < (b) ? (a) : (b)).
*/
#ifdef FAILURE
#undef FAILURE
#endif
#ifdef MIN(a,b)
#undef MIN(a,b)
#endif
#include "../remote/spxwin.h"
#define NO_ITIMER
#define SYS_ERR gds_arg_dos
#define ERRNO errno
#define MAX_PTYPE ptype_out_of_band
/*
** The windows network libraries have to be dynamically loaded because the
** libraries may not be installed if the user does not have SPX (e.g. if
** they are using TCP). This section defines the SPX functions to
** reference function pointers which will be set using the windows
** GetProcAddress function in initSPX.
**
** IMPORTANT: If you add a call to a new SPX function, you must add
** a #define for it, define a function pointer for it, and add a
** call to GetProcAddress for it in initSPX so the function pointer
** gets initialized correctly.
*/
#define IPXCancelEvent IPXCancelEventPtr
#define IPXCloseSocket IPXCloseSocketPtr
#define IPXGetInternetworkAddress IPXGetInternetworkAddressPtr
#define IPXOpenSocket IPXOpenSocketPtr
#define IPXRelinquishControl IPXRelinquishControlPtr
#define IPXSPXDeinit IPXSPXDeinitPtr
#define SPXEstablishConnection SPXEstablishConnectionPtr
#define SPXInitialize SPXInitializePtr
#define SPXListenForSequencedPacket SPXListenForSequencedPacketPtr
#define SPXSendSequencedPacket SPXSendSequencedPacketPtr
#define SPXTerminateConnection SPXTerminateConnectionPtr
#define NWCallsInit NWCallsInitPtr
#define NWGetDefaultConnectionID NWGetDefaultConnectionIDPtr
#define NWReadPropertyValue NWReadPropertyValuePtr
#define NWWordSwap NWWordSwapPtr
/*
** Declare function pointers, using function prototypes for type checking
*/
int (FAR PASCAL * IPXCancelEventPtr) (DWORD IPXTaskID,
ECB FAR * eventControlBlock);
void (FAR PASCAL * IPXCloseSocketPtr) (DWORD IPXTaskID, WORD socket);
void (FAR PASCAL * IPXGetInternetworkAddressPtr) (DWORD IPXTaskID,
BYTE FAR * internetAddress);
int (FAR PASCAL * IPXOpenSocketPtr) (DWORD IPXTaskID, WORD FAR * socket,
BYTE socketType);
void (FAR PASCAL * IPXRelinquishControlPtr) (void);
void (FAR PASCAL * IPXSPXDeinitPtr) (DWORD IPXTaskId);
int (FAR PASCAL * SPXEstablishConnectionPtr) (DWORD IPXTaskID,
BYTE retryCount,
BYTE watchDog,
WORD FAR * SPXConnID,
ECB FAR * eventControlBlock);
int (FAR PASCAL * SPXInitializePtr) (DWORD FAR * IPXTaskID,
WORD maxECBs, WORD maxPacketSize,
BYTE FAR * majorRevisionNumber,
BYTE FAR * minorRevisionNumber,
WORD FAR * maxConnections,
WORD FAR * availableConnections);
void (FAR PASCAL * SPXListenForSequencedPacketPtr) (DWORD IPXTaskID,
ECB FAR *
eventControlBlock);
void (FAR PASCAL * SPXSendSequencedPacketPtr) (DWORD IPXTaskID,
WORD SPXConnID,
ECB FAR * eventControlBlock);
void (FAR PASCAL * SPXTerminateConnectionPtr)
(DWORD IPXTaskID, WORD SPXConnID, ECB FAR * eventControlBlock);
NWCCODE(NWAPI * NWCallsInitPtr) (void NWPTR in, void NWPTR out);
NWCCODE(NWAPI * NWGetDefaultConnectionIDPtr) (NWCONN_HANDLE NWPTR conn);
NWCCODE(NWAPI * NWReadPropertyValuePtr) (NWCONN_HANDLE conn,
char NWPTR objectName,
WORD objectType,
char NWPTR propertyName,
BYTE segmentNumber,
BYTE NWPTR segmentData,
BYTE NWPTR moreSegments,
BYTE NWPTR flags);
WORD(NWAPI * NWWordSwapPtr) (WORD swapWord);
extern STR REMOTE_make_string();
void process_async_data(void);
static PORT aux_connect(PORT port, PACKET * packet, XDR_INT(*ast) ());
static void terminate_port(spx_port_handle * handle);
static accept_connection(PORT port, P_CNCT * cnct);
static PORT alloc_port(PORT parent);
static void cleanup_port(PORT port);
static void disconnect(PORT port);
static void spxnet_copy(SCHAR * from, SCHAR * to, int length);
static void spxnet_zero(SCHAR * address, int length);
static PORT receive(PORT main_port, PACKET * packet);
static send_full(PORT port, PACKET * packet);
static send_partial(PORT port, PACKET * packet);
static xdrspxnet_create(XDR * xdrs,
PORT port,
UCHAR * buffer, USHORT length, enum xdr_op x_op);
static bool_t xdrspxnet_endofrecord(XDR * xdrs, bool_t flushnow);
static void spxnet_destroy(void);
static int spxnet_error(PORT port,
TEXT * operation, STATUS, int status, ECB * ecb);
static void spxnet_gen_error(PORT port, STATUS status, ...);
static bool_t spxnet_getbytes(XDR * xdrs, SCHAR * buff, u_int count);
static bool_t spxnet_getlong(XDR * xdrs, SLONG * lp);
static u_int spxnet_getpostn(XDR * xdrs);
static caddr_t spxnet_inline(XDR * xdrs, u_int bytecount);
static bool_t spxnet_putbytes(XDR * xdrs, SCHAR * buff, u_int count);
static bool_t spxnet_putlong(XDR * xdrs, SLONG * lp);
static bool_t spxnet_read(XDR * xdrs);
static bool_t spxnet_setpostn(XDR * xdrs, u_int bytecount);
static bool_t spxnet_write(XDR * xdrs, bool_t end_flag);
static void exit_handler(PORT main_port);
static packet_receive(PORT port,
UCHAR * buffer, SSHORT buffer_length, SSHORT * length);
static packet_receive_async(PORT port,
UCHAR * buffer,
SSHORT buffer_length, SSHORT * length);
static bool_t packet_send(PORT port, SCHAR * buffer, SSHORT buffer_length);
static void build_ecb_pool(spx_port_handle * handle, void *esr);
static void free_ecb_pool(spx_port_handle * handle);
static ECB *get_next_fragment(spx_port_handle * handle);
static void setup_tx_ecb(ECB FAR * ecb,
SPXHeader FAR * txhdr,
spx_port_handle * handle, SCHAR FAR * data);
static bool_t wait_for_free_tx_ecbs(PORT port);
static int initSPX(PORT);
static xdr_t::xdr_ops spxnet_ops = {
spxnet_getlong,
spxnet_putlong,
spxnet_getbytes,
spxnet_putbytes,
spxnet_getpostn,
spxnet_setpostn,
spxnet_inline,
spxnet_destroy
};
IPXAddress my_address;
static int SPX_initialized = FALSE;
/* This can't be a static since it's used by spxesr.c */
spx_port_handle *head_of_spx_port_list = 0;
/*
** The net libs are only inited once, so save the handles and task id in
** a global for everyone to share.
*/
static HINSTANCE hSpxIpxLib, hNwCallsLib;
static DWORD tskid;
PORT SPXNET_analyze(
TEXT * file_name,
USHORT * file_length,
STATUS * status_vector,
TEXT * node_name, TEXT * user_string, USHORT uv_flag)
{
/**************************************
*
* S P X 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.
*
**************************************/
RDB rdb;
PORT port;
PACKET *packet;
P_CNCT *cnct;
int eff_gid, eff_uid;
SSHORT user_length;
SCHAR outsegs;
SCHAR outflag;
UCHAR *p, user_id[200];
TEXT buffer[64];
struct p_cnct_repeat *protocol;
WORD local_socket;
WORD dest_socket = INTERBASE_SOCKET;
SCHAR dest_address[128];
int ret;
HGLOBAL handle_handle;
struct spx_port_handle FAR *handle;
ECB connecb;
SPXHeader connhdr;
NWCONN_HANDLE connid;
*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(p, &eff_uid, &eff_gid, 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(p, 64);
p[-1] = (UCHAR) strlen((SCHAR *) p);
for (; *p; p++)
if (*p >= 'A' && *p <= 'Z')
*p = *p - 'A' + 'a';
if ((eff_uid == -1) || uv_flag) {
*p++ = CNCT_user_verification;
*p++ = 0;
}
user_length = (SSHORT) (p - user_id);
/* Establish connection to server */
/* If we want user verification, we can't speak anything less than version 7 */
/* unlike other protocols, SPX can't talk to server versions which had the
bug in which only 5 protocols were supported, so go ahead and try more */
cnct = &packet->p_cnct;
#ifdef SCROLLABLE_CURSORS
cnct->p_cnct_count = 6;
#else
cnct->p_cnct_count = 4;
#endif
cnct->p_cnct_user_id.cstr_length = user_length;
cnct->p_cnct_user_id.cstr_address = user_id;
protocol = cnct->p_cnct_versions;
protocol->p_cnct_version = PROTOCOL_VERSION7;
protocol->p_cnct_architecture = arch_generic;
protocol->p_cnct_min_type = ptype_rpc;
protocol->p_cnct_max_type = MAX_PTYPE;
protocol->p_cnct_weight = 2;
++protocol;
protocol->p_cnct_version = PROTOCOL_VERSION7;
protocol->p_cnct_architecture = ARCHITECTURE;
protocol->p_cnct_min_type = ptype_rpc;
protocol->p_cnct_max_type = MAX_PTYPE;
protocol->p_cnct_weight = 3;
++protocol;
protocol->p_cnct_version = PROTOCOL_VERSION8;
protocol->p_cnct_architecture = arch_generic;
protocol->p_cnct_min_type = ptype_rpc;
protocol->p_cnct_max_type = MAX_PTYPE;
protocol->p_cnct_weight = 4;
++protocol;
protocol->p_cnct_version = PROTOCOL_VERSION8;
protocol->p_cnct_architecture = ARCHITECTURE;
protocol->p_cnct_min_type = ptype_rpc;
protocol->p_cnct_max_type = MAX_PTYPE;
protocol->p_cnct_weight = 5;
#ifdef SCROLLABLE_CURSORS
++protocol;
protocol->p_cnct_version = PROTOCOL_SCROLLABLE_CURSORS;
protocol->p_cnct_architecture = arch_generic;
protocol->p_cnct_min_type = ptype_rpc;
protocol->p_cnct_max_type = MAX_PTYPE;
protocol->p_cnct_weight = 6;
++protocol;
protocol->p_cnct_version = PROTOCOL_SCROLLABLE_CURSORS;
protocol->p_cnct_architecture = ARCHITECTURE;
protocol->p_cnct_min_type = ptype_rpc;
protocol->p_cnct_max_type = MAX_PTYPE;
protocol->p_cnct_weight = 7;
#endif
/* Try connection using first set of protocols. punt if error */
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;
/* If we can't talk to a server, punt. Let somebody else generate
an error. status_vector will have the network error info. */
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 = REMOTE_make_string(node_name);
/* Load the network libraries, and initialize the global task id */
if (initSPX(port))
return NULL;
ret = NWGetDefaultConnectionID(&connid);
if (ret) {
spxnet_error(port, "NWGetDefaultConnectionID", isc_net_lookup_err,
ret, NULL);
return NULL;
}
ret =
NWReadPropertyValue(connid, node_name, OT_FILE_SERVER, "NET_ADDRESS",
1, dest_address, (LPBYTE) & outsegs,
(LPBYTE) & outflag);
if (ret != 0) {
TEXT msg[64];
spxnet_gen_error(port, isc_network_error,
gds_arg_string,
(STATUS) port->port_connection->str_data,
isc_arg_gds, isc_net_lookup_err, SYS_ERR,
ret - 30000 + SPX_ERROR_BEG, 0);
sprintf(msg, "SPXNET/spxnet_error: ReadPropertyValue errno = %#x",
ret);
gds__log(msg, 0);
return NULL;
}
local_socket = 0;
ret = IPXOpenSocket(tskid, (void far *) &local_socket, 0xff);
handle_handle =
GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(spx_port_handle));
handle = (spx_port_handle FAR *) GlobalLock(handle_handle);
handle->global_handle = handle_handle;
handle->task_id = tskid;
handle->local_socket = local_socket;
memcpy(&(handle->server_address), dest_address, sizeof(IPXAddress));
memcpy(&(handle->server_address.socket), &dest_socket, 2);
build_ecb_pool(handle, 0);
handle->next_fragment = 0;
handle->window_handle = 0;
handle->next = 0;
handle->connection_id = 0;
port->port_handle = (HANDLE) handle;
/* queue it up */
if (head_of_spx_port_list == 0)
head_of_spx_port_list = handle;
else {
handle->next = head_of_spx_port_list;
head_of_spx_port_list = handle;
}
IPXGetInternetworkAddress(tskid, (BYTE FAR *) & my_address);
memcpy(my_address.socket, &local_socket, 2);
connhdr.length = NWWordSwap(sizeof(SPXHeader));
connhdr.packetType = (SCHAR) 5;
memcpy(&(connhdr.destination), &(handle->server_address),
sizeof(IPXAddress));
connecb.socketNumber = local_socket;
connecb.fragmentCount = 1;
connecb.fragmentDescriptor[0].size = sizeof(SPXHeader);
connecb.fragmentDescriptor[0].address = (SCHAR far *) & connhdr;
connecb.ESRAddress = 0;
ret =
SPXEstablishConnection(tskid, (BYTE) 15, (BYTE) 0xFF,
(LPWORD) & connid, &connecb);
if (!ret) {
while (connecb.inUseFlag != 0)
IPXRelinquishControl();
}
if (ret || connecb.completionCode) {
spxnet_error(port, "SpxEstablishConnection", isc_net_connect_err,
ret ? ret : connecb.completionCode, NULL);
disconnect(port);
ALLR_release((BLK) rdb);
return NULL;
}
handle->connection_id = connid;
send_full(port, packet);
/* Get response packet from server. */
rdb->rdb_port = port;
port->port_context = rdb;
if (!port->receive(packet)) {
disconnect(port);
ALLR_release((BLK) rdb);
return NULL;
}
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;
}
static accept_connection(port, cnct)
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 length, l;
#if FALSE
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;
#if FALSE
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++) != NULL)
do
*p++ = *id++;
while (--length);
*p = 0;
break;
case CNCT_user_verification:
#if FALSE
user_verification = 1;
#endif
id++;
break;
default:
id += *id + 1;
}
return TRUE;
}
static PORT alloc_port(parent)
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];
int size;
size = BUFFER_SIZE;
size *= 2;
port = (PORT) ALLOCV(type_port, size);
port->port_type = port_spx;
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, "SPX (%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_flags = parent->port_flags;
}
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 = aux_connect;
port->port_request = 0;
port->port_buff_size = BUFFER_SIZE;
xdrspxnet_create(&port->port_send, port,
&port->port_buffer[BUFFER_SIZE], BUFFER_SIZE,
XDR_ENCODE);
xdrspxnet_create(&port->port_receive, port, port->port_buffer, 0,
XDR_DECODE);
return port;
}
static void disconnect(port)
PORT port;
{
/**************************************
*
* d i s c o n n e c t
*
**************************************
*
* Functional description
* Break a remote connection.
*
**************************************/
PORT parent, *ptr;
spx_port_handle FAR *handle;
/* If this is a sub-port, unlink it from it's parent */
if ((parent = port->port_parent) != NULL) {
if (port->port_async) {
disconnect(port->port_async);
port->port_async = NULL;
}
for (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) {
disconnect(port->port_async);
port->port_async = NULL;
}
}
handle = (spx_port_handle *) port->port_handle;
if (handle != NULL) {
if (handle->window_handle)
DestroyWindow(handle->window_handle);
terminate_port(handle);
}
cleanup_port(port);
return;
}
static void cleanup_port(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 terminate_port(spx_port_handle * handle)
{
/**************************************
*
* t e r m i n a t e _ p o r t
*
**************************************
*
* Functional description
*
**************************************/
ECB FAR *tecb;
SPXHeader FAR *spx;
spx_port_handle FAR *prev_handle;
spx_port_handle *tmp_handle;
HGLOBAL ecb_mem;
UCHAR *t_ecb;
if (handle == NULL)
return;
if (handle->connection_id) {
ecb_mem = GlobalAlloc(GPTR | GMEM_DDESHARE,
(sizeof(ECB) + MAX_DATA + sizeof(SPXHeader)));
t_ecb = (UCHAR *) GlobalLock(ecb_mem);
/* Format and send the disconnect request to the other side */
t_ecb += MAX_DATA;
tecb = (ECB *) t_ecb;
t_ecb += sizeof(ECB);
spx = (void *) t_ecb;
tecb->ESRAddress = 0;
tecb->inUseFlag = 0;
tecb->completionCode = 0;
tecb->socketNumber = handle->local_socket;
tecb->fragmentCount = 1;
tecb->fragmentDescriptor[0].address = spx;
tecb->fragmentDescriptor[0].size = sizeof(SPXHeader);
spx->packetType = (SCHAR) 5;
spx->length = sizeof(SPXHeader);
SPXTerminateConnection(handle->task_id, handle->connection_id, tecb);
while (tecb->inUseFlag != 0)
IPXRelinquishControl();
GlobalFree(ecb_mem);
}
IPXCloseSocket(handle->task_id, handle->local_socket);
/* Free the pool of ecb's for this connection */
free_ecb_pool(handle);
/* unlink this structure from the list */
if (head_of_spx_port_list == handle)
head_of_spx_port_list = head_of_spx_port_list->next;
else {
prev_handle = head_of_spx_port_list;
tmp_handle = head_of_spx_port_list;
while (tmp_handle != NULL) {
if (tmp_handle == handle)
break;
prev_handle = tmp_handle;
tmp_handle = tmp_handle->next;
}
prev_handle->next = tmp_handle->next;
}
GlobalFree(handle->global_handle);
}
static void spxnet_copy(from, to, length)
SCHAR *from, *to;
int length;
{
/**************************************
*
* s p x n e t _ c o p y
*
**************************************
*
* Functional description
* Copy a number of bytes;
*
**************************************/
if (length)
do
*to++ = *from++;
while ((--length) != 0);
}
static void spxnet_zero(address, length)
SCHAR *address;
int length;
{
/**************************************
*
* s p x n e t _ z e r o
*
**************************************
*
* Functional description
* Zero a block of memory.
*
**************************************/
if (length)
do
*address++ = 0;
while ((--length) != 0);
}
static PORT receive(main_port, packet)
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))
return (NULL);
return main_port;
}
static send_full(port, packet)
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 xdrspxnet_endofrecord(&port->port_send, TRUE);
}
static send_partial(port, packet)
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 xdrspxnet_create(xdrs, port, buffer, length, x_op)
XDR *xdrs;
PORT port;
UCHAR *buffer;
USHORT length;
enum xdr_op x_op;
{
/**************************************
*
* x d r s p x 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 = &spxnet_ops;
xdrs->x_op = x_op;
return TRUE;
}
static bool_t xdrspxnet_endofrecord(xdrs, flushnow)
XDR *xdrs;
bool_t flushnow;
{
/**************************************
*
* x d r s p x n e t _ e n d o f r e c o r d
*
**************************************
*
* Functional description
* Write out the rest of a record.
*
**************************************/
return spxnet_write(xdrs, flushnow);
}
static void spxnet_destroy()
{
/**************************************
*
* s p x n e t _ d e s t r o y
*
**************************************
*
* Functional description
* Destroy a stream. A no-op.
*
**************************************/
}
static bool_t spxnet_getbytes(xdrs, buff, count)
XDR *xdrs;
SCHAR *buff;
u_int count;
{
/**************************************
*
* s p x 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 > 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 (!spxnet_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 && !spxnet_read(xdrs))
return FALSE;
*buff++ = *xdrs->x_private++;
--xdrs->x_handy;
}
return TRUE;
}
static bool_t spxnet_getlong(xdrs, lp)
XDR *xdrs;
SLONG *lp;
{
/**************************************
*
* s p x 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, &l, 4))
return FALSE;
*lp = ntohl(l);
return TRUE;
}
static u_int spxnet_getpostn(xdrs)
XDR *xdrs;
{
/**************************************
*
* s p x 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 spxnet_inline(xdrs, bytecount)
XDR *xdrs;
u_int bytecount;
{
/**************************************
*
* s p x n e t _ i n l i n e
*
**************************************
*
* Functional description
* Return a pointer to somewhere in the buffer.
*
**************************************/
if (bytecount > xdrs->x_handy)
return FALSE;
return xdrs->x_base + bytecount;
}
static bool_t spxnet_putbytes(xdrs, buff, count)
XDR *xdrs;
SCHAR *buff;
u_int count;
{
/**************************************
*
* s p x 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 > 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 (!spxnet_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 && !spxnet_write(xdrs, 0))
return FALSE;
--xdrs->x_handy;
*xdrs->x_private++ = *buff++;
}
return TRUE;
}
static bool_t spxnet_putlong(xdrs, lp)
XDR *xdrs;
SLONG *lp;
{
/**************************************
*
* s p x 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, AOF32L(l), 4);
}
static bool_t spxnet_read(xdrs)
XDR *xdrs;
{
/**************************************
*
* s p x 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. */
while (TRUE) {
length = end - p;
if (!packet_receive(port, p, length, &length))
return FALSE;
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 spxnet_setpostn(xdrs, bytecount)
XDR *xdrs;
u_int bytecount;
{
/**************************************
*
* s p x 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 > xdrs->x_handy)
return FALSE;
xdrs->x_private = xdrs->x_base + bytecount;
return TRUE;
}
static bool_t spxnet_write(xdrs, end_flag)
XDR *xdrs;
bool_t end_flag;
{
/**************************************
*
* s p x 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 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;
if (!packet_send(port, p, length))
return FALSE;
xdrs->x_private = xdrs->x_base;
xdrs->x_handy = BUFFER_SIZE;
return TRUE;
}
static void exit_handler(main_port)
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)
terminate_port((spx_port_handle *) port->port_handle);
}
static bool_t packet_send(port, buffer, buffer_length)
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.
*
**************************************/
ECB *ecb;
SPXHeader FAR *txhdr;
spx_port_handle *handle;
SCHAR FAR *tx_mem;
SCHAR FAR *data;
int i, ccode;
WORD length;
WORD index;
handle = (spx_port_handle *) port->port_handle;
if (handle == NULL)
/* The connection already died and was closed */
return spxnet_error(port, "SPXSendSequencedPacket", isc_net_write_err,
SPX_CONNECTION_TERMINATED, NULL);
index = 0;
ccode = 0;
/* Keep looping through the ecbs until all data is sent */
while (buffer_length && !ccode) {
i = 0;
tx_mem = handle->tx_pool;
while (buffer_length && i < TX_POOL_SIZE) {
data = tx_mem;
tx_mem += MAX_DATA;
ecb = (ECB *) tx_mem;
tx_mem += sizeof(ECB);
txhdr = (SPXHeader FAR *) tx_mem;
tx_mem += sizeof(SPXHeader);
setup_tx_ecb(ecb, txhdr, handle, data);
length = buffer_length <= MAX_DATA ? buffer_length : MAX_DATA;
buffer_length -= length;
if (!buffer_length)
/* This is the last packet */
txhdr->connectionControl |= 0x10;
else
txhdr->connectionControl = 0;
txhdr->length = NWWordSwap(sizeof(SPXHeader) + length);
ecb->fragmentDescriptor[1].size = length;
memcpy(data, &(buffer[index]), length);
index += length;
SPXSendSequencedPacket(handle->task_id, handle->connection_id,
ecb);
i++;
}
/* We used up all the ecbs, wait for them to clear and start again */
if (!wait_for_free_tx_ecbs(port))
return FALSE;
}
return (TRUE);
}
static bool_t wait_for_free_tx_ecbs(PORT port)
{
/**************************************
*
* w a i t _ f o r _ f r e e _ t x _ e c b s
*
**************************************
*
* Functional description
* wait for free tx ecb pool
*
**************************************/
SCHAR FAR *tx_mem;
ECB FAR *ecb;
int i;
spx_port_handle *handle;
handle = (spx_port_handle *) port->port_handle;
tx_mem = handle->tx_pool;
i = 0;
while (i < TX_POOL_SIZE) {
tx_mem += MAX_DATA;
ecb = (ECB *) tx_mem;
tx_mem += sizeof(ECB) + sizeof(SPXHeader);
while (ecb->inUseFlag != 0)
IPXRelinquishControl();
if (ecb->completionCode != 0)
return ((bool_t) (spxnet_error(port, "SPXSendSequencedPacket",
isc_net_write_err,
ecb->completionCode, ecb)));
i++;
}
return TRUE;
}
static void setup_tx_ecb(ECB FAR * ecb, SPXHeader FAR * txhdr,
spx_port_handle * handle, SCHAR FAR * data)
{
/**************************************
*
* s e t u p _ t x _ e c b
*
**************************************
*
* Functional description
* set up tx ecbs
*
**************************************/
ecb->ESRAddress = 0;
ecb->inUseFlag = 0;
ecb->completionCode = 0;
ecb->fragmentCount = 2;
ecb->fragmentDescriptor[0].address = txhdr;
ecb->fragmentDescriptor[0].size = sizeof(SPXHeader);
ecb->fragmentDescriptor[1].address = data;
txhdr->packetType = (SCHAR) 5;
txhdr->dataStreamType = 0;
memcpy(&(txhdr->destination.network), &(handle->server_address), 12);
memcpy(&(txhdr->source.network), &my_address, 12);
}
static packet_receive(port, buffer, buffer_length, length)
PORT port;
UCHAR *buffer;
SSHORT buffer_length, *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.
*
**************************************/
SCHAR FAR *data;
ECB FAR *ecb;
SPXHeader FAR *spx;
spx_port_handle *handle;
WORD len;
WORD buffer_offset;
SCHAR last_fragment = FALSE;
handle = (spx_port_handle *) port->port_handle;
if (handle == NULL)
/* The connection already died and was closed */
return spxnet_error(port, "SPXListenForSequencedPacket",
isc_net_read_err, SPX_CONNECTION_TERMINATED,
NULL);
buffer_offset = 0;
last_fragment = FALSE;
len = 0;
while (!last_fragment) {
ecb = get_next_fragment(handle);
if (!ecb)
return 0;
spx = (SPXHeader *) ecb->fragmentDescriptor[0].address;
data = ecb->fragmentDescriptor[1].address;
data += sizeof(SLONG);
len = spx->length;
len = NWWordSwap(len);
len -= (sizeof(SPXHeader) + sizeof(SLONG));
if ((spx->dataStreamType == 0xFE) || (ecb->completionCode != 0)) {
spxnet_error(port, "SPXListenForSequencedPacket",
isc_net_read_err, ecb->completionCode, ecb);
/* If the connection is still valid, repost the listen on this ecb */
if (port->port_handle != NULL)
SPXListenForSequencedPacket(handle->task_id, ecb);
return 0;
}
memcpy(buffer + buffer_offset, data, len);
buffer_offset += len;
if (spx->connectionControl & 0x10)
last_fragment = TRUE;
/* Repost the listen for this ecb */
SPXListenForSequencedPacket(handle->task_id, ecb);
}
*length = buffer_offset;
return (TRUE);
}
static ECB *get_next_fragment(spx_port_handle * handle)
{
/**************************************
*
* g e t _ n e x t _ f r a g m e n t
*
**************************************/
ECB FAR *ecb;
int i;
SCHAR FAR *mem;
SPXHeader FAR *spx;
SCHAR FAR *data;
SLONG fragment_number;
SCHAR found = FALSE;
while (!found) {
i = 0;
mem = handle->rx_pool;
while (i < RX_POOL_SIZE) {
mem += MAX_DATA;
ecb = (ECB *) mem;
mem += sizeof(ECB);
mem += sizeof(SPXHeader);
if (ecb->inUseFlag == 0) {
spx = (SPXHeader *) ecb->fragmentDescriptor[0].address;
if (ecb->completionCode || spx->dataStreamType == 0xFE)
return ecb;
data = ecb->fragmentDescriptor[1].address;
fragment_number = *((SLONG *) data);
if (fragment_number == handle->next_fragment) {
found = TRUE;
break;
}
}
i++;
}
if (!found) {
Yield();
}
}
handle->next_fragment++;
return (ecb);
}
static void build_ecb_pool(spx_port_handle * handle, void *esr)
{
/**************************************
*
* b u i l d _ e c b _ p o o l
*
**************************************/
int i;
UCHAR *data;
ECB *ecb;
SPXHeader *spx;
UCHAR *rx_mem;
handle->rx_global = GlobalAlloc(GPTR | GMEM_DDESHARE,
RX_POOL_SIZE * (sizeof(ECB) + MAX_DATA +
sizeof(SPXHeader)));
rx_mem = GlobalLock(handle->rx_global);
handle->rx_pool = rx_mem;
handle->tx_global = GlobalAlloc(GPTR | GMEM_DDESHARE,
TX_POOL_SIZE * (sizeof(ECB) + MAX_DATA +
sizeof(SPXHeader)));
handle->tx_pool = GlobalLock(handle->tx_global);
i = 0;
while (i < RX_POOL_SIZE) {
data = rx_mem;
rx_mem += MAX_DATA;
ecb = (ECB *) rx_mem;
rx_mem += sizeof(ECB);
spx = (SPXHeader *) rx_mem;
rx_mem += sizeof(SPXHeader);
ecb->ESRAddress = esr;
ecb->inUseFlag = 0;
ecb->completionCode = 0;
ecb->socketNumber = handle->local_socket;
ecb->fragmentCount = 2;
ecb->fragmentDescriptor[0].address = spx;
ecb->fragmentDescriptor[0].size = sizeof(SPXHeader);
ecb->fragmentDescriptor[1].address = data;
ecb->fragmentDescriptor[1].size = MAX_DATA;
spx->packetType = (SCHAR) 5;
spx->length = sizeof(SPXHeader);
SPXListenForSequencedPacket(handle->task_id, ecb);
i++;
}
}
static void free_ecb_pool(spx_port_handle * handle)
{
/**************************************
*
* f r e e _ e c b _ p o o l
*
**************************************/
int i = 0;
int ccode;
ECB FAR *ecb;
UCHAR FAR *rx_mem;
UCHAR msg[128];
rx_mem = handle->rx_pool;
/* Cancel any outstanding listens on the ECBs */
while (i < RX_POOL_SIZE) {
/* Get the ecb by skipping past the data */
rx_mem += MAX_DATA;
ecb = (ECB *) rx_mem;
/* Increment rx_mem past ecb and spx header to the next block */
rx_mem += sizeof(ECB) + sizeof(SPXHeader);
if (ecb->inUseFlag != 0) {
ccode = IPXCancelEvent(handle->task_id, ecb);
if (ccode) {
sprintf(msg,
"SPXWIN/spxwin_error: IPXCancelEvent errno = %#x",
ccode);
gds__log(msg, 0);
}
else {
/* Loop, waiting for the event to be canceled */
while (ecb->completionCode != 0xfc)
IPXRelinquishControl();
}
}
i++;
}
/* Free the ecb memory */
GlobalFree(handle->rx_global);
GlobalFree(handle->tx_global);
}
void spx_cleanup(void *arg)
{
/**************************************
*
* spx_cleanup
*
**************************************
*
* Functional description:
* Cleanup on exit
*
**************************************/
spx_port_handle *port_handle = head_of_spx_port_list;
if (SPX_initialized) {
/* Free any open connections before terminating */
while (port_handle != NULL) {
/*
** Can't call disconnect at this point since the port memory block was
** already freed.
*/
terminate_port(port_handle);
/*
** disconnect will delete the handle from the list, so rather
** than walking the list, just keep deleting the front until it's
** empty.
*/
port_handle = head_of_spx_port_list;
}
IPXSPXDeinit(tskid);
FreeLibrary(hSpxIpxLib);
FreeLibrary(hNwCallsLib);
SPX_initialized = FALSE;
}
}
static int spxnet_error(
PORT port,
TEXT * function,
STATUS operation, int status, ECB * ecb)
{
/**************************************
*
* s p x n e t _ e r r o r
*
**************************************
*
* Functional description
* An I/O error has occurred. Call
* spxnet_gen_error with the appropriate args
* to format the status vector if any.
* In any case, return NULL, which
* is used to indicate an error.
*
**************************************/
TEXT msg[64];
SPXHeader *spx;
if (ecb != NULL) {
/*
** If we have an ecb, check if the connection died on the
** other side and if so, free the resources on our side.
*/
spx = (SPXHeader *) (ecb->fragmentDescriptor[0].address);
if ((spx->dataStreamType == 0xFE) ||
(ecb->completionCode == SPX_CONNECTION_TERMINATED) ||
(ecb->completionCode == SPX_CONNECTION_FAILED)) {
terminate_port((spx_port_handle *) port->port_handle);
port->port_handle = NULL;
}
}
if (status) {
spxnet_gen_error(port, isc_network_error,
gds_arg_string,
(STATUS) port->port_connection->str_data,
isc_arg_gds, operation, SYS_ERR,
status + SPX_ERROR_BEG, 0);
sprintf(msg, "SPXNET/spxnet_error: %s errno = %#x", function, status);
gds__log(msg, 0);
}
else {
/* No status value, just format the basic arguments. */
spxnet_gen_error(port, isc_network_error,
gds_arg_string,
(STATUS) port->port_connection->str_data,
isc_arg_gds, operation, 0);
}
return (int) NULL;
}
static void spxnet_gen_error( PORT port, STATUS status, ...)
{
/**************************************
*
* s p x n e t _ g e n _ e r r o r
*
**************************************
*
* Functional description
* An error has occurred.
* Format the status vector if there is one and
* save the status vector strings in a permanent place.
*
**************************************/
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);
}
}
#define MAX_TASKS 128
#define GET_WIN_ENTRYPOINT(hlib, funcp, funcname) \
funcp = GetProcAddress (hlib, funcname);\
if (funcp == NULL) \
{ return FAILURE; }
static int initSPX(PORT port)
{
/**************************************
*
* i n i t S P X
*
**************************************
*
* Functional description:
* Initializes the SPX library.
*
**************************************/
int ret = SUCCESS;
BYTE majrev, minrev; /* major and minor rev# of SPX */
WORD maxcon, avacon; /* max and avail # of connections */
extern void spx_cleanup(void *);
extern HINSTANCE LoadDll(UCHAR *);
if (!SPX_initialized) {
hSpxIpxLib = hNwCallsLib = 0;
hSpxIpxLib = LoadDll("nwipxspx.dll");
if (hSpxIpxLib) {
GET_WIN_ENTRYPOINT(hSpxIpxLib, IPXCancelEventPtr,
"IPXCancelEvent")
GET_WIN_ENTRYPOINT(hSpxIpxLib, IPXCloseSocketPtr,
"IPXCloseSocket")
GET_WIN_ENTRYPOINT(hSpxIpxLib, IPXGetInternetworkAddressPtr,
"IPXGetInternetworkAddress")
GET_WIN_ENTRYPOINT(hSpxIpxLib, IPXOpenSocketPtr,
"IPXOpenSocket")
GET_WIN_ENTRYPOINT(hSpxIpxLib, IPXRelinquishControlPtr,
"IPXRelinquishControl")
GET_WIN_ENTRYPOINT(hSpxIpxLib, IPXSPXDeinitPtr,
"IPXSPXDeinit")
GET_WIN_ENTRYPOINT(hSpxIpxLib, SPXEstablishConnectionPtr,
"SPXEstablishConnection")
GET_WIN_ENTRYPOINT(hSpxIpxLib, SPXInitializePtr,
"SPXInitialize")
GET_WIN_ENTRYPOINT(hSpxIpxLib, SPXListenForSequencedPacketPtr,
"SPXListenForSequencedPacket")
GET_WIN_ENTRYPOINT(hSpxIpxLib, SPXSendSequencedPacketPtr,
"SPXSendSequencedPacket")
GET_WIN_ENTRYPOINT(hSpxIpxLib, SPXTerminateConnectionPtr,
"SPXTerminateConnection")
/*
** Call SPX Initialize to see if the network is installed.
** On input, tskid is set to indicate resources should be
** allocated in a pool for access by multiple apps. On output,
** it will be the task id used by later SPX calls.
*/
tskid = 0xffffffffL;
ret = SPXInitialize(&tskid, RX_POOL_SIZE, 576,
&majrev,
&minrev,
(LPWORD) & maxcon, (LPWORD) & avacon);
/*
** This needs better error handling so the user gets a
** status vector explaining what went wrong
*/
if (ret == SPX_INSTALLED || ret == IPXSPX_PREV_INIT) {
/*
** If SPX is around, go ahead and load nwcalls dll. Otherwise,
** if SPX is not installed then loading nwcalls will cause
** windows to get an error looking for netware.dll
*/
ret = SUCCESS;
hNwCallsLib = LoadDll("nwcalls.dll");
if (hNwCallsLib) {
GET_WIN_ENTRYPOINT(hNwCallsLib, NWCallsInitPtr,
"NWCallsInit")
GET_WIN_ENTRYPOINT(hNwCallsLib,
NWGetDefaultConnectionIDPtr,
"NWGetDefaultConnectionID")
GET_WIN_ENTRYPOINT(hNwCallsLib,
NWReadPropertyValuePtr,
"NWReadPropertyValue")
GET_WIN_ENTRYPOINT(hNwCallsLib, NWWordSwapPtr,
"NWWordSwap")
}
else {
spxnet_gen_error(port,
isc_network_error,
gds_arg_string,
port->port_connection->str_data,
isc_arg_gds,
isc_net_init_error,
isc_arg_gds,
isc_loadlib_failure,
isc_arg_string, "nwcalls.dll", 0);
IPXSPXDeinit(tskid);
ret = FAILURE;
}
}
else {
/* There was an error initing SPX */
spxnet_error(port, "SPXInitialize", isc_net_init_error, ret,
NULL);
ret = FAILURE;
}
}
else {
/* Unable to load nwipxspx.dll */
spxnet_gen_error(port,
isc_network_error,
gds_arg_string,
port->port_connection->str_data,
isc_arg_gds,
isc_net_init_error,
isc_arg_gds,
isc_loadlib_failure,
isc_arg_string, "nwipxspx.dll", 0);
ret = FAILURE;
}
if (ret != SUCCESS) {
if (hSpxIpxLib)
FreeLibrary(hSpxIpxLib);
if (hNwCallsLib)
FreeLibrary(hNwCallsLib);
SPX_initialized = FALSE;
ret = FAILURE;
}
else {
SPX_initialized = TRUE;
}
}
return ret;
}
static PORT aux_connect(PORT port, PACKET * packet, XDR_INT(*ast) ())
{
/**************************************
*
* a u x _ c o n n e c t
*
**************************************
*
* Functional description
*
**************************************/
P_RESP *response;
PORT new_port;
spx_port_handle FAR *handle;
spx_port_handle FAR *new_handle;
HGLOBAL new_handle_handle;
WORD local_socket;
ECB connecb;
SPXHeader connhdr;
NWCONN_HANDLE connid;
IPXAddress dest_address;
int ret;
DWORD tskid;
extern HINSTANCE hInstance;
HWND hwnd;
/* format response packet with my address in it */
response = &packet->p_resp;
spxnet_copy(response->p_resp_data.cstr_address, &dest_address,
response->p_resp_data.cstr_length);
/* get the spx port handle */
handle = (spx_port_handle *) port->port_handle;
/* get the task id */
tskid = handle->task_id;
/* alloc a new port */
port->port_async = new_port = alloc_port(port->port_parent);
new_port->port_flags |= PORT_async;
new_port->port_flags |= port->port_flags & PORT_no_oob;
new_port->port_ast = ast;
local_socket = 0;
ret = IPXOpenSocket(tskid, (void far *) &local_socket, 0xff);
/* set up new spx port handle */
new_handle_handle =
GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(spx_port_handle));
new_handle = (spx_port_handle FAR *) GlobalLock(new_handle_handle);
new_handle->global_handle = new_handle_handle;
new_handle->task_id = handle->task_id;
new_handle->local_socket = local_socket;
memcpy(&(new_handle->server_address), &(dest_address),
sizeof(IPXAddress));
new_handle->next_fragment = 0;
new_handle->next = 0;
build_ecb_pool(new_handle, process_async_data);
/* Create the window which will get posted to in the network esr */
hwnd = CreateWindow("RemoteMsgClassSpx", "RemoteMsgWndSpx",
WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
SetWindowLong(hwnd, 0, (long) new_port);
new_handle->window_handle = hwnd;
/* set up ecb to use to establish connection */
connhdr.length = NWWordSwap(sizeof(SPXHeader));
connhdr.packetType = (SCHAR) 5;
memcpy(&(connhdr.destination), &(dest_address), sizeof(IPXAddress));
connecb.socketNumber = local_socket;
connecb.fragmentCount = 1;
connecb.fragmentDescriptor[0].size = sizeof(SPXHeader);
connecb.fragmentDescriptor[0].address = (SCHAR far *) & connhdr;
connecb.ESRAddress = 0;
/* queue it up */
if (head_of_spx_port_list == 0)
head_of_spx_port_list = new_handle;
else {
new_handle->next = head_of_spx_port_list;
head_of_spx_port_list = new_handle;
}
/* establish a connection */
ret =
SPXEstablishConnection(tskid, (BYTE) 0x30, (BYTE) 0xFF,
(LPWORD) & connid, &connecb);
/* wait for connection to be established */
if (!ret) {
while (connecb.inUseFlag != 0)
IPXRelinquishControl();
}
if (ret || connecb.completionCode) {
spxnet_error(port, "SpxEstablishConnection",
isc_net_event_connect_err,
ret ? ret : connecb.completionCode, NULL);
terminate_port(new_handle);
return NULL;
}
/* set new connection info */
new_handle->connection_id = connid;
new_port->port_handle = (HANDLE) new_handle;
return new_port;
}
LRESULT FAR PASCAL spxnet_wndproc(HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
/**************************************
*
* s p x n e t _ w n d p r o c
*
* called when app notices a message has been posted
* to this window.
*
**************************************
*
* Functional description:
* This is the windows equivalent of inet_handler.
* When an async request completes, a message will be posted
* to this window. The function then calls the ast function
* in the port structure (saved in the window extra bytes)
* and re-initiates the async listen on the port.
*
**************************************/
PORT port;
if (message == WM_USER + 1) {
/* get port */
port = (PORT) GetWindowLong(hwnd, 0);
/* call the ast */
(port->port_ast) (port);
/* Tell Windows it does not need to process the message */
return FALSE;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}