Solstice X.25 9.2 Developer's Guide

Chapter 4 Listening for Calls

This chapter contains examples of how to listen for single or multiple incoming calls and then accept or reject the call.


Note -

There are copies of the code samples referred to in this chapter in the /opt/SUNWconn/x25/samples.nli directory.


4.1 Listening for a Single Call

The steps for listening for a single incoming call are:

  1. Send an N_Xlisten message to the X.25 driver.

    This should carry the called address list in which the application is interested.

  2. Wait for the response to the Listen Request.

    The l_result flag will indicate success or failure. If the l_result flag indicates failure, the application can decide either to close the stream or to try again later.

  3. Wait for Connect Indication messages from the X.25 driver.

  4. Decide whether to accept on this or a different stream.

  5. Negotiate facilities and QOS, if required.

    A Connect Confirmation message carrying the appropriate connection identifier is then passed down on the stream on which the connection is being accepted.

  6. Construct the listen message.

    The listen message has two parts. Construct the control part of the message like this:

    struct xlistenf lisreq;
     lisreq.xl_type = XL_CTL;
     lisreq.xl_command = N_XListen;
     lisreq.lmax = 1;

    In this example, lmax has the value of 1, indicating that only one Connect Indication is to be handled at a time.

    The data part of the message contains the sequence of bytes that specify the Call User Data string and address(es) which are to be listened for. The simplest case for this would be to set "Don't Care" values for both the CUD and address:

    int lislen;
     char lisbuf[MAXLIS];
     lisbuf[0] = X25_DONTCARE; /* l_cumode*/
     lisbuf[1] = X25_DONTCARE; /* l_mode*/
     lislen = 2;

    Alternatively, to set the CUD to match exactly the (X.29) value defined in the array cudf[] (0x01000000), and the NSAP to match any sequence starting 0x80, 0x00, the following would be used:

    lislen = 0;
     lisbuf[lislen++] = X25_IDENTITY; /* l_cumode */
     lisbuf[lislen++] = CUDFLEN; /* l_culength */
     memcpy(&(lisbuf[lislen]), cudf, CUDFLEN); /* l_cubytes */
     lislen += CUDFLEN;
     lisbuf[lislen++] = X25_STARTSWITH; /* l_mode */
     lisbuf[lislen++] = X25_NSAP; /* l_type */
     lisbuf[lislen++] = 4; /* l_length */
     lisbuf[lislen++] = 0x80;  /* l_add */
     lisbuf[lislen++] = 0x00; 

    Or, to accept any CUD Field, with a DTE of 2342315656565:

    #define MY_DTE_LEN 13
     #define MY_DTE_OCTETS 7
     char my_dte[MY_DTE_OCTETS] = {0x23,0x42,0x31,0x56,0x56,0x56,0x50};
     lislen = 0;
     lisbuf[lislen++] = X25_DONTCARE;  /* l_cumode */
     lisbuf[lislen++] = X25_IDENTITY; /* l_mode */
     lisbuf[lislen++] = X25_DTE; /* l_type */
     lisbuf[lislen++] = MY_DTE_LEN; /* l_length */
     memcpy(&(lisbuf[lislen]), my_dte, MY_DTE_OCTETS); /* l_add */
     lislen += MY_DTE_OCTETS;

    Note -

    The l_add field uses packed hexadecimal digits and the l_length value is actually the number of semi-octets, whereas the l_culength field specifies the length of the l_cubytes field in octets.


  7. Send the Listen Request down the open stream:

    ctlblk.len = sizeof(struct xlistenf);
     ctlblk.buf = (char *) &lisreq;
     datblk.len = lislen;
     datblk.buf = lisbuf;
     if (putmsg(x25_fd, &ctlblk, &datblk, 0) < 0) {
     	perror("Listen putmsg failure");
     	return -1;
     	}
  8. Wait for the listen response; the result flag indicates success or failure:

    #define DBUFSIZ 128
     #define CBUFSIZ MAX( sizeof(struct xccnff), sizeof(struct xdiscf) )
     struct xlistenf *lis_msg;
     ctlblk.maxlen = CBUFSIZ; /* See 4.1 above for declarations */
     ctlblk.buf = ctlbuf;
     datblk.maxlen = DBUFSIZ;
     datblk.buf = datbuf;
     for(;;) {
     	if (getmsg (x25_fd, &ctlblk, &datblk, &getflags) < 0) {
     		perror("Listen getmsg failure");
     		return -1;
     		}
     	lis_msg = (struct xlistenf *) ctlbuf;
     	if ((lis_msg->xl_type == XL_CTL) &&  (lis_msg->xl_command == N_XListen))
     		if (lis_msg->l_result != 0) {
     			printf("Listen command failed \n");
     			return -1;
     			}
     		else {
     			printf("Listen command succeeded \n");
     			return 0;
     			}
     	} 

    Cancelling a Listen Request is done in the same way, except that no data is passed with the request. It cancels all successful Listens that have been made on that stream.

  9. Once the listening application has received a Listen Response indicating success, it should wait for incoming Connect Indications.

    When an N_CI message arrives, the application should inspect its parameters-- address, call user data, facilities, quality of service, and so on--then decide whether to accept or reject the connection.

    A listening application can accept a call either on the stream the indication arrived on, or on some other stream. This other stream can be one which is already open and free, or the application can open it.

    Whatever method is used for the accept, the identifier conn_id in the Connect Indication message must be copied into the accept message for matching by the X.25 driver. If this identifier in the accept message does not match, a Disconnect is sent to the accepting application. This causes the resource to hang on the stream on which the incoming call was sent, since the connection is never accepted.

    A listening application can reject the call by sending a N_DI message down the stream on which the Connect Indication arrived. A Connect Indication cannot be rejected on a different stream. The connection identifier must be quoted in the message for matching, since there may be several Connect Indications passed to the listening application. If there is no match for the rejection, the message is silently discarded.

    The rejecting listener can request one of two actions in response to the disconnect:

    • Request immediate disconnect. The application sets the reason field to NU_PERMANENT (0xF5).

    • Search for further matching listeners. The application set the reason field to any value except 0xF5.

      The following code example shows how to reject an incoming call:

      struct xcallf  *conind;
       struct xdiscf disc_msg; 
       /* Use getmsg to receive the Connect Indication
        * use conind to point to it
        */
       disc_msg.xl_type = XL_CTL;
       disc_msg.xl_command = N_DI;
       disc_msg.conind = conind->conind;
       disc_msg.cause = cause; /* cause to be returned */
       disc_msg.diag = diag; /* diagnostic to be returned */
       if (disc_immed) /* no more searches */
       	disc_msg.reason = NU_PERMANENT; /* 0xF5 */
       /* Send Rejection down stream with putmsg */

      Note -

      The application must not accept a connection on a listening stream that is capable of handling more than one Connect Indication at one time if there could subsequently be other Connect Indications to be handled on that stream. For example, the application issues a Listen Request to handle three Connect Indications at one time. A Connect Indication is received and sent to the application on the listen stream. The application must not accept this connection on the listen stream because there could be two more Connect Indications that can be sent subsequently.


  10. Negotiate any facilities or OSI CONS QOS parameters required.

    To do this, set the negotiate_qos flag in the Connect Response message. The values received should then be copied into the response, and those facilities and/or parameters (and any related flags) for which a different value is desired should then be altered (see "2.7 Facilities and QOS Parameters"). Copy the entire QOS structure from the indication to the response. This allows for future additions to this structure.

    An example of negotiation is shown below. Here all the values are copied as indicated, except the packet size, which is negotiated down to 256 if it is flagged as negotiable, and is greater than 256:

    struct xcallf *conind;
     struct xccnff conresp;
     /* Do a getmsg etc to receive the Connect Indication,
      * assign conind to point to it.
      */
     conresp.xl_type = XL_CTL;
     conresp.xl_command = N_CC;
     conresp.conn_id = conind->conn_id;  /* Connection identifier */
     conresp.CONS_call = TRUE /* This is a CONS call */
     memset(&conresp.responder, 0, sizeof(struct xaddrf));
     /* Let network fill in responding addr */
     conresp.negotiate_qos = TRUE;
     memcpy (&conresp.rqos, &conind->qos, sizeof (struct qosformat) );
     if (conind->qos.xtras.pwoptions & NEGOT_PKT) {
     	if (conind->qos.xtras.rempacket > 8)
     		conresp.rqos.xtras.rempacket = 8; 
     	if (conind->qos.xtras.locpacket > 8)
     		conresp.rqos.xtras.locpacket = 8;
     	}
     /* Set any other values to be negotiated here,
      * then send the response down with a putmsg.
      */

    Alternatively, the application may decide to accept (agree with) the indicated values, in which case the negotiate_qos flag is set to zero.

    If a connection is never established on a listening stream (using a matching accept) then that stream remains listening on the address list supplied. On the other hand, once an established connection has been disconnected, the stream does not return to a listening state. Instead, it remains open in an idle state. If the application needs to listen again, then the listen message must be re-sent. Rejection does not alter the listening state of the stream.

4.2 Listening for Multiple Incoming Calls

Sample code for a listener that can handle several incoming PAD calls simultaneously is provided in the file /opt/SUNWconn/x25/samples.nli/listen.c. Listeners to handle other types of incoming calls are similar. The steps are:

  1. Define the values you need.

    Specify the maximum number of simultaneous calls you want to allow. Set the maximum number of simultaneous calls depending on the processor power available to you and the number of calls you expect to need to handle.

  2. Open the X.25 device.

    The open_stream() function does this. It requests notification from the kernel when there is incoming data on the stream.

  3. Listen for incoming data.

    The do_listen() function specifies the information used to decide what to do with an incoming call. The example shows two ways of doing this, one simple, the other more complex. In the simple example, the program listens for any call user data beginning with the four BCD digits 1234.

  4. Wait for incoming calls.

    To do this, call the getmsg() function. If an incoming call arrives that matches the criteria that you specified in step 1, the X25 driver will send an N_CI indication. At this point, you could choose to do some more sophisticated checking. The example program includes a function called try_next() that tells the X.25 driver to see if the connect message is destined for another application, and a function called reject_call() that tells the X.25 driver to reject the call.

  5. Accept the incoming call.

    Assuming the call is valid, the accept_call() function is used to accept it. Note that when accepting incoming data, the application must copy the call indication identifier into the connect confirm sent to the kernel.

  6. Handle the incoming call as appropriate.

    The sample code contains an example of a call that is handled by printing a message and closing the device (which closes the connection).

  7. Exit the program when finished.