/* Communications Interface code for Remote Procedure Call ts.c ** ======================================================= ** ** This code was originally translated from the pascal using the 'ptc' ** program. It is now maintained separately. ** ** See also the RPC internals manual, which describes how modules ** similar to this one work, also giving flow charts and message formats. ** ** History: ** 30 Apr 90 Transport medium objects separated from this module. ** See CMS copy of previous version for all previous history. ** ** Compilation options: ** ** ETHERNET Include code for communication using raw ethernet ** packets. ** ** TCP Include code for handling TCP/IP using the Berkley ** socket interface. ** ** TS Always set. This selects visibility of TS layer ** interface, and is necessary for TS_INTERNALS: ** ** TS_INTERNALS Always set. This selects visibility of TS layer ** structure socket is its size must be known for malloc!. ** ** AST The operating systems supports asynchonous procedures ** This is not essential, but provides useful added ** functionality. */ /* Module parameters: ** ----------------- ** ** These may be undefined and redefined by syspec.h */ #define LISTEN_BACKLOG 2 /* Number of pending connect requests (TCP)*/ #define INET_VMS_ERROR 0x08558002 /* Add to (errno<<3) to make VMS error */ #define WILDCARD '*' /* Wildcard used in addressing */ #define NETCLOSE close /* Routine to close a TCP-IP socket */ #define NETREAD read /* Routine to read from a TCP-IP socket */ #define NETWRITE write /* Routine to write to a TCP-IP socket */ # define FIRST_TCP_PORT 5000 /* First port number to try to bind to */ # define LAST_TCP_PORT 5999 /* Last one to try before giving up */ #include "syspec.h" /* Standard Include files: ** */ #include #define TS /* Get visibility of TS interface */ #define TS_INTERNALS /* Get visibility of TS internals for socket size! */ #include "rpcrts.h" /* Complete TS layer visibility - includes rpc_ts.h */ #include "rpc_code.h" /* Coding convention macros */ /* Definitions for case-statements */ # define Line __LINE__ PRIVATE void Caseerror(); /* ** Definitions for pointers */ #define NIL_MESSAGE (rpc_message *)0 extern char *malloc(); extern void free(); extern char *strncpy(); /* Genuine module-global variables: */ rpc_protocol * rpc_protocols; /* List of protocols */ /* socket_type valid_sockets; List of allocated sockets */ /* External Routines ** ----------------- */ extern void rpc_new(); /* Allocate rpc_message */ extern void rpc_dispose(); /* Deallocate rpc_message */ extern void rpc_get_string(); /* Get Pascal or C string parameter */ #ifdef AST /* !!!!!! */ extern void sys_setast(); /* En/Disable ASTs */ rpc_status ts_when_receive(); /* forward (ASTs only) */ #endif /* UTILITY ROUTINES ** */ /* Dump contents of message to screen ** ---------------------------------- */ void hex_message(p_message, len) rpc_message_pointer p_message; int len; { register int j; register int i; char ch; if (len > RPC_BUFFER_SIZE) UTRACE(tfp, " Illegal length of %1d\n", len); else if (len == 0) UTRACE(tfp, "(no data)\n"); else { UTRACE(tfp, " length%5d, contents:\n", len); switch ((p_message->body.which)) { case CALL_MESSAGE: UTRACE(tfp, " CALL: TID Package_No. Vers. Proc# Parameters...\n"); break ; case REJECT_MESSAGE: UTRACE(tfp, "REJECT: TID Why? Status_Code Details...\n"); break ; case RETURN_MESSAGE: UTRACE(tfp, "RETURN: TID Returned_Parameters...\n"); break ; case ABORT_MESSAGE: UTRACE(tfp, " ABORT: TID Code ...\n"); break ; default: UTRACE(tfp, " Type (*** Unidentified message type ***)\n"); } UTRACE(tfp, " "); for (i=0; i<=(len-1) ; i++) { if (i==128) { /* Truncate long messages */ UTRACE(tfp, "... etc.\n"); break; } UTRACE(tfp, " %02x", p_message->body.rpc_b[i]); if ((i % 16 == 15) || (i == len - 1)) { /* end of line? */ for (j = i % 16; j<=14; j++) UTRACE(tfp, " "); UTRACE(tfp, " "); for (j = (16*(i/16));j <= i; j++) { ch = p_message->body.rpc_b[j]; if ((ch < ' ') || (ch > '~')) ch = '.'; UTRACE(tfp, "%c", ch); } UTRACE(tfp, "\n"); if (i < len - 1) UTRACE(tfp, " "); } /*end if end of line*/ } /*end for*/ } } /* Check valid socket descriptor check_socket() ** ----------------------------- ** ** This implementation is trivial at the moment. ** ** On entry, ** ** socket is a socket pointer passed to the TS. ** ** On exit, ** ** returns status indicating, if BAD, that the socket is invalid ** and should not be used by the TS. */ rpc_status check_socket(socket) socket_type socket; { if (socket == NULL) return RPC_S_NOSUCHSOCKET; else return RPC_S_NORMAL; } /* Parse a cardinal value parse_cardinal() ** ---------------------- ** ** On entry, ** *pp points to first character to be interpreted, terminated by ** non 0:9 character. ** *pstatus points to status already valid ** maxvalue gives the largest allowable value. ** ** On exit, ** *pp points to first unread character ** *pstatus points to status updated iff bad */ unsigned int parse_cardinal(pstatus, pp, max_value) rpc_status *pstatus; char **pp; unsigned int max_value; { rpc_long n; if ( (**pp<'0') || (**pp>'9')) { /* Null string is error */ *pstatus = RPC_S_SYNTAX_4; /* No number where one expeceted */ return 0; } n=0; while ((**pp>='0') && (**pp<='9')) n = n*10 + *((*pp)++) - '0'; if (n>max_value) { *pstatus = RPC_S_SYNTAX_4; /* Cardinal outside range */ return 0; } return n; } /***************************************************************************** ** ** Procedure: Send a message over the given socket ** ** On entry, ** socket gives the socket over which to communicate ** pmessage points to a pointer to the message, with fields: ** m_next (undefined) ** m_socket valid ** m_index Length of message = bufsize ** m_status (undefined) ** bufsize gives the number of data bytes to be transferred, ** excluding any bytes needed for the protocol. ** On exit, ** The pointer to the message may have been changed. In this case, ** it will point to a message with the m_socket and m_status fields ** the same, but without the data. ts_send is therefore DESTRUCTIVE. */ #ifdef __STDC__ rpc_status ts_send( socket_type socket, /* IN Socket */ rpc_message_pointer *ppmessage, /* IN Ptr to ptr to message */ int bufsize) /* IN Number of bytes to send */ #else rpc_status ts_send(socket, ppmessage, bufsize) socket_type socket; rpc_message_pointer *ppmessage; int bufsize; #endif { rpc_status status; status = check_socket( socket ); if (GOOD(status)) { register struct socket_struct *soc = socket; return (*soc->soc_protocol->write)(ppmessage); } else return status; } /*************************************************************************** * * Procedure: OPEN TRANSPORT CONNECTION * ------------------------- * * On entry, * The service parameter gives details of the notional connection to be * set up, as a string, in a format defined in the RPC user manual. * This defines the medium to be used (after the '.'), and the network * address or subaddress etc within that medium. * On exit, * The function value is the status (error or normal) of the operation. * If successful (function value odd), the socket number returned may * be used for future calls on the new notional connection. * * Non-reentrant, because of the use of global variables */ /*______________________________________________________________________ Match given name against template string Exact match required, case insensitive */ static char upper_case(ch) char ch; { if ((ch>='a')&&(ch<='z')) return (ch-'a'+'A'); return ch; } /* Match name against string name_match() ** ------------------------- ** ** On entry, ** ** given - a 40 character character array ** start - the first position in 'given' to compare ** template- a zero-terminated string to be compared against. ** ** On exit, ** retuns TRUE iff the names match exactly except for upper/lower case. ** */ int name_match(given, template) char * given; char * template; { register int i; for (i=0; template[i] && given[i]; i++) if (upper_case(given[i]) != upper_case(template[i])) return FALSE; return (BOOLEAN) (!template[i] && !given[i]); /* mismatch or diff. lengths */ } /* Register Transport Protocol rpc_use() ** --------------------------- ** ** If the same name is registered twice, the last registered will be ** found first. This allows users to override a default offering. ** ** On entry, ** protocol points to a protocol structure which may or may not ** already be registered. ** ** On exit, ** The protocol is on the list rpc_protocols. */ #ifdef __STDC__ rpc_status rpc_use(rpc_protocol * protocol) #else rpc_status rpc_use(protocol) rpc_protocol * protocol; #endif { rpc_protocol * scan; /* For safety, we first check that the protocol is not registered. */ for (scan = rpc_protocols; scan; scan = scan->next); if (scan==protocol) return RPC_S_NORMAL; /* Then, we add it to the head of the queue of protocols. */ CRITICAL protocol->next = rpc_protocols; rpc_protocols = protocol; NONCRITICAL return RPC_S_NORMAL; } /* Unregister a protocol rpc_unuse() ** --------------------- ** ** This is provided for completeness though it may never be used. ** It unregisters a protocol if it had been registered. ** ** On entry, ** protocol points to a protocol object ** ** On exit, ** protocol is not on the list of rpc_protocols. */ #ifdef __STDC__ rpc_status rpc_unuse(rpc_protocol * protocol) #else rpc_status rpc_unuse(protocol) rpc_protocol * protocol; #endif { rpc_protocol * scan; rpc_protocol * last; /* Trails scan by one link */ CRITICAL for (last=(rpc_protocol *)(&rpc_protocols), scan=last->next; scan; last=scan, scan=last->next) if (scan==protocol) { last->next = scan->next; /* Remove from queue */ NONCRITICAL return RPC_S_NORMAL; } NONCRITICAL return RPC_S_NORMAL; /* Wasn't on it. */ } /* Open Transport Connection ts_open() ** _________________________ ** ** On entry, ** service is a 40-character blank filled or zero-terminated string ** which gives the complete address, including medium name. ** ** socket points to a location to receive the connection identifier. ** ** On exit, ** ** returns a status code. ** ** *socket If status is good, then *socket is a valid connection ** identifier to be used in future calls to this module. ** It should eventually be returned using ts_close(). ** ** If status is bad, then *socket is undefined, and may not ** be used for future calls. */ #ifdef __STDC__ rpc_status ts_open( socket_type *psocket, /* socket returned */ rpc_name service) /* communications address */ #else rpc_status ts_open(psocket, service) socket_type *psocket; rpc_name service; #endif { rpc_status status; /* To be returned */ int length; /* Name length */ int dotpos; /* Position of last "." in name */ rpc_name my_name; /* Zero-terminated service name */ rpc_name ts_open_tsap; /* Address without medium on it */ register socket_type soc; rpc_get_string(my_name, service); /* Pick up string parameter */ CTRACE(tfp, "RPC/TS: Opening `%s'\n", my_name); /* Allocate a new socket: */ soc = (socket_type)malloc(sizeof(socket_descriptor)); if (!(soc)) return RPC_S_OPERATING_SYSTEM; /* No memory! */ *psocket= soc; /* Clear common fields in socket: */ soc->soc_last_call_sent = NULL; soc->soc_last_reply_sent = NULL; soc->soc_next_call_tid = 0; /* Find position of the dot in the service name, copy service ** and medium names into separate strings: */ length = strlen(my_name); for(dotpos = length; (dotpos > 0) && (my_name[dotpos] != '.'); dotpos--) /*void*/; if (my_name[dotpos] != '.') return RPC_S_SYNTAX_2; /* No dot! */ strncpy(ts_open_tsap, my_name, dotpos); /* TSAP */ ts_open_tsap[dotpos]=0; /* Terminate it */ CTRACE(tfp, "RPC/TS: TSAP = `%s'\n", ts_open_tsap); /* Find which medium name follows the last dot: */ { rpc_protocol * scan; for(scan=rpc_protocols; scan; scan=scan->next) if (name_match(&my_name[dotpos+1], scan->name)) break; if (!scan) status=RPC_S_SYNTAX_3; else { soc->soc_protocol = scan; status = (*scan->open)(soc, ts_open_tsap); /* Open the channel */ } } if (BAD(status)) free(soc); /* Socket is not valid if BAD status */ return status; } /* ts_open */ /* Return the address by which we are known to others ** -------------------------------------------------- ** ** Note that this routine is callable by FORTRAN and Pascal, giving back ** a 40-character blank-filled name. ** ** Bug: returns buffer-overflow status if the name would fir exactly, because ** it allows space also for its own working terminator. */ /*ARGSUSED*/ #ifdef __STDC__ rpc_status ts_my_address( socket_type socket, /* IN Socket */ rpc_name addrstr) /* OUT address of this socket */ #else rpc_status ts_my_address(socket, addrstr) socket_type socket; rpc_name addrstr; #endif { rpc_status status; status = (*socket->soc_protocol->my_address) (socket, addrstr, RPC_NAME_LENGTH); if (BAD(status)) return status; if ((strlen(addrstr)+strlen(socket->soc_protocol->name)+2) > RPC_NAME_LENGTH) return RPC_S_BUFFER_OVERFLOW; strcat(addrstr, "."); strcat(addrstr, socket->soc_protocol->name); /* Blank fill: */ { int i; for(i=strlen(addrstr); isoc_protocol->peer_address) (socket, addrstr, RPC_NAME_LENGTH); if (BAD(status)) return status; if ((strlen(addrstr)+strlen(socket->soc_protocol->name)+2) > RPC_NAME_LENGTH) return RPC_S_BUFFER_OVERFLOW; strcat(addrstr, "."); strcat(addrstr, socket->soc_protocol->name); /* Blank fill: */ { int i; for(i=strlen(addrstr); isoc_protocol->close)(soc); /* Throw away any spare messages: */ if (soc->soc_last_call_sent != NULL) rpc_dispose(soc->soc_last_call_sent); while (soc->soc_last_reply_sent != NULL) { m = soc->soc_last_reply_sent; soc->soc_last_reply_sent = m->m_next; rpc_dispose(m); } free(soc); /* Return socket to heap */ return status; } /* Initialise Transport Service Module ts_init() ** ----------------------------------- ** ** This is a one-off initialisation procedure for the whole TS module. ** ** On entry, ** No prerequisites. ** ** On exit, ** returns a status indicating any unrecoverable errrors. */ #ifdef __STDC__ rpc_status ts_init(void) #else rpc_status ts_init() #endif { #ifdef ETHERNET (void) use_ethernet(); #endif #ifdef V24 (void) use_v24(); #endif #ifdef TCP (void) use_tcp(); #endif #ifdef CATS (void) use_cats(); #endif #ifdef DECNET (void) use_decnet(); #endif CTRACE(tfp, "RPC/TS: Initialised.\n"); return RPC_S_NORMAL; } /* Generic Case Bound error routine Caseerror() ** -------------------------------- ** ** On entry, ** A fatal program error has been found, in that a value used in a ** switch statement was not any of the expected values. ** ** Does not return. */ PRIVATE void Caseerror(n) int n; { PRINT_ERROR(stderr, "RPC/TS/Internal error: Missing case limb: line %d\n", n); exit(RPC_S_TS_INTERNAL_ERROR); } #ifdef AST /****************************************************************************** * * Queue Handling Routines * * (some of these are not currently used) */ #ifdef NOTDEFINED void queue_add_head(queue, item) socket_type *queue; socket_type item; { sys_setast(0); item->soc_next = (*queue); (*queue) = item; sys_setast(1); } #endif /*__________________________________________________ */ void queue_add_tail(queue, item) socket_type *queue; socket_type item; { socket_type scan; sys_setast(0); item->soc_next = NULL; if ((*queue) == NULL) (*queue) = item; else { scan = (*queue); while (scan->soc_next != NULL) scan = scan->soc_next; scan->soc_next = item; } sys_setast(1); } #ifdef NOTDEFINED void queue_remove(queue, item) socket_type *queue; socket_type item; { socket_type scan; sys_setast(0); if (((*queue) == item)) (*queue) = item->soc_next; else { scan = (*queue); while (scan->soc_next != NULL) { if ((scan->soc_next == item)) scan->soc_next = item->soc_next; scan = scan->soc_next; } } sys_setast(1); } #endif #endif