fis-gtm/sr_port/iosocket_use.c

574 lines
21 KiB
C

/****************************************************************
* *
* Copyright 2001, 2012 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. *
* *
****************************************************************/
/* iosocket_use.c */
#include "mdef.h"
#include "gtm_socket.h"
#include "gtm_unistd.h"
#include "gtm_iconv.h"
#include "gtm_stdio.h"
#include "gtm_string.h"
#include <errno.h>
#include "gtm_inet.h"
#include <netinet/tcp.h>
#include "copy.h"
#include "io.h"
#include "io_params.h"
#include "iotimer.h"
#include "iotcpdef.h"
#include "iotcproutine.h"
#include "gt_timer.h"
#include "iosocketdef.h"
#include "nametabtyp.h"
#include "namelook.h"
#include "stringpool.h"
#include "gtm_conv.h"
GBLREF io_pair io_curr_device;
GBLREF io_pair io_std_device;
GBLREF io_desc *active_device;
GBLREF tcp_library_struct tcp_routines;
GBLREF d_socket_struct *socket_pool;
GBLREF boolean_t gtm_utf8_mode;
GBLREF spdesc stringpool;
GBLREF UConverter *chset_desc[];
GBLREF int4 gtm_max_sockets;
GBLREF d_socket_struct *newdsocket;
GBLREF boolean_t dollar_zininterrupt;
LITREF nametabent filter_names[];
LITREF unsigned char filter_index[27];
LITREF unsigned char io_params_size[];
error_def(ERR_ABNCOMPTINC);
error_def(ERR_ACOMPTBINC);
error_def(ERR_ADDRTOOLONG);
error_def(ERR_ANCOMPTINC);
error_def(ERR_CURRSOCKOFR);
error_def(ERR_DELIMSIZNA);
error_def(ERR_DELIMWIDTH);
error_def(ERR_DEVPARMNEG);
error_def(ERR_ILLESOCKBFSIZE);
error_def(ERR_MRTMAXEXCEEDED);
error_def(ERR_SETSOCKOPTERR);
error_def(ERR_SOCKBFNOTEMPTY);
error_def(ERR_SOCKNOTFND);
error_def(ERR_SOCKMAX);
error_def(ERR_TEXT);
error_def(ERR_TTINVFILTER);
error_def(ERR_ZFF2MANY);
error_def(ERR_ZINTRECURSEIO);
void iosocket_use(io_desc *iod, mval *pp)
{
unsigned char ch, len;
int handled_len, handlea_len, handles_len;
int4 length, width, new_len;
d_socket_struct *dsocketptr;
socket_struct *socketptr, newsocket;
char handlea[MAX_HANDLE_LEN], handles[MAX_HANDLE_LEN], handled[MAX_HANDLE_LEN];
char addr[SA_MAXLITLEN], *errptr, sockaddr[SA_MAXLITLEN],
temp_addr[SA_MAXLITLEN], ioerror;
unsigned char delimiter_buffer[MAX_N_DELIMITER * (MAX_DELIM_LEN + 1)];
unsigned char zff_buffer[MAX_ZFF_LEN];
boolean_t attach_specified = FALSE,
detach_specified = FALSE,
connect_specified = FALSE,
ioerror_specified = FALSE,
listen_specified = FALSE,
socket_specified = FALSE,
delay_specified = FALSE,
nodelay_specified = FALSE,
bfsize_specified = FALSE,
ibfsize_specified = FALSE,
moreread_specified = FALSE,
create_new_socket;
int4 index, n_specified, zff_len, delimiter_len, moreread_timeout;
int fil_type, nodelay, p_offset = 0;
uint4 bfsize = DEFAULT_SOCKET_BUFFER_SIZE, ibfsize;
char *tab;
int save_errno;
size_t d_socket_struct_len;
mstr lcl_zff;
assert(iod->state == dev_open);
assert(iod->type == gtmsocket);
dsocketptr = (d_socket_struct *)(iod->dev_sp);
/* ---------------------------------- parse the command line ------------------------------------ */
n_specified = 0;
zff_len = -1; /* indicates neither ZFF nor ZNOFF specified */
delimiter_len = -1; /* indicates neither DELIM nor NODELIM specified */
/* A read or wait was interrupted for this device. Allow only parmless use in $zinterrupt code for
and interrupted device.
*/
if (iop_eol != *(pp->str.addr + p_offset))
{ /* Parameters were specified */
if (dsocketptr->mupintr)
{ /* And if we are in $zinterrupt code this is not allowed */
if (dollar_zininterrupt)
rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO);
/* We are not in $zinterrupt code and this device was not resumed properly
so clear its restartability.
*/
io_find_mvstent(iod, TRUE);
dsocketptr->mupintr = FALSE;
}
} else if (dsocketptr->mupintr && !dollar_zininterrupt)
{ /* The interrupted read was not properly resumed so clear it now */
dsocketptr->mupintr = FALSE;
dsocketptr->sock_save_state.who_saved = sockwhich_invalid;
io_find_mvstent(iod, TRUE);
}
while (iop_eol != (ch = *(pp->str.addr + p_offset++)))
{
assert((params)ch < (params)n_iops);
switch (ch)
{
case iop_exception:
iod->error_handler.len = *(pp->str.addr + p_offset);
iod->error_handler.addr = (char *)(pp->str.addr + p_offset + 1);
s2pool(&iod->error_handler);
break;
case iop_filter:
len = *(pp->str.addr + p_offset);
tab = pp->str.addr + p_offset + 1;
if ((fil_type = namelook(filter_index, filter_names, tab, len)) < 0)
{
rts_error(VARLSTCNT(1) ERR_TTINVFILTER);
return;
}
switch (fil_type)
{
case 0:
iod->write_filter |= CHAR_FILTER;
break;
case 1:
iod->write_filter |= ESC1;
break;
case 2:
iod->write_filter &= ~CHAR_FILTER;
break;
case 3:
iod->write_filter &= ~ESC1;
break;
}
break;
case iop_nofilter:
iod->write_filter = 0;
break;
case iop_attach:
n_specified++;
attach_specified = TRUE;
handlea_len = (int)(*(pp->str.addr + p_offset));
memcpy(handlea, (char *)(pp->str.addr + p_offset + 1), handlea_len);
break;
case iop_detach:
n_specified++;
detach_specified = TRUE;
handled_len = (int)(*(pp->str.addr + p_offset));
memcpy(handled, (char *)(pp->str.addr + p_offset + 1), handled_len);
break;
case iop_connect:
n_specified++;
connect_specified = TRUE;
len = *(pp->str.addr + p_offset);
if (len < SA_MAXLITLEN)
{
memcpy(sockaddr, (char *)(pp->str.addr + p_offset + 1), len);
sockaddr[len] = '\0';
} else
rts_error(VARLSTCNT(6) ERR_ADDRTOOLONG,
4, len, pp->str.addr + p_offset + 1, len, SA_MAXLITLEN);
break;
case iop_delimiter:
n_specified++;
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(VARLSTCNT(1) ERR_DELIMSIZNA);
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:
bfsize_specified = TRUE;
GET_ULONG(bfsize, pp->str.addr + p_offset);
if ((0 == bfsize) || (MAX_SOCKET_BUFFER_SIZE < bfsize))
rts_error(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(VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, ibfsize);
break;
case iop_ioerror:
n_specified++;
ioerror_specified = TRUE;
ioerror = *(char *)(pp->str.addr + p_offset + 1);
break;
case iop_zlisten:
n_specified++;
listen_specified = TRUE;
len = *(pp->str.addr + p_offset);
if (len < SA_MAXLITLEN)
{
memcpy(sockaddr, (char *)(pp->str.addr + p_offset + 1), len);
sockaddr[len] = '\0';
} else
rts_error(VARLSTCNT(6) ERR_ADDRTOOLONG,
4, len, pp->str.addr + p_offset + 1, len, SA_MAXLITLEN);
break;
case iop_socket:
n_specified++;
socket_specified = TRUE;
handles_len = (int)(*(pp->str.addr + p_offset));
memcpy(handles, (char *)(pp->str.addr + p_offset + 1), handles_len);
break;
case iop_ipchset:
#if defined(KEEP_zOS_EBCDIC) || defined(VMS)
if ((iconv_t)0 != iod->input_conv_cd)
ICONV_CLOSE_CD(iod->input_conv_cd);
SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1));
if (DEFAULT_CODE_SET != iod->in_code_set)
ICONV_OPEN_CD(iod->input_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1));
#endif
break;
case iop_opchset:
#if defined(KEEP_zOS_EBCDIC) || defined(VMS)
if ((iconv_t)0 != iod->output_conv_cd)
ICONV_CLOSE_CD(iod->output_conv_cd);
SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1));
if (DEFAULT_CODE_SET != iod->out_code_set)
ICONV_OPEN_CD(iod->output_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET);
#endif
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(VARLSTCNT(4) ERR_ZFF2MANY, 2, zff_len, MAX_ZFF_LEN);
break;
case iop_znoff:
zff_len = 0;
break;
case iop_length:
GET_LONG(length, pp->str.addr + p_offset);
if (length < 0)
rts_error(VARLSTCNT(1) ERR_DEVPARMNEG);
iod->length = length;
break;
case iop_width:
/* SOCKET WIDTH is handled the same way as TERMINAL WIDTH */
GET_LONG(width, pp->str.addr + p_offset);
if (width < 0)
rts_error(VARLSTCNT(1) ERR_DEVPARMNEG);
if (0 == width)
{
iod->width = TCPDEF_WIDTH;
iod->wrap = FALSE;
} else
{
iod->width = width;
iod->wrap = TRUE;
}
break;
case iop_wrap:
iod->wrap = TRUE;
break;
case iop_nowrap:
iod->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(VARLSTCNT(1) ERR_DEVPARMNEG);
else if (MAX_MOREREAD_TIMEOUT < moreread_timeout)
rts_error(VARLSTCNT(3) ERR_MRTMAXEXCEEDED, 1, MAX_MOREREAD_TIMEOUT);
moreread_specified = TRUE;
break;
default:
/* ignore deviceparm */
break;
}
p_offset += ((io_params_size[ch] == IOP_VAR_SIZE) ?
(unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]);
}
/* ------ return immediately if no flag, worth a check because it is mostly true ------------ */
if (1 == p_offset)
return;
/* ------------------------------ compatibility verification -------------------------------- */
if ((socket_specified) && ((n_specified > 2) || ((2 == n_specified) && (0 >= delimiter_len))))
{
rts_error(VARLSTCNT(8) ERR_ACOMPTBINC, 6, LEN_AND_LIT("SOCKET"), LEN_AND_LIT("DELIMITER"), LEN_AND_LIT("USE"));
return;
}
if (connect_specified && listen_specified)
{
rts_error(VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("CONNECT"), LEN_AND_LIT("ZLISTEN"), LEN_AND_LIT("USE"));
return;
}
if (delay_specified && nodelay_specified)
{
rts_error(VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("DELAY"), LEN_AND_LIT("NODELAY"), LEN_AND_LIT("OPEN"));
return;
}
/* ------------------ make a local copy of device structure to play with -------------------- */
d_socket_struct_len = SIZEOF(d_socket_struct) + (SIZEOF(socket_struct) * (gtm_max_sockets - 1));
memcpy(newdsocket, dsocketptr, d_socket_struct_len);
/* --------------- handle the two special cases attach/detach first ------------------------- */
if (detach_specified)
{
if (1 < n_specified)
{
rts_error(VARLSTCNT(6) ERR_ANCOMPTINC, 4, LEN_AND_LIT("DETACH"), LEN_AND_LIT("USE"));
return;
}
if (NULL == socket_pool)
iosocket_poolinit();
iosocket_switch(handled, handled_len, newdsocket, socket_pool);
memcpy(dsocketptr, newdsocket, d_socket_struct_len);
if (0 > dsocketptr->current_socket)
{
io_curr_device.in = io_std_device.in;
io_curr_device.out = io_std_device.out;
}
return; /* detach can only be specified by itself */
}
if (attach_specified)
{ /* NOTE: A socket could be moved from one device to another using DETACH/ATTACH. A socket does not carry I[O]CHSET with
* it while being moved. Such a socket will use the I[O]CHSET of the device it is ATTACHed to. If there is input still
* buffered, this may cause unintentional consequences in the application if I[O]CHSET changes. GT.M does not detect
* (or report) a change in I[O]CHSET due to DETACH/ATTACH.
*/
if (1 < n_specified)
{
rts_error(VARLSTCNT(6) ERR_ANCOMPTINC, 4, LEN_AND_LIT("ATTACH"), LEN_AND_LIT("USE"));
return;
}
if (NULL == socket_pool)
{
rts_error(VARLSTCNT(4) ERR_SOCKNOTFND, 2, handlea_len, handlea);
return;
}
iosocket_switch(handlea, handlea_len, socket_pool, newdsocket);
memcpy(dsocketptr, newdsocket, d_socket_struct_len);
return; /* attach can only be specified by itself */
}
/* ------------ create/identify the socket to work on and make a local copy ----------------- */
if (create_new_socket = (listen_specified || connect_specified)) /* real "=" */
{
/* allocate the structure for a new socket */
if (NULL == (socketptr = iosocket_create(sockaddr, bfsize, -1)))
return;
/* give the new socket a handle */
iosocket_handle(handles, &handles_len, TRUE, dsocketptr);
socketptr->handle_len = handles_len;
memcpy(socketptr->handle, handles, handles_len);
socketptr->dev = newdsocket; /* use newdsocket temporarily for the sake of bind/connect */
} else
{
if (socket_specified)
{
/* use the socket flag to identify which socket to apply changes */
if (0 > (index = iosocket_handle(handles, &handles_len, FALSE, newdsocket)))
{
rts_error(VARLSTCNT(4) ERR_SOCKNOTFND, 2, handles_len, handles);
return;
}
newdsocket->current_socket = index;
socketptr = newdsocket->socket[index];
} else
{
socketptr = newdsocket->socket[newdsocket->current_socket];
if (newdsocket->n_socket <= newdsocket->current_socket)
{
assert(FALSE);
rts_error(VARLSTCNT(4) ERR_CURRSOCKOFR, 2, newdsocket->current_socket, newdsocket->n_socket);
return;
}
}
}
newsocket = *socketptr;
/* ---------------------- apply changes to the local copy of the socket --------------------- */
if (0 <= delimiter_len)
{
iosocket_delimiter(delimiter_buffer, delimiter_len, &newsocket, (0 == delimiter_len));
/* The delimiter has changed. The iosocket_readfl/write routine won't notice so we have to do
the UTF16xx conversion since we changed it.
*/
DBGSOCK2((stdout, "socuse: Delimiter(s) replaced - num delims: %d delimiter_len: %d ichset: %d ochset: %d\n",
newsocket.n_delimiter, delimiter_len, iod->ichset, iod->ochset));
if (0 < delimiter_len)
{
if (!newsocket.first_read && (CHSET_UTF16BE == iod->ichset || CHSET_UTF16LE == iod->ichset))
{ /* We have been reading with this socket so convert this new delimiter set */
DBGSOCK2((stdout, "socuse: Converting new delimiters for input\n"));
iosocket_delim_conv(&newsocket, iod->ichset);
}
if (!newsocket.first_write && (CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset))
{ /* We have been writing with this socket so convert the new default output delimiter */
DBGSOCK2((stdout, "socuse: Converting new delimiters for output\n"));
if (newsocket.first_read || (CHSET_UTF16BE != iod->ichset && CHSET_UTF16LE != iod->ichset))
{ /* Need to do conversion as iosocket_delim_conv above didn't do it for us */
DBGSOCK2((stdout, "socuse: running convert for write since input didn't do it\n"));
new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset],
&newsocket.delimiter[0], NULL, NULL);
if (MAX_DELIM_LEN < new_len)
{
rts_error(VARLSTCNT(1) ERR_DELIMSIZNA);
return;
}
} else
{
DBGSOCK2((stdout, "socuse: using previous length from read conversion\n"));
new_len = newsocket.idelimiter[0].len;
}
newsocket.odelimiter0.len = new_len;
UNICODE_ONLY(newsocket.odelimiter0.char_len = newsocket.delimiter[0].char_len);
newsocket.odelimiter0.addr = malloc(new_len);
memcpy(newsocket.odelimiter0.addr,
(newsocket.first_read ? (char *)stringpool.free : newsocket.idelimiter[0].addr),
new_len);
}
}
}
if (iod->wrap && 0 != newsocket.n_delimiter && iod->width < newsocket.delimiter[0].len)
rts_error(VARLSTCNT(4) ERR_DELIMWIDTH, 2, iod->width, newsocket.delimiter[0].len);
if (0 <= zff_len && /* ZFF or ZNOFF specified */
0 < (newsocket.zff.len = zff_len)) /* assign the new ZFF len, might be 0 from ZNOFF, or ZFF="" */
{ /* ZFF="non-zero-len-string" specified */
if (CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset) /* need conversion of ZFF */
{
DBGSOCK2((stdout, "socuse: Converting zff\n"));
lcl_zff.addr = (char *)zff_buffer;
lcl_zff.len = zff_len;
new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], &lcl_zff, NULL, NULL);
if (MAX_ZFF_LEN < new_len)
rts_error(VARLSTCNT(4) ERR_ZFF2MANY, 2, new_len, MAX_ZFF_LEN);
if (NULL == newsocket.zff.addr) /* we rely on newsocket.zff.addr being set to 0 in iosocket_create() */
newsocket.zff.addr = (char *)malloc(MAX_ZFF_LEN);
newsocket.zff.len = new_len;
UNICODE_ONLY(newsocket.zff.char_len = 0); /* don't care */
memcpy(newsocket.zff.addr, stringpool.free, new_len);
} else
{ /* Store parm without conversion */
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. This validation is handled by gtm_conv in the path above.
*/
utf8_len_strict(zff_buffer, zff_len);
}
if (NULL == newsocket.zff.addr) /* we rely on newsocket.zff.addr being set to 0 in iosocket_create() */
newsocket.zff.addr = (char *)malloc(MAX_ZFF_LEN);
memcpy(newsocket.zff.addr, zff_buffer, zff_len);
}
}
if (ioerror_specified)
newsocket.ioerror = ('T' == ioerror || 't' == ioerror);
if (nodelay_specified || delay_specified)
newsocket.nodelay = nodelay_specified; /* defaults to DELAY */
if (ibfsize_specified)
newsocket.bufsiz = ibfsize;
if (moreread_specified)
{
newsocket.moreread_timeout = moreread_timeout;
newsocket.def_moreread_timeout = TRUE; /* need to know this was user-defined in iosocket_readfl.c */
}
if (!create_new_socket)
{
/* these changes apply to only pre-existing sockets */
if (bfsize_specified)
newsocket.buffer_size = bfsize;
#ifdef TCP_NODELAY
nodelay = newsocket.nodelay ? 1 : 0;
if ((socketptr->nodelay != newsocket.nodelay) &&
(-1 == tcp_routines.aa_setsockopt(newsocket.sd, IPPROTO_TCP,
TCP_NODELAY, &nodelay, SIZEOF(nodelay))))
{
save_errno = errno;
errptr = (char *)STRERROR(save_errno);
rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("TCP_NODELAY"), save_errno, LEN_AND_STR(errptr));
return;
}
#endif
if ((socketptr->bufsiz != newsocket.bufsiz) &&
(-1 == tcp_routines.aa_setsockopt(newsocket.sd, SOL_SOCKET,
SO_RCVBUF, &newsocket.bufsiz, SIZEOF(newsocket.bufsiz))))
{
save_errno = errno;
errptr = (char *)STRERROR(save_errno);
rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("SO_RCVBUF"), save_errno, LEN_AND_STR(errptr));
return;
}
if (socketptr->buffer_size != newsocket.buffer_size)
{
if (socketptr->buffered_length > bfsize)
rts_error(VARLSTCNT(4) ERR_SOCKBFNOTEMPTY, 2, bfsize, socketptr->buffered_length);
newsocket.buffer = (char *)malloc(bfsize);
if (0 < socketptr->buffered_length)
{
memcpy(newsocket.buffer, socketptr->buffer + socketptr->buffered_offset,
socketptr->buffered_length);
newsocket.buffered_offset = 0;
}
}
}
/* -------------------------------------- action -------------------------------------------- */
if ((listen_specified && (!iosocket_bind(&newsocket, NO_M_TIMEOUT, ibfsize_specified))) ||
(connect_specified && (!iosocket_connect(&newsocket, 0, ibfsize_specified))))
{ /* error message should be printed from bind/connect */
if (socketptr->sd > 0)
(void)tcp_routines.aa_close(socketptr->sd);
iosocket_delimiter((unsigned char *)NULL, 0, &newsocket, TRUE);
if (NULL != socketptr->zff.addr)
free(socketptr->zff.addr);
if (NULL != socketptr->buffer)
free(socketptr->buffer);
free(socketptr);
return;
}
/* ------------------------------------ commit changes -------------------------------------- */
if (create_new_socket)
{
if (gtm_max_sockets <= newdsocket->n_socket)
{
rts_error(VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets);
return;
}
/* a new socket is created. so add to the list */
newsocket.dev = dsocketptr;
newdsocket->socket[newdsocket->n_socket++] = socketptr;
newdsocket->current_socket = newdsocket->n_socket - 1;
}
else if (socketptr->buffer_size != newsocket.buffer_size)
free(socketptr->buffer);
*socketptr = newsocket;
memcpy(dsocketptr, newdsocket, d_socket_struct_len);
return;
}