fis-gtm/sr_port/iosocket_open.c

484 lines
18 KiB
C

/****************************************************************
* *
* Copyright 2012, 2013 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include <errno.h>
#include "gtm_string.h"
#include "gtm_stdio.h"
#include "gtm_socket.h"
#include "gtm_inet.h"
#include "gtm_time.h"
#include "copy.h"
#include "gt_timer.h"
#include "io.h"
#include "iotimer.h"
#include "iotcp_select.h"
#include "iotcpdef.h"
#include "iotcproutine.h"
#include "io_params.h"
#include "iosocketdef.h"
#include "gtm_caseconv.h"
#include "stringpool.h"
#include "gtm_conv.h"
#include "gtm_utf8.h"
#include "gtm_netdb.h"
GBLREF tcp_library_struct tcp_routines;
GBLREF d_socket_struct *socket_pool, *newdsocket;
GBLREF io_pair io_std_device; /* standard device */
GBLREF boolean_t gtm_utf8_mode;
GBLREF int4 gtm_max_sockets;
GBLREF boolean_t dollar_zininterrupt;
LITREF unsigned char io_params_size[];
LITREF mstr chset_names[];
error_def(ERR_ABNCOMPTINC);
error_def(ERR_ADDRTOOLONG);
error_def(ERR_DELIMSIZNA);
error_def(ERR_DELIMWIDTH);
error_def(ERR_DEVPARINAP);
error_def(ERR_DEVPARMNEG);
error_def(ERR_GETNAMEINFO);
error_def(ERR_ILLESOCKBFSIZE);
error_def(ERR_MRTMAXEXCEEDED);
error_def(ERR_SOCKETEXIST);
error_def(ERR_SOCKMAX);
error_def(ERR_TEXT);
error_def(ERR_ZFF2MANY);
error_def(ERR_ZINTRECURSEIO);
#define ESTABLISHED "ESTABLISHED"
short iosocket_open(io_log_name *dev, mval *pp, int file_des, mval *mspace, int4 timepar)
{
char addr[SA_MAXLITLEN], *errptr, sockaddr[SA_MAXLITLEN],
temp_addr[SA_MAXLITLEN], dev_type[MAX_DEV_TYPE_LEN];
unsigned char ch, *c, *next, *top;
int handle_len, moreread_timeout, len;
unsigned short port;
int4 errlen, msec_timeout, real_errno, p_offset = 0, zff_len, delimiter_len;
int d_socket_struct_len;
ABS_TIME cur_time, end_time;
io_desc *ioptr;
fd_set tcp_fd;
uint4 bfsize = DEFAULT_SOCKET_BUFFER_SIZE, ibfsize;
d_socket_struct *dsocketptr;
socket_struct *socketptr;
mv_stent *mv_zintdev;
boolean_t zint_conn_restart = FALSE;
socket_interrupt *sockintr;
mstr chset_mstr;
boolean_t attach_specified = FALSE,
listen_specified = FALSE,
connect_specified = FALSE,
ioerror_specified = FALSE,
delay_specified = FALSE,
nodelay_specified = FALSE,
ibfsize_specified = FALSE,
moreread_specified = FALSE,
is_principal = FALSE, /* called from inetd */
ichset_specified,
ochset_specified;
unsigned char delimiter_buffer[MAX_N_DELIMITER * (MAX_DELIM_LEN + 1)], zff_buffer[MAX_ZFF_LEN];
char ioerror, ip[3], tcp[4],
sock_handle[MAX_HANDLE_LEN], delimiter[MAX_DELIM_LEN + 1];
int socketptr_delim_len;
char ipaddr[SA_MAXLEN];
int errcode;
struct addrinfo *ai_ptr, *remote_ai_ptr;
ioptr = dev->iod;
assert((params) *(pp->str.addr + p_offset) < (unsigned char)n_iops);
assert(ioptr != 0);
assert(ioptr->state >= 0 && ioptr->state < n_io_dev_states);
assert(ioptr->type == gtmsocket);
if ((ioptr->state == dev_closed) && mspace && mspace->str.len && mspace->str.addr)
{
lower_to_upper((uchar_ptr_t)dev_type, (uchar_ptr_t)mspace->str.addr, mspace->str.len);
if (STR_LIT_LEN("SOCKET") != mspace->str.len || 0 != memcmp(dev_type, "SOCKET", STR_LIT_LEN("SOCKET")))
{
if (ioptr->dev_sp)
free(ioptr->dev_sp);
ioptr->state = dev_never_opened;
}
}
d_socket_struct_len = SIZEOF(d_socket_struct) + (SIZEOF(socket_struct) * (gtm_max_sockets - 1));
if (ioptr->state == dev_never_opened)
{
dsocketptr = ioptr->dev_sp = (void *)malloc(d_socket_struct_len);
ioptr->newly_created = TRUE;
memset(dsocketptr, 0, d_socket_struct_len);
dsocketptr->iod = ioptr;
} else
dsocketptr = (d_socket_struct *)ioptr->dev_sp;
if (ioptr->state == dev_never_opened)
{
ioptr->state = dev_closed;
ioptr->width = TCPDEF_WIDTH;
ioptr->length = TCPDEF_LENGTH;
ioptr->wrap = TRUE;
if (-1 == iotcp_fillroutine())
assert(FALSE);
if (!io_std_device.in)
/* called from io_init */
is_principal = TRUE;
}
if (dsocketptr->mupintr)
{ /* check if connect was interrupted */
sockintr = &dsocketptr->sock_save_state;
if (sockwhich_invalid == sockintr->who_saved)
GTMASSERT; /* Interrupt should never have an invalid save state */
if (dollar_zininterrupt)
{
dsocketptr->mupintr = FALSE;
sockintr->who_saved = sockwhich_invalid;
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZINTRECURSEIO);
}
if (sockwhich_connect != sockintr->who_saved)
GTMASSERT; /* ZINTRECURSEIO should have caught */
mv_zintdev = io_find_mvstent(dsocketptr->iod, FALSE);
if (mv_zintdev && mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid)
{ /* mupintr will be reset and mvstent popped in iosocket_connect */
connect_specified = TRUE;
ibfsize_specified = sockintr->ibfsize_specified;
assert(newdsocket);
assert(newdsocket == sockintr->newdsocket);
memcpy(newdsocket, (d_socket_struct *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr,
d_socket_struct_len);
socketptr = newdsocket->socket[newdsocket->current_socket];
assert(socketptr == (socket_struct *)mv_zintdev->mv_st_cont.mvs_zintdev.socketptr);
zint_conn_restart = TRUE; /* skip what we already did, state == dev_closed */
}
} else
{
ioptr->dollar.zeof = FALSE;
if (NULL == newdsocket)
newdsocket = (d_socket_struct *)malloc(d_socket_struct_len);
memcpy(newdsocket, dsocketptr, d_socket_struct_len);
memcpy(ioptr->dollar.device, "0", SIZEOF("0"));
zff_len = -1; /* indicates neither ZFF nor ZNOFF specified */
delimiter_len = -1; /* indicates neither DELIM nor NODELIM specified */
ichset_specified = ochset_specified = FALSE;
while (iop_eol != (ch = *(pp->str.addr + p_offset++)))
{
switch(ch)
{
case iop_delimiter:
delimiter_len = (int4)(unsigned char)*(pp->str.addr + p_offset);
if (((MAX_DELIM_LEN + 1) * MAX_N_DELIMITER) >= delimiter_len)
memcpy(delimiter_buffer, (pp->str.addr + p_offset + 1), delimiter_len);
else
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DELIMSIZNA);
break;
case iop_ipchset:
UNICODE_ONLY(
if (gtm_utf8_mode)
{ /* Only change ipchset if in UTF8 mode */
chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1);
chset_mstr.len = *(pp->str.addr + p_offset);
SET_ENCODING(ioptr->ichset, &chset_mstr);
ichset_specified = TRUE;
}
);
break;
case iop_opchset:
UNICODE_ONLY(
if (gtm_utf8_mode)
{ /* Only change ipchset if in UTF8 mode */
chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1);
chset_mstr.len = *(pp->str.addr + p_offset);
SET_ENCODING(ioptr->ochset, &chset_mstr);
ochset_specified = TRUE;
}
);
break;
case iop_chset:
UNICODE_ONLY(
if (gtm_utf8_mode)
{ /* Only change ipchset/opchset if in UTF8 mode */
chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1);
chset_mstr.len = *(pp->str.addr + p_offset);
SET_ENCODING(ioptr->ichset, &chset_mstr);
ioptr->ochset = ioptr->ichset;
ichset_specified = ochset_specified = TRUE;
}
);
break;
/* Note the following 4 cases (iop_m/utf16/utf16be/utf16le) have no corresponding device parameter
but are included here because they can be easily used in internal processing.
*/
case iop_m:
UNICODE_ONLY(
ioptr->ichset = ioptr->ochset = CHSET_M;
ichset_specified = ochset_specified = TRUE;
);
break;
case iop_utf16:
UNICODE_ONLY(
if (gtm_utf8_mode)
{ /* Only change chset if in UTF8 mode */
ioptr->ichset = ioptr->ochset = CHSET_UTF16;
ichset_specified = ochset_specified = TRUE;
}
);
break;
case iop_utf16be:
UNICODE_ONLY(
if (gtm_utf8_mode)
{ /* Only change chset if in UTF8 mode */
ioptr->ichset = ioptr->ochset = CHSET_UTF16BE;
ichset_specified = ochset_specified = TRUE;
}
);
break;
case iop_utf16le:
UNICODE_ONLY(
if (gtm_utf8_mode)
{ /* Only change chset if in UTF8 mode */
ioptr->ichset = ioptr->ochset = CHSET_UTF16LE;
ichset_specified = ochset_specified = TRUE;
}
);
break;
/**********************************/
case iop_nodelimiter:
delimiter_len = 0;
break;
case iop_zdelay:
delay_specified = TRUE;
break;
case iop_znodelay:
nodelay_specified = TRUE;
break;
case iop_zbfsize:
GET_ULONG(bfsize, pp->str.addr + p_offset);
if ((0 == bfsize) || (MAX_SOCKET_BUFFER_SIZE < bfsize))
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, bfsize);
break;
case iop_zibfsize:
ibfsize_specified = TRUE;
GET_ULONG(ibfsize, pp->str.addr + p_offset);
if ((0 == ibfsize) || (MAX_INTERNAL_SOCBUF_SIZE < ibfsize))
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, bfsize);
break;
case iop_zlisten:
listen_specified = TRUE;
len = (int)(*(pp->str.addr + p_offset));
if (len < SA_MAXLITLEN)
{
memset(sockaddr, 0, SIZEOF(sockaddr));
memcpy(sockaddr, pp->str.addr + p_offset + 1, len);
} else
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_ADDRTOOLONG, 4, len,
pp->str.addr + p_offset + 1, len, SA_MAXLITLEN);
break;
case iop_connect:
connect_specified = TRUE;
len = (int)(*(pp->str.addr + p_offset));
if (len < SA_MAXLITLEN)
{
memset(sockaddr, 0, SIZEOF(sockaddr));
memcpy(sockaddr, pp->str.addr + p_offset + 1, len);
} else
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_ADDRTOOLONG, 4,
len, pp->str.addr + p_offset + 1, len, SA_MAXLITLEN);
break;
case iop_ioerror:
ioerror_specified = TRUE;
ioerror = *(pp->str.addr + p_offset + 1); /* the first char decides */
break;
case iop_exception:
ioptr->error_handler.len = *(pp->str.addr + p_offset);
ioptr->error_handler.addr = (char *)(pp->str.addr + p_offset + 1);
s2pool(&ioptr->error_handler);
break;
case iop_attach:
attach_specified = TRUE;
handle_len = (int)(*(pp->str.addr + p_offset));
if (handle_len > MAX_HANDLE_LEN)
handle_len = MAX_HANDLE_LEN;
memcpy(sock_handle, pp->str.addr + p_offset + 1, handle_len);
break;
case iop_socket:
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DEVPARINAP);
break;
case iop_zff:
if (MAX_ZFF_LEN >= (zff_len = (int4)(unsigned char)*(pp->str.addr + p_offset)))
memcpy(zff_buffer, (char *)(pp->str.addr + p_offset + 1), zff_len);
else
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZFF2MANY, 2, zff_len, MAX_ZFF_LEN);
break;
case iop_znoff:
zff_len = 0;
break;
case iop_wrap:
ioptr->wrap = TRUE;
break;
case iop_nowrap:
ioptr->wrap = FALSE;
break;
case iop_morereadtime:
/* Time in milliseconds socket read will wait for more data before returning */
GET_LONG(moreread_timeout, pp->str.addr + p_offset);
if (-1 == moreread_timeout)
moreread_timeout = DEFAULT_MOREREAD_TIMEOUT;
else if (-1 > moreread_timeout)
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DEVPARMNEG);
else if (MAX_MOREREAD_TIMEOUT < moreread_timeout)
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_MRTMAXEXCEEDED, 1,
MAX_MOREREAD_TIMEOUT);
moreread_specified = TRUE;
break;
default:
break;
}
p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ?
(unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]);
}
if (!ichset_specified)
ioptr->ichset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M;
if (!ochset_specified)
ioptr->ochset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M;
if (CHSET_M != ioptr->ichset && CHSET_UTF16 != ioptr->ichset)
get_chset_desc(&chset_names[ioptr->ichset]);
if (CHSET_M != ioptr->ochset && CHSET_UTF16 != ioptr->ochset)
get_chset_desc(&chset_names[ioptr->ochset]);
if (listen_specified && connect_specified)
{
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("CONNECT"),
LEN_AND_LIT("ZLISTEN"), LEN_AND_LIT("OPEN"));
return FALSE;
}
if (delay_specified && nodelay_specified)
{
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("DELAY"),
LEN_AND_LIT("NODELAY"), LEN_AND_LIT("OPEN"));
return FALSE;
}
if (listen_specified || connect_specified || is_principal)
{
if (NULL == (socketptr = iosocket_create(sockaddr, bfsize, is_principal ? file_des : -1)))
return FALSE;
assert(listen_specified == socketptr->passive);
if (ioerror_specified)
socketptr->ioerror = ('T' == ioerror || 't' == ioerror);
socketptr->nodelay = nodelay_specified; /* defaults to DELAY */
if (ibfsize_specified)
socketptr->bufsiz = ibfsize;
if (moreread_specified)
{
socketptr->moreread_timeout = moreread_timeout;
socketptr->def_moreread_timeout = TRUE; /* iosocket_readfl.c needs to know user specified */
}
/* socket handle -- also check for duplication */
if (attach_specified)
{
if (iosocket_handle(sock_handle, &handle_len, FALSE, newdsocket) >= 0)
{
if (FD_INVALID != socketptr->temp_sd)
tcp_routines.aa_close(socketptr->temp_sd);
SOCKET_FREE(socketptr);
assert(ioptr->newly_created == FALSE);
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKETEXIST, 2, handle_len, sock_handle);
return FALSE;
}
} else
iosocket_handle(sock_handle, &handle_len, TRUE, dsocketptr);
socketptr->handle_len = handle_len;
memcpy(socketptr->handle, sock_handle, handle_len);
/* parse the delimiter: delimiter_buffer ==> socketptr->delimiter[...] */
if (0 <= delimiter_len)
iosocket_delimiter(delimiter_buffer, delimiter_len, socketptr, (0 == delimiter_len));
if (ioptr->wrap && 0 != socketptr->n_delimiter && ioptr->width < socketptr->delimiter[0].len)
{
socketptr_delim_len = socketptr->delimiter[0].len;
SOCKET_FREE(socketptr);
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DELIMWIDTH, 2, ioptr->width, socketptr_delim_len);
assert(FALSE);
}
/* connects newdsocket and socketptr (the new socket) */
if (gtm_max_sockets <= newdsocket->n_socket)
{
assert(ioptr->newly_created == FALSE);
if (FD_INVALID != socketptr->temp_sd)
tcp_routines.aa_close(socketptr->temp_sd);
SOCKET_FREE(socketptr);
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets);
return FALSE;
}
socketptr->dev = newdsocket;
newdsocket->socket[newdsocket->n_socket++] = socketptr;
newdsocket->current_socket = newdsocket->n_socket - 1;
}
if (0 <= zff_len && /* ZFF or ZNOFF specified */
0 < (socketptr->zff.len = zff_len)) /* assign the new ZFF len, might be 0 from ZNOFF, or ZFF="" */
{ /* ZFF="non-zero-len-string" specified */
if (gtm_utf8_mode) /* Check if ZFF has any invalid UTF-8 character */
{ /* Note: the ZFF string originates from the source program, so is in UTF-8 mode or M mode regardless
* of OCHSET of this device. ZFF is output on WRITE # command, and MUST contain valid UTF-8 sequence. */
utf8_len_strict(zff_buffer, zff_len); /* triggers badchar error for invalid sequence */
}
if (NULL == socketptr->zff.addr) /* we rely on socketptr->zff.addr being set to 0 in iosocket_create() */
socketptr->zff.addr = (char *)malloc(MAX_ZFF_LEN);
memcpy(socketptr->zff.addr, zff_buffer, zff_len);
}
}
/* action */
if ((listen_specified && (!iosocket_bind(socketptr, timepar, ibfsize_specified))) ||
(connect_specified && (!iosocket_connect(socketptr, timepar, ibfsize_specified))))
{
if (socketptr->sd > 0)
(void)tcp_routines.aa_close(socketptr->sd);
SOCKET_FREE(socketptr);
return FALSE;
} else if (is_principal)
{ /* fill in what bind or connect would */
ai_ptr = &(socketptr->local.ai);
remote_ai_ptr = &(socketptr->remote.ai);
/* translate internal address to numeric ip address */
GETNAMEINFO(SOCKET_LOCAL_ADDR(socketptr), ai_ptr->ai_addrlen, ipaddr, SIZEOF(ipaddr), NULL, 0, NI_NUMERICHOST,
errcode);
if (0 != errcode)
{
RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode);
return FALSE;
}
STRNDUP(ipaddr, SIZEOF(ipaddr), socketptr->local.saddr_ip);
GETNAMEINFO(SOCKET_REMOTE_ADDR(socketptr), remote_ai_ptr->ai_addrlen, ipaddr, SIZEOF(ipaddr), NULL, 0,
NI_NUMERICHOST, errcode);
if (0 != errcode)
{
RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode);
return FALSE;
}
STRNDUP(ipaddr, SIZEOF(ipaddr), socketptr->remote.saddr_ip);
len = SIZEOF(ESTABLISHED) - 1;
memcpy(&ioptr->dollar.key[0], ESTABLISHED, len);
ioptr->dollar.key[len++] = '|';
memcpy(&ioptr->dollar.key[len], socketptr->handle, socketptr->handle_len);
len += socketptr->handle_len;
ioptr->dollar.key[len++] = '|';
strncpy(&ioptr->dollar.key[len], socketptr->remote.saddr_ip, DD_BUFLEN - 1 - len);
ioptr->dollar.key[DD_BUFLEN-1] = '\0'; /* In case we fill the buffer */
}
/* commit the changes to the list */
if (listen_specified || connect_specified || is_principal)
{
socketptr->dev = dsocketptr;
memcpy(dsocketptr, newdsocket, d_socket_struct_len);
}
ioptr->newly_created = FALSE;
ioptr->state = dev_open;
return TRUE;
}