# The COMSTACK Module

## Synopsis (blocking mode)

    COMSTACK stack;
    char *buf = 0;
    int size = 0, length_incoming;
    char *protocol_package;
    int protocol_package_length;
    char server_address_str[] = "myserver.com:2100";
    void *server_address_ip;
    int status;

    stack = cs_create(tcpip_type, 1, PROTO_Z3950);
    if (!stack) {
        perror("cs_create");  /* use perror() here since we have no stack yet */
        exit(1);
    }

    server_address_ip = cs_addrstr (stack, server_address_str);

    status = cs_connect(stack, server_address_ip);
    if (status != 0) {
        cs_perror(stack, "cs_connect");
        exit(1);
    }

    status = cs_put(stack, protocol_package, protocol_package_length);
    if (status) {
        cs_perror(stack, "cs_put");
        exit(1);
    }

    /* Now get a response */

    length_incoming = cs_get(stack, &buf, &size);
    if (!length_incoming) {
        fprintf(stderr, "Connection closed\n");
        exit(1);
    } else if (length_incoming < 0) {
        cs_perror(stack, "cs_get");
        exit(1);
    }

    /* Do stuff with buf here */

    /* clean up */
    cs_close(stack);
    if (buf)
        free(buf);

## 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.

         int 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

        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> &lsqb; ':' <portnum> &rsqb;

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

        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`.

## 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.

When a function (including the data exchange functions) reports an error condition, use the function `cs_errno()` to determine the cause of the problem. The function

        void cs_perror(COMSTACK handle char *message);

works like `perror(2)` and prints the `message` argument, along with a system message, to `stderr`. Use the character array

        extern const char *cs_errlist&lsqb;&rsqb;;

to get hold of the message, if you want to process it differently. The function

        const char *cs_stackerr(COMSTACK handle);

Returns an error message from the lower layer, if one has been provided.

## Summary and Synopsis

        #include <yaz/comstack.h>

        #include <yaz/tcpip.h>  /* this is for TCP/IP and SSL support */
        #include <yaz/unix.h>   /* this is for UNIX sockeL 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);

        int cs_close(COMSTACK handle);

        int cs_look(COMSTACK handle);

        void *cs_straddr(COMSTACK handle, const char *str);

        char *cs_addrstr(COMSTACK h);

        extern int cs_errno;

        void cs_perror(COMSTACK handle char *message);

        const char *cs_stackerr(COMSTACK handle);

        extern const char *cs_errlist[];