The COMSTACK Module
Synopsis (blocking mode)
Introduction
The &comstack;
subsystem provides a transparent interface to different types of transport
stacks for the exchange of BER-encoded data and HTTP packets.
At present, the RFC1729 method (BER over TCP/IP), local UNIX socket and an
experimental SSL stack are supported, but others may be added in time.
The philosophy of the
module is to provide a simple interface by hiding unused options and
facilities of the underlying libraries. This is always done at the risk
of losing generality, and it may prove that the interface will need
extension later on.
There hasn't been interest in the XTImOSI stack for some years.
Therefore, it is no longer supported.
The interface is implemented in such a fashion that only the
sub-layers constructed to the transport methods that you wish to
use in your application are linked in.
You will note that even though simplicity was a goal in the design,
the interface is still orders of magnitudes more complex than the
transport systems found in many other packages. One reason is that
the interface needs to support the somewhat different requirements of
the different lower-layer communications stacks; another important
reason is that the interface seeks to provide a more or less
industrial-strength approach to asynchronous event-handling.
When no function is allowed to block, things get more complex -
particularly on the server side.
We urge you to have a look at the demonstration client and server
provided with the package. They are meant to be easily readable and
instructive, while still being at least moderately useful.
Common Functions
Managing Endpoints
COMSTACK cs_create(CS_TYPE type, int blocking, int protocol);
Creates an instance of the protocol stack - a communications endpoint.
The type parameter determines the mode
of communication. At present the following values are supported:
tcpip_type
TCP/IP (BER over TCP/IP or HTTP over TCP/IP)
ssl_type
Secure Socket Layer (SSL). This COMSTACK
is experimental and is not fully implemented. If
HTTP is used, this effectively is HTTPS.
unix_type
Unix socket (unix only). Local Transfer via
file socket. See unix
7.
The cs_create function returns a null-pointer
if a system error occurs.
The blocking parameter should be one if
you wish the association to operate in blocking mode, zero otherwise.
The protocol field should be
PROTO_Z3950 or PROTO_HTTP.
Protocol PROTO_SR is no longer supported.
void cs_close(COMSTACK handle);
Closes the connection (as elegantly as the lower layers will permit),
and releases the resources pointed to by the
handle
parameter. The
handle
should not be referenced again after this call.
We really need a soft disconnect, don't we?
Data Exchange
int cs_put(COMSTACK handle, char *buf, int len);
Sends
buf
down the wire. In blocking mode, this function will return only when a
full buffer has been written, or an error has occurred. In nonblocking
mode, it's possible that the function will be unable to send the full
buffer at once, which will be indicated by a return value of 1. The
function will keep track of the number of octets already written; you
should call it repeatedly with the same values of buf
and len, until the buffer has been transmitted.
When a full buffer has been sent, the function will return 0 for
success. -1 indicates an error condition (see below).
int cs_get(COMSTACK handle, char **buf, int *size);
Receives a PDU or HTTP Response from the peer. Returns the number of
bytes read.
In nonblocking mode, it is possible that not all of the packet can be
read at once. In this case, the function returns 1. To simplify the
interface, the function is
responsible for managing the size of the buffer. It will be reallocated
if necessary to contain large packages, and will sometimes be moved
around internally by the subsystem when partial packages are read. Before
calling
cs_get
for the fist time, the buffer can be initialized to the null pointer,
and the length should also be set to 0 - cs_get will perform a
malloc(2)
on the buffer for you. When a full buffer has been read, the size of
the package is returned (which will always be greater than 1). -1
indicates an error condition.
See also the cs_more() function below.
int cs_more(COMSTACK handle);
The cs_more() function should be used in conjunction
with cs_get and
select(2).
The cs_get() function will sometimes
(notably in the TCP/IP mode) read more than a single protocol package
off the network. When this happens, the extra package is stored
by the subsystem. After calling cs_get(), and before
waiting for more input, You should always call
cs_more()
to check if there's a full protocol package already read. If
cs_more()
returns 1,
cs_get()
can be used to immediately fetch the new package. For the
mOSI
subsystem, the function should always return 0, but if you want your
stuff to be protocol independent, you should use it.
The cs_more()
function is required because the RFC1729-method
does not provide a way of separating individual PDUs, short of
partially decoding the BER. Some other implementations will carefully
nibble at the packet by calling
read(2)
several times. This was felt to be too inefficient (or at least
clumsy) - hence the call for this extra function.
int cs_look(COMSTACK handle);
This function is useful when you're operating in nonblocking
mode. Call it when
select(2)
tells you there's something happening on the line. It returns one of
the following values:
CS_NONE
No event is pending. The data found on the line was not a
complete package.
CS_CONNECT
A response to your connect request has been received. Call
cs_rcvconnect
to process the event and to finalize the connection establishment.
CS_DISCON
The other side has closed the connection (or maybe sent a disconnect
request - but do we care? Maybe later). Call
cs_close to close your end of the association
as well.
CS_LISTEN
A connect request has been received.
Call cs_listen to process the event.
CS_DATA
There's data to be found on the line.
Call cs_get to get it.
You should be aware that even if
cs_look()
tells you that there's an event event pending, the corresponding
function may still return and tell you there was nothing to be found.
This means that only part of a package was available for reading. The
same event will show up again, when more data has arrived.
int cs_fileno(COMSTACK h);
Returns the file descriptor of the association. Use this when
file-level operations on the endpoint are required
(select(2) operations, specifically).
Client Side
int cs_connect(COMSTACK handle, void *address);
Initiate a connection with the target at address
(more on addresses below). The function will return 0 on success, and 1 if
the operation does not complete immediately (this will only
happen on a nonblocking endpoint). In this case, use
cs_rcvconnect to complete the operation,
when select(2) or poll(2)
reports input pending on the association.
int cs_rcvconnect(COMSTACK handle);
Complete a connect operation initiated by cs_connect().
It will return 0 on success; 1 if the operation has not yet completed (in
this case, call the function again later); -1 if an error has occurred.
Server Side
To establish a server under the inetd
server, you can use
COMSTACK cs_createbysocket(int socket, CS_TYPE type, int blocking,
int protocol);
The socket parameter is an established socket (when
your application is invoked from inetd, the
socket will typically be 0.
The following parameters are identical to the ones for
cs_create.
int cs_bind(COMSTACK handle, void *address, int mode)
Binds a local address to the endpoint. Read about addresses below. The
mode parameter should be either
CS_CLIENT or CS_SERVER.
int cs_listen(COMSTACK handle, char *addr, int *addrlen);
Call this to process incoming events on an endpoint that has been
bound in listening mode. It will return 0 to indicate that the connect
request has been received, 1 to signal a partial reception, and -1 to
indicate an error condition.
COMSTACK cs_accept(COMSTACK handle);
This finalizes the server-side association establishment, after
cs_listen has completed successfully. It returns a new connection
endpoint, which represents the new association. The application will
typically wish to fork off a process to handle the association at this
point, and continue listen for new connections on the old
handle.
You can use the call
const char *cs_addrstr(COMSTACK);
on an established connection to retrieve the host-name of the remote host.
You may need to use this function with some care if your
name server service is slow or unreliable
Addresses
The low-level format of the addresses are different depending on the
mode of communication you have chosen. A function is provided by each
of the lower layers to map a user-friendly string-form address to the
binary form required by the lower layers.
void *cs_straddr(COMSTACK handle, const char *str);
The format for TCP/IP and SSL addresses is:
<host> [ ':' <portnum> ]
The hostname can be either a domain name or an
IP address. The port number, if omitted, defaults to 210.
For TCP/IP and SSL transport modes, the special hostname "@"
is mapped to any local address
(the manifest constant INADDR_ANY).
It is used to establish local listening endpoints in the server role.
For UNIX sockets, the format of an address is the socket filename.
When a connection has been established, you can use
const char *cs_addrstr(COMSTACK h);
to retrieve the host name of the peer system. The function returns
a pointer to a static area, which is overwritten on the next call
to the function.
A fairly recent addition to the &comstack; module is the utility
function
COMSTACK cs_create_host (const char *str, int blocking, void **vp);
which is just a wrapper for cs_create and
cs_straddr. The str
is similar to that described for cs_straddr
but with a prefix denoting the &comstack; type. Prefixes supported
are tcp:, unix: and
ssl: for TCP/IP, UNIX and SSL respectively.
If no prefix is given, then TCP/IP is used.
The blocking is passed to
function cs_create. The third parameter
vp is a pointer to &comstack; stack type
specific values.
For SSL (ssl_type) vp is an already create
OpenSSL CTX. For TCP/IP and UNIX vp
is unused (can be set to NULL.
SSL
void *cs_get_ssl(COMSTACK cs);
Returns the SSL handle, SSL * for comstack. If comstack
is not of type SSL, NULL is returned.
int cs_set_ssl_ctx(COMSTACK cs, void *ctx);
Sets SSL context for comstack. The parameter is expected to be of type
SSL_CTX *. This function should be called just
after comstack has been created (before connect, bind, etc).
This function returns 1 for success; 0 for failure.
int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname);
Sets SSL certificate for comstack as a PEM file. This function
returns 1 for success; 0 for failure.
int cs_get_ssl_peer_certificate_x509(COMSTACK cs, char **buf, int *len);
This function returns the peer certificate. If successful,
*buf and *len holds
X509 buffer and length respectively. Buffer should be freed
with xfree. This function returns 1 for success;
0 for failure.
Diagnostics
All functions return -1 if an error occurs. Typically, the functions
will return 0 on success, but the data exchange functions
(cs_get, cs_put,
cs_more) follow special rules. Consult their
descriptions.
The error code for the COMSTACK can be retrieved using C macro
cs_errno which will return one
of the error codes CSYSERR,
CSOUTSTATE,
CSNODATA, ...
int cs_errno(COMSTACK handle);
You can the textual representation of the error code
by using cs_errmsg - which
works like strerror(3)
const char *cs_errmsg(int n);
It is also possible to get straight to the textual represenataion
without the error code by using
cs_strerror.
const char *cs_strerror(COMSTACK h);
Summary and Synopsis
#include /* this is for TCP/IP and SSL support */
#include /* this is for UNIX socket support */
COMSTACK cs_create(CS_TYPE type, int blocking, int protocol);
COMSTACK cs_createbysocket(int s, CS_TYPE type, int blocking,
int protocol);
COMSTACK cs_create_host(const char *str, int blocking,
void **vp);
int cs_bind(COMSTACK handle, int mode);
int cs_connect(COMSTACK handle, void *address);
int cs_rcvconnect(COMSTACK handle);
int cs_listen(COMSTACK handle);
COMSTACK cs_accept(COMSTACK handle);
int cs_put(COMSTACK handle, char *buf, int len);
int cs_get(COMSTACK handle, char **buf, int *size);
int cs_more(COMSTACK handle);
void cs_close(COMSTACK handle);
int cs_look(COMSTACK handle);
void *cs_straddr(COMSTACK handle, const char *str);
const char *cs_addrstr(COMSTACK h);
]]>